- 参考:
- 头文件
- auto_ptr
-
- unique_ptr
-
- shared_ptr
- 模板声明
- 通过辅助类模拟实现 shared_ptr
- shared_ptr使用示例
- weaked_ptr
-
- 如何选择智能指针
C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:
- auto_ptr(C++11已舍弃)
- unique_ptr
- shared_ptr
- weak_ptr
其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr。 虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,
shared_ptr 和 weak_ptr 则是 C+11 从准标准库 Boost 中引入的两种智能指针
参考:
C++ STL 四种智能指针
C++ 智能指针详解
头文件
#include<memory>
auto_ptr
C++11中,auto_ptr 已被摒弃
std::auto_ptr 可用来管理单个对象的堆内存,但是,请注意如下几点:
(1) 尽量不要使用“operator=”。如果使用了,请不要再使用先前对象。
(2) 记住 release() 函数不会释放对象,仅仅归还所有权。
(3) std::auto_ptr 最好不要当成参数传递。
(4) 由于 std::auto_ptr 的“operator=”问题,有其管理的对象不能放入 std::vector 等容器中。
(5) 不能用来管理堆内存数组
使用示例
#include <iostream>
#include <memory>
int main () {
std::auto_ptr<int> p1 (new int);
*p1.get()=10;
std::auto_ptr<int> p2 (p1);
std::cout << "p2 points to " << *p2 << '\n';
return 0;
}
unique_ptr
类模板声明
non-specialized
template <class T, class D = default_delete<T>> class unique_ptr;
array specialization
template <class T, class D> class unique_ptr<T[],D>;
- 为了避免因潜在的内存问题导致程序崩溃,摒弃 auto_ptr,提出了unique_ptr。
unique_ptr 比 auto_ptr 更加安全,因为 auto_ptr 有拷贝语义,拷贝后原对象变得无效,再次访问原对象时会导致程序崩溃;
unique_ptr 则禁止了拷贝语义,但提供了移动语义,即可以使用std::move() 进行控制权限的转移
unique_ptr<string> upt(new string("lvlv"));
unique_ptr<string> upt1(upt);
unique_ptr<string> upt1=upt;
unique_ptr<string> upt1=std::move(upt);
auto_ptr<string> apt(new string("lvlv"));
auto_ptr<string> apt1(apt);
auto_ptr<string> apt1=apt;
这里要注意,在使用std::move将unique_ptr的控制权限转移后,不能够再通过unique_ptr来访问和控制资源了,否则同样会出现程序崩溃。我们可以在使用unique_ptr访问资源前,使用成员函数get()进行判空操作。
unique_ptr<string> upt1=std::move(upt);
if(upt.get()!=nullptr)
{
}
- unique_ptr 不仅安全,而且灵活。
如果 unique_ptr 是个临时右值,编译器允许拷贝语义。参考如下代码:
unique_ptr<string> demo(const char* s)
{
unique_ptr<string> temp (new string(s));
return temp;
}
unique_ptr<string> ps;
ps = demo('Uniquely special");
demo() 返回一个临时 unique_ptr,然后 ps 接管了临时对象 unique_ptr 所管理的资源,而返回时临时的 unique_ptr 被销毁,也就是说没有机会使用 unique_ptr 来访问无效的数据,换句话来说,这种赋值是不会出现任何问题的,即没有理由禁止这种赋值。实际上,编译器确实允许这种赋值。相对于 auto_ptr 任何情况下都允许拷贝语义,这正是 unique_ptr 更加灵活聪明的地方。
下图演示了两个 unique_ptr 实例之间的所有权转换。
unique_ptr 指针与其所指对象的关系:在智能指针生命周期内,可以改变智能指针所指对象,如创建智能指针时通过构造函数指定、通过 reset 方法重新指定、通过 release 方法释放所有权、通过移动语义转移所有权,unique_ptr 还可能没有对象,这种情况被称为 empty。
条款21:尽量使用std::make_unique和std::make_shared而不直接使用new
智能指针之make_unique与make_shared
让我们从对齐std::make_unique 和 std::make_shared这两块开始。std::make_shared是c++11的一部分,但很可惜std::make_unique不是。它是在c++14里加入标准库的。假如你在使用c++11,也别担心,你很容易写出一个基本的版本。看这里:
template<typename T, typename… Ts>
std::unique_ptr make_unique(Ts&&… params)
{
return std::unique_ptr(new T(std::forward(params)…));
}
正如你看到的,make_unique完美传递了参数给对象的构造函数,从一个原始指针构造出一个std::unique_ptr,返回创建的std::unique_ptr。
这个形式的函数不支持数组和定制删除器(见条款18),但它证明了一点点的努力就可以根据需要创建一个make_unique。要记住的是不要把你的版本放到std命名空间里,因为你不想当升级到c++14后会和库提供的标准实现冲突吧。
std::make_unique 和 std::make_shared是三个make函数中的两个,make函数用来把一个任意参数的集合完美转移给一个构造函数从而生成动态分配内存的对象,并返回一个指向那个对象的灵巧指针。第三个make是std::allocate_shared。它像std::make_shared一样,除了第一个参数是一个分配器对象,用来进行动态内存分配。
make_unique使用示例
auto upw1(std::make_unique< Widget >()); // with make func
std::unique_ptr< Widget > upw2(new Widget); // without make func
make_shared的使用示例
auto spw1(std::make_shared< Widget >()); // with make func
std::shared_ptr< Widget > spw2(new Widget); // without make func
unique_ptr的基本操作有:
unique_ptr<int> u_i;
u_i.reset(new int(3));
unique_ptr<int> u_i2(new int(4));
unique_ptr<T,D> u(d);
int *p_i = u_i2.release();
unique_ptr<string> u_s(new string("abc"));
unique_ptr<string> u_s2 = std::move(u_s);
u_s2.reset(u_s.release());
u_s2=nullptr;
- 扩展 auto_ptr 不能完成的功能。
(a)unique_ptr 可放在容器中,弥补了 auto_ptr 不能作为容器元素的缺点。
vector<unique_ptr<string>> vs { new string{“Doug”}, new string{“Adams”} };
vector<unique_ptr<string>>v;
unique_ptr<string> p1(new string("abc"));
(b)管理动态数组,因为 unique_ptr 有 unique_ptr<X[]> 重载版本,销毁动态对象时调用 delete[]。
unique_ptr<int[]> p (new int[3]{1,2,3});
p[0] = 0;
(c)自定义资源删除操作(Deleter)。unique_ptr 默认的资源删除操作是 delete/delete[],若需要,可以进行自定义:
void end_connection(connection *p) { disconnect(*p); }
unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);
示例
#include <iostream>
#include <memory>
struct C { int a; int b; };
int main () {
std::unique_ptr<C> foo (new C);
std::unique_ptr<C> bar;
foo->a = 10;
foo->b = 20;
bar = std::move(foo);
if (foo) std::cout << "foo: " << foo->a << ' ' << foo->b << '\n';
if (bar) std::cout << "bar: " << bar->a << ' ' << bar->b << '\n';
return 0;
}
shared_ptr
shared_ptr 是一个标准的共享所有权的智能指针,允许多个指针指向同一个对象。shared_ptr 利用引用计数的方式实现了对所管理的对象的所有权的分享,即允许多个 shared_ptr 共同管理同一个对象。
注意:c++17前,std::shared_ptr它并不支持动态数组
shared_ptr和动态数组
std::tr1::shared_ptr源码赏析
模板声明
template <class T> class shared_ptr;
shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的),在使用引用计数的机制上提供了可以共享所有权的智能指针,当然这需要额外的开销:
(1)shared_ptr 对象除了包括一个所拥有对象的指针外,还必须包括一个引用计数代理对象的指针;
(2)时间上的开销主要在初始化和拷贝操作上, * 和 -> 操作符重载的开销跟 auto_ptr 是一样;
(3)开销并不是我们不使用 shared_ptr 的理由,,永远不要进行不成熟的优化,直到性能分析器告诉你这一点。
通过辅助类模拟实现 shared_ptr
(1)基础对象类
首先,我们来定义一个基础对象类 Point 类,为了方便后面我们验证智能指针是否有效,我们为 Point 类创建如下接口:
class Point
{
private:
int x, y;
public:
Point(int xVal = 0, int yVal = 0) :x(xVal), y(yVal) {}
int getX() const { return x; }
int getY() const { return y; }
void setX(int xVal) { x = xVal; }
void setY(int yVal) { y = yVal; }
};
(2)辅助类
在创建智能指针类之前,我们先创建一个辅助类。这个类的所有成员皆为私有类型,因为它不被普通用户所使用。为了只为智能指针使用,还需要把智能指针类声明为辅助类的友元。这个辅助类含有两个数据成员:计数count 与基础对象指针。也即辅助类用以封装使用计数与基础对象指针
class RefPtr
{
private:
friend class SmartPtr;
RefPtr(Point *ptr):p(ptr),count(1){ }
~RefPtr(){delete p;}
int count;
Point *p;
};
(3)为基础对象类实现智能指针类
引用计数是实现智能指针的一种通用方法。智能指针将一个计数器与类指向的对象相关联,引用计数跟踪共有多少个类对象共享同一指针。它的具体做法如下:
(a)当创建智能指针类的新对象时,初始化指针,并将引用计数设置为1;
(b)当能智能指针类对象作为另一个对象的副本时,拷贝构造函数复制副本的指向辅助类对象的指针,并增加辅助类对象对基础类对象的引用计数(加1);
(c)使用赋值操作符对一个智能指针类对象进行赋值时,处理复杂一点:先使左操作数的引用计数减 1(为何减 1:因为指针已经指向别的地方),如果减1后引用计数为 0,则释放指针所指对象内存。然后增加右操作数所指对象的引用计数(为何增加:因为此时做操作数指向对象即右操作数指向对象);
(d)完成析构函数:调用析构函数时,析构函数先使引用计数减 1,如果减至 0 则 delete 对象。
做好前面的准备后,我们可以为基础对象类 Point 书写一个智能指针类了。根据引用计数实现关键点,我们可以写出如下智能指针类:
class SmartPtr
{
public:
SmartPtr() { rp = nullptr; }
SmartPtr(Point *ptr):rp(new RefPtr(ptr)) {}
SmartPtr(const SmartPtr &sp):rp(sp.rp)
{
++rp->count;
cout << "in copy constructor" <<endl;
}
SmartPtr& operator=(const SmartPtr& rhs)
{
++rhs.rp->count;
if (rp != nullptr && --rp->count == 0)
{
delete rp;
}
rp = rhs.rp;
cout << "in assignment operator" << endl;
return *this;
}
Point* operator->()
{
return rp->p;
}
Point& operator*()
{
return *(rp->p);
}
~SmartPtr()
{
if (--rp->count == 0)
delete rp;
else
cout << "还有" << rp->count << "个指针指向基础对象" << endl;
}
private:
RefPtr* rp;
};
(4)智能指针类的使用与测试
至此,我们的智能指针类就完成了,我们可以来看看如何使用。
int main()
{
Point *pa = new Point(10, 20);
{
SmartPtr sptr1(pa);
cout <<"sptr1:"<<sptr1->getX()<<","<<sptr1->getY()<<endl;
{
SmartPtr sptr2(sptr1);
cout<<"sptr2:" <<sptr2->getX()<<","<<sptr2->getY()<<endl;
{
SmartPtr sptr3;
SmartPtr sptr3=sptr1;
cout<<"sptr3:"<<(*sptr3).getX()<<","<<(*sptr3).getY()<<endl;
}
}
}
cout << pa->getX() << endl;
return 0;
}
运行结果:
sptr1:10,20
in copy constructor
sptr2:10,20
in assignment operator
sptr3:10,20
还有2个指针指向基础对象
还有1个指针指向基础对象
-572662307
如期,在离开大括号后,共享基础对象的指针从 3->2->1->0 变换,最后计数为 0 时,pa 对象被 delete,此时使用 getX() 已经获取不到原来的值。
(5)对智能指针的改进
目前这个智能指针只能用于管理 Point 类的基础对象,如果此时定义了个矩阵的基础对象类,那不是还得重新写一个属于矩阵类的智能指针类吗?但是矩阵类的智能指针类设计思想和 Poin t类一样啊,就不能借用吗?答案当然是能,那就是使用模板技术。为了使我们的智能指针适用于更多的基础对象类,我们有必要把智能指针类通过模板来实现。这里贴上上面的智能指针类的模板版本:
template <typename T> class SmartPtr;
template <typename T> class RefPtr
{
private:
friend class SmartPtr<T>;
RefPtr(T *ptr):p(ptr), count(1)
{}
~RefPtr()
{
delete p;
}
int count;
T *p;
};
template <typename T> class SmartPtr
{
public:
SmartPtr(T *ptr) :rp(new RefPtr<T>(ptr))
{}
SmartPtr(const SmartPtr<T> &sp):rp(sp.rp)
{
++rp->count;
}
SmartPtr& operator=(const SmartPtr<T>& rhs)
{
++rhs.rp->count;
if (--rp->count == 0)
delete rp;
rp = rhs.rp;
return *this;
}
T & operator *()
{
return *(rp->p);
}
T* operator ->()
{
return rp->p;
}
~SmartPtr()
{
if (--rp->count == 0)
{
delete rp;
}
else
{
cout << "还有" << rp->count << "个指针指向基础对象" << endl;
}
}
private:
RefPtr<T> *rp;
};
shared_ptr使用示例
C++内存管理之shared_ptr
条款21:尽量使用std::make_unique和std::make_shared而不直接使用new
智能指针之make_unique与make_shared
让我们从对齐std::make_unique 和 std::make_shared这两块开始。std::make_shared是c++11的一部分,但很可惜std::make_unique不是。它是在c++14里加入标准库的。假如你在使用c++11,也别担心,你很容易写出一个基本的版本。看这里:
template<typename T, typename… Ts>
std::unique_ptr make_unique(Ts&&… params)
{
return std::unique_ptr(new T(std::forward(params)…));
}
正如你看到的,make_unique完美传递了参数给对象的构造函数,从一个原始指针构造出一个std::unique_ptr,返回创建的std::unique_ptr。
这个形式的函数不支持数组和定制删除器(见条款18),但它证明了一点点的努力就可以根据需要创建一个make_unique。要记住的是不要把你的版本放到std命名空间里,因为你不想当升级到c++14后会和库提供的标准实现冲突吧。
std::make_unique 和 std::make_shared是三个make函数中的两个,make函数用来把一个任意参数的集合完美转移给一个构造函数从而生成动态分配内存的对象,并返回一个指向那个对象的灵巧指针。第三个make是std::allocate_shared。它像std::make_shared一样,除了第一个参数是一个分配器对象,用来进行动态内存分配。
make_unique使用示例
auto upw1(std::make_unique< Widget >()); // with make func
std::unique_ptr< Widget > upw2(new Widget); // without make func
make_shared的使用示例
auto spw1(std::make_shared< Widget >()); // with make func
std::shared_ptr< Widget > spw2(new Widget); // without make func
函数名 | 功能 |
---|
T* get() | 返回指向被管理对象的裸指针,如果没有被管理的对象,则返回空指针 |
long use_count() | 返回shared_ptr指向的对象被共享的个数 |
bool unique() const | 返回是否是独占所有权( use_count 为 1) |
void reset() | 放弃内部对象的所有权, 会引起原有对象的引用计数的减少 |
void reset(T* ptr) | 先释放当前被管理的对象的所有权,会引起原有对象的引用计数的减少。然后变更所有权为 ptr 指向的对象 |
void swap( shared_ptr& r ) | 交换两个 shared_ptr 对象(即交换所拥有的对象) |
示例一
std::shared_ptr<int> sp0(new int(2));
std::shared_ptr<int> sp1(new int(11));
std::shared_ptr<int> sp2 = sp1;
printf("%d\n", *sp0);
printf("%d\n", *sp1);
printf("%d\n", *sp2);
sp1.swap(sp0);
printf("%d\n", *sp0);
printf("%d\n", *sp1);
printf("%d\n", *sp2);
std::shared_ptr<int> sp3(new int(22));
std::shared_ptr<int> sp4 = sp3;
printf("%d\n", *sp3);
printf("%d\n", *sp4);
sp3.reset();
printf("%d\n", sp3.use_count());
printf("%d\n", sp4.use_count());
printf("%d\n", sp3);
printf("%d\n", sp4);
std::shared_ptr<int> sp5(new int(22));
std::shared_ptr<int> sp6 = sp5;
std::shared_ptr<int> sp7 = sp5;
printf("%d\n", *sp5);
printf("%d\n", *sp6);
printf("%d\n", *sp7);
printf("%d\n", sp5.use_count());
printf("%d\n", sp6.use_count());
printf("%d\n", sp7.use_count());
sp5.reset(new int(33));
printf("%d\n", sp5.use_count());
printf("%d\n", sp6.use_count());
printf("%d\n", sp7.use_count());
printf("%d\n", *sp5);
printf("%d\n", *sp6);
printf("%d\n", *sp7);
示例二
#include <iostream>
#include <memory>
struct C { int a; int b; };
int main () {
std::shared_ptr<C> foo;
std::shared_ptr<C> bar (new C);
foo = bar;
foo->a = 10;
bar->b = 20;
std::cout<<foo.use_count()<<'\n';
if (foo) std::cout << "foo: " << foo->a << ' ' << foo->b << '\n';
if (bar) std::cout << "bar: " << bar->a << ' ' << bar->b << '\n';
return 0;
}
结果
foo: 10 20
bar: 10 20
weaked_ptr
weak_ptr 被设计为与 shared_ptr 共同工作,可以从一个 shared_ptr 或者另一个 weak_ptr 对象构造而来。weak_ptr 是为了配合 shared_ptr 而引入的一种智能指针,它更像是 shared_ptr 的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载 operator* 和 operator->,因此取名为 weak,表明其是功能较弱的智能指针。
它的最大作用在于协助 shared_ptr 工作,可获得资源的观测权,像旁观者那样观测资源的使用情况。观察者意味着 weak_ptr 只对 shared_ptr 进行引用,而不改变其引用计数,当被观察的 shared_ptr 失效后,相应的 weak_ptr 也相应失效。
weak_ptr 用法
- weak_ptr 的成员函数 use_count() : 可以观测资源的引用计数;
- 成员函数 expired() : 功能等价于 use_count()==0,但更快,表示被观测的资源(也就是 shared_ptr 管理的资源)已经不复存在。
- 成员函数lock(): 从被观测的 shared_ptr 获得管理的对象的一个可用的shared_ptr , 从而操作资源。但当 expired()==true 的时候,lock() 函数将返回一个存储空指针的 shared_ptr。
总的来说,weak_ptr 的基本用法总结如下:
weak_ptr<T> w;
weak_ptr<T> w(sp);
w=p;
w.reset();
w.use_count();
w.expired();
w.lock();
下面是一个简单的使用示例:
#include < assert.h>
#include <iostream>
#include <memory>
#include <string>
using namespace std;
int main()
{
shared_ptr<int> sp(new int(10));
assert(sp.use_count() == 1);
weak_ptr<int> wp(sp);
assert(wp.use_count() == 1);
if (!wp.expired())
{
shared_ptr<int> sp2 = wp.lock();
*sp2 = 100;
assert(wp.use_count() == 2);
}
assert(wp.use_count() == 1);
cout << "int:" << *sp << endl;
return 0;
}
程序输出:
int:100
weak_ptr 的作用
现在要说的问题是,weak_ptr 到底有什么作用呢?
从上面那个例子看来,似乎没有任何作用。其实 weak_ptr 可用于打破循环引用。
引用计数是一种便利的内存管理机制,但它有一个很大的缺点,那就是不能管理循环引用的对象。一个简单的例子如下:
#include <iostream>
#include <memory>
class Woman;
class Man
{
private:
std::shared_ptr<Woman> _wife;
public:
void setWife(std::shared_ptr<Woman> woman)
{
_wife = woman;
}
void doSomthing()
{
if(_wife.lock())
{}
}
~Man()
{
std::cout << "kill man\n";
}
};
class Woman
{
private:
std::shared_ptr<Man> _husband;
public:
void setHusband(std::shared_ptr<Man> man)
{
_husband = man;
}
~Woman()
{
std::cout <<"kill woman\n";
}
};
int main(int argc, char** argv)
{
std::shared_ptr<Man> m(new Man());
std::shared_ptr<Woman> w(new Woman());
if(m && w)
{
m->setWife(w);
w->setHusband(m);
}
return 0;
}
在 Man 类内部会引用一个 Woman,Woman 类内部也引用一个 Man。当一个 man 和一个 woman 是夫妻的时候,他们直接就存在了相互引用问题。
man 内部有个用于管理wife生命期的 shared_ptr 变量,也就是说 wife 必定是在 husband 去世之后才能去世。同样的,woman 内部也有一个管理 husband 生命期的 shared_ptr 变量,也就是说 husband 必须在 wife 去世之后才能去世。
这就是循环引用存在的问题:husband 的生命期由 wife 的生命期决定,wife 的生命期由 husband 的生命期决定,最后两人都死不掉,违反了自然规律,导致了内存泄漏。
一般来讲,解除这种循环引用有下面三种可行的方法:
(1)当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
(2)当 parent 的生存期超过 children 的生存期的时候,children 改为使用一个普通指针指向 parent。
(3)使用弱引用的智能指针打破这种循环引用。
虽然这三种方法都可行,但方法 1 和方法 2 都需要程序员手动控制,麻烦且容易出错。
这里主要介绍一下第三种方法,使用弱引用的智能指针std:weak_ptr 来打破循环引用。
weak_ptr 对象引用资源时不会增加引用计数,但是它能够通过 lock() 方法来判断它所管理的资源是否被释放.。做法就是上面的代码注释的地方取消注释,取消 Woman 类或者 Man 类的任意一个即可,也可同时取消注释,全部换成弱引用 weak_ptr。
另外很自然地一个问题是:既然 weak_ptr 不增加资源的引用计数,那么在使用 weak_ptr 对象的时候,资源被突然释放了怎么办呢?不用担心,因为不能直接通过 weak_ptr 来访问资源。
那么如何通过 weak_ptr 来间接访问资源呢?
答案是在需要访问资源的时候 weak_ptr 为你生成一个shared_ptr,shared_ptr 能够保证在 shared_ptr 没有被释放之前,其所管理的资源是不会被释放的。创建 shared_ptr 的方法就是 lock() 成员函数。
注意: shared_ptr 实现了 operator bool() const 方法来判断被管理的资源是否已被释放。
operator bool () 提供一个本类型到bool的隐式转换,不允许使用参数。
关于operator bool () 和bool operator ==()
如何选择智能指针
上文简单地介绍了 C++ STL 的四种智能指针。当然,除了 STL 的智能指针,C++ 准标准库 Boost 的智能指针,比如 boost::scoped_ptr、boost::shared_array、boost::intrusive_ptr 也可以在编程实践中拿来使用,但这里不做进一步的介绍,有兴趣的读者可以参考:C++ 智能指针详解
在了解 STL 的四种智能指针后,大家可能会想另一个问题:在实际应用中,应使用哪种智能指针呢?
下面给出几个使用指南。
-
如果程序要使用多个指向同一个对象的指针,应选择 shared_ptr。这样的情况包括:
(a)将指针作为参数或者函数的返回值进行传递的话,应该使用 shared_ptr;
(b)两个对象都包含指向第三个对象的指针,此时应该使用 shared_ptr 来管理第三个对象;
(c)STL 容器包含指针。很多 STL 算法都支持复制和赋值操作,这些操作可用于 shared_ptr,但不能用于 unique_ptr(编译器发出 warning)和 auto_ptr(行为不确定)。如果你的编译器没有提供 shared_ptr,可使用 Boost 库提供的 shared_ptr。
-
如果程序不需要多个指向同一个对象的指针,则可使用 unique_ptr。如果函数使用 new 分配内存,并返还指向该内存的指针,将其返回类型声明为 unique_ptr 是不错的选择。这样,所有权转让给接受返回值的 unique_ptr,而该智能指针将负责调用 delete。可将 unique_ptr 存储到 STL 容器中,只要对容器元素不使用拷贝操作的算法即可(如 sort())。例如,可在程序中使用类似于下面的代码段。
unique_ptr<int> make_int(int n)
{
return unique_ptr<int>(new int(n));
}
void show(unique_ptr<int>& p1)
{
cout << *p1 << ' ';
}
int main()
{
vector<unique_ptr<int>> vp(size);
for(int i = 0; i < vp.size(); i++)
{
vp[i] = make_int(rand() % 1000);
}
vp.push_back(make_int(rand() % 1000));
for_each(vp.begin(), vp.end(), show);
}
其中 push_back 调用没有问题,因为它返回一个临时 unique_ptr,该 unique_ptr 被赋给 vp 中的一个 unique_ptr。另外,如果按值而不是按引用给 show() 传递对象,for_each() 将非法,因为这将导致使用一个来自 vp 的非临时 unique_ptr 初始化 pi,而这是不允许的。前面说过,编译器将发现错误使用 unique_ptr 的企图。
在 unique_ptr 为右值时,可将其赋给 shared_ptr,这与将一个 unique_ptr 赋给另一个 unique_ptr 需要满足的条件相同,即 unique_ptr 必须是一个临时对象。与前面一样,在下面的代码中,make_int() 的返回类型为 unique_ptr:
unique_ptr<int> pup(make_int(rand() % 1000));
shared_ptr<int> spp(pup);
shared_ptr<int> spr(make_int(rand() % 1000));
模板 shared_ptr 包含一个显式构造函数,可用于将右值 unique_ptr 转换为 shared_ptr。shared_ptr 将接管原来归 unique_ptr 所有的对象。
在满足 unique_ptr 要求的条件时,也可使用 auto_ptr,但 unique_ptr 是更好的选择。如果你的编译器没有unique_ptr,可考虑使用 Boost 库提供的 scoped_ptr,它与 unique_ptr 类似。
-
虽然说在满足 unique_ptr 要求的条件时,使用 auto_ptr 也可以完成对内存资源的管理,但是因为 auto_ ptr 不够安全,不提倡使用,即任何情况下都不应该使用 auto_ptr。
-
为了解决 shared_ptr 的循环引用问题,我们可以祭出 weak_ptr。
-
在局部作用域(例如函数内部或类内部),且不需要将指针作为参数或返回值进行传递的情况下,如果对性能要求严格,使用 scoped_ptr 的开销较 shared_ptr 会小一些。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)