目录
std::unique_ptr独占的智能指针
std::weak_ptr弱引用的智能指针
总结:
使用智能指针时需要引用头文件
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存,在最后一个shared_ptr析构的时候,内存才会被释放。
初始化:
通过构造函数、std::make_shared
辅助函数 和reset方法来初始化shared_ptr。优先使用std::make_shared
来构造智能指针,提高效率。 std::shared_ptr
p(new int(1)); std::make_shared p2(new int(1)); std::shared_ptr ptr; ptr.reset(new int(1)); if(ptr) { cout<<"ptr is not null"; } 当智能指针有值的时候,调用reset会使引用计数减1。
获取原始指针:
通过get()方法返回原始指针。
std::shared_ptr
ptr(new int(1)); int* p = ptr.get();
指定删除器:
当p的引用计数为0时,自动调用删除器deleteIntPtr来释放对象内存。或者使用lambda表达式:
void deleteIntPtr(int* p) { delete p; } std::shared_ptr
p(new int, deleteIntPtr); std::shared_ptr p(new int, [](int* p) { delete p; }); 管理动态数组时,需要指定删除器,因为std::shared_ptr的默认删除器不支持数组对象。
std::shared_ptr
p(new int[10], [](int* p) { delete[] p; }); 将std::default_delete作为删除器。default_delete的内部是通过调用delete来实现的。
std::shared_ptr
p(new int[10], std::default_delete ()); 通过封装一个make_shared_array方法来让shared_ptr支持数组。
template
shared_ptr make_shared_array(size_t size) { return shared_ptr (new T[size], default_delete ()); } std::shared_ptr p = make_shared_array (10); std::shared_ptr p = make_shared_array (10);
注意:
1、 不要用一个原始指针初始化多个shared_ptr
int *ptr = new int;
shared_ptr p(ptr);
shared_ptr p2(ptr); //逻辑错误
2、不要在函数实参中创建shared_ptr
function (shared_ptr
(new int), g()); //有缺陷 c++函数参数的计算顺序在不同的编译器不同的调用约定下可能不一样。一般是从右到左,也有可能是从左到右,所以,可能的过程是先new int,然后再调用g(),如果恰好g()发生异常,而shared_ptr还没创建,则int内存泄漏。正确写法:
shared_ptr
p(new int()); f(p, g());
3、通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this指针本质是一个裸指针,这样可能会导致重复析构。
struct A { shared_ptr getSelf() { return shared_ptr (this); // } }; int main() { shared_ptr sp1(new A); shared_ptr sp2 = sp1->getSelf(); return 0; }
由于用同一个指针(this)构造了两个智能指针sp1和sp2,而它们之间没有任何关系。在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。
正确做法:
让目标类通过派生std::enable_shared_from_this
类,然后使用基类的成员函数shared_from_this来返回this的shared_ptr。 class A:public std::enable_shared_from_this { std::shared_ptr getSelf() { return shared_from_this(); } }; std::shared_ptr sp(new A); std::shared_ptr p = sp->getSelf();
4、避免循环引用。智能指针最大的缺陷是循环引用,循环引用会导致内存泄漏。
struct A; struct B; struct A { std::shared_ptr bptr; ~A() { cout<<"A del" }; }; struct B { std::shared_ptr aptr; ~B() { cout<<"B del" }; }; void testPtr() { { std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; }//对象应该销毁 }
两个指针A和B都不会被删除,存在内存泄漏,循环引用导致ap和bp的引用计数为2, 在离开作用域之后,ap和bp的引用计数减为1,并不会减为0,导致两个指针都不会析构,产生内存泄漏。解决办法是把A和B任何一个成员变量改为weak_ptr;
不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。
unique_ptr
ptr(new T); unique_ptr ptr1 = ptr; //错误,不能复制
unique_ptr不允许复制,但可以通过函数返回的方式返回其他的unique_ptr,还可以通过std::move来转移到其他的unique_ptr,这样它本身就不在拥有原来指针的所有权。
unique_ptr
ptr(new T); unique_ptr ptr1 = std::move(ptr);
unique_ptr不像shared_ptr可以通过make_shared方法来创建智能指针,c++11目前还没有提供
make_unique方法,在c++14中会提供make_shared类似的make_unique来创建unique_ptr。
实现一个make_unique方法:
//支持普通指针 template
inline typename enable_if::value, unique_ptr >::type make_unique(Args&&... args) { return unique_ptr (new T(std::forward (args)...)); } //支持动态数组 template inline typename enable_if ::value && extent ::value==0, unique_ptr >::type make_unique(size_t size) { typedef typename remove_extent ::type U; return unique_ptr (new U[size]()); } //过滤掉定长数组的情况 template typename enable_if ::value != 0, void>::type make_unique(Args&&... ) = delete; /* 思路:如果不是数组,则直接创建unique_ptr。如果是数组,先判断是否为定长数组, 若为定长数组则编译不通过(因为不能这样调用make_unique (10),而应该这样 make_unique (10));若为非定长数组,则获取数组中的元素类型,再根据入参size 创建动态数组的unique_ptr。 */
unique_ptr可以指向一个数组,shared_ptr不可以。
std::unique_ptr
ptr(new int[10]); ptr[9] = 10; //最后一个元素值为10 std::shared_ptr ptr(new int[10]);//不合法
删除器:
unique_ptr指定删除器和shared_ptr有差别:
unique_ptr指定删除器和shared_ptr有差别:
std::shared_ptr
ptr(new int(1), [](int* p) { delete p; }); //正确 std::unique_ptr ptr(new int(1), [](int* p) { delete p; }); //错误 unique_ptr指定删除器的时候需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器。可以写成:
std::unique_ptr
ptr(new int(1), [](int* p) { delete p; }); 这种写法在lambda没有捕获变量的情况下正确,如果捕获了变量,则会编译报错:
std::unique_ptr
ptr(new int(1), [&](int* p) { delete p; });//错误,捕获变量 lambda在没有捕获变量的情况下是可以直接转换为函数指针的,一旦捕获了就无法转换。 如果希望unique_ptr的删除器支持lambda,可以写成:
std::unique_ptr
> ptr(new int(1), [&](int* p) { delete p; }); 自定义unique_ptr的删除器:
#include
#include struct autoDelete { void operator()(int*p) { delete p; } }; int main() { std::unique_ptr p(new int(1)); return 0; }
弱引用指针weak_ptr是用来监视shared_ptr的,不会使引用计数加1,它不管理shared_ptr内部的指针,主要是为了监视shared_ptr的生命周期。
weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,主要是为了通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,只是作为一个旁观者来监视shared_ptr中的资源是否存在。weak_ptr还可以用来返回this指针和解决循环引用问题。
用法:
1、通过use_count()方法获得当前观测资源的引用计数
shared_ptr
sp(new int(10)); weak_ptr wp(sp); cout<
2、通过expired()方法来判断所观测的资源是否已经被释放
shared_ptr
sp(new int(10)); weak_ptr wp(sp); if(wp.expired()) { cout<<"weak_ptr无效,监视的智能指针已被释放"<
3、通过lock方法来获取所监视的shared_ptr
std::weak_ptr
gw; void f() { if(gw.expired()) //所监视的shared_ptr是否被释放 { cout<<"gw已释放 \n"; } else { auto spt = gw.lock(); cout<<*spt<<"\n"; } } int main() { { auto sp = std::make_shared (42); gw = sp; f(); } f(); } //依次输出: 42 gw已释放
weak_ptr返回this指针:
在shared_ptr中:
不能直接将this指针返回为shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回智能指针。
原因是:std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观测this智能指针,调用shared_from_this方法时,会调用内部这个weak_ptr的lock方法,将所观测的shared_ptr返回。
class A:public std::enable_shared_from_this { std::shared_ptr getSelf() { return shared_from_this(); } ~A() { cout<<"A delete"<
sp(new A); std::shared_ptr p = sp->getSelf(); //输出:A delete /* 在外面创建A对象的智能指针和通过该对象返回this智能指针都是安全的,因为 shared_from_this()是内部的weak_ptr调用lock方法之后返回的智能指针。在 离开作用域之后,sp的引用计数减为0,A对象被析构,不会出现A对象被析构两 次的问题。 */
注意:
获取自身智能指针的函数仅在shared_ptr
weak_ptr解决循环引用问题:
struct A; struct B; struct A { std::shared_ptr bptr; ~A() { cout<<"A del" }; }; struct B { std::weak_ptr aptr; ~B() { cout<<"B del" }; }; void testPtr() { { std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; }//对象应该销毁 } //输出: A del B del /* 在对B的成员赋值时,即执行bp->aptr = ap;时,由于aptr是weak_ptr, 它并不会增加引用计数,所以ap的引用计数仍然会是1,在离开作用域之后, ap的引用计数会减为0,A指针会被析构,析构后其内部的bptr的引用计数会减为1, 然后在离开作用域之后bp引用计数又从1减为0,B对象也将被析构,不会造成内部泄漏。 */
unique_ptr和shared_ptr的使用场景:要根据实际应用需求来选择,如果希望只有一个智能指针管理资源或管理数组就用unique_ptr,要是希望多个智能指针管理同一个资源就用shared_ptr。
weak_ptr是shared_ptr的助手,只是监视shared_ptr管理的资源是否被释放,本身并不操作或者管理资源,用于解决shared_ptr循环引用和返回this指针的问题。