1. shared_ptr
1.1 通过构造函数初始化
如果智能指针被初始化了一块有效内存,那么这块内存的引用计数+1,如果智能指针没有被初始化或者被初始化为nullptr空指针,引用计数不会+1。另外,不要使用一个原始指针初始化多个shared_ptr。
#include <iostream> #include <memory> using namespace std; int main() { // 使用智能指针管理一块 int 型的堆内存 shared_ptr<int> ptr1(new int(520)); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; // 使用智能指针管理一块字符数组对应的堆内存 shared_ptr<char> ptr2(new char[12]); cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; // 创建智能指针对象, 不管理任何内存 shared_ptr<int> ptr3; cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; // 创建智能指针对象, 初始化为空 shared_ptr<int> ptr4(nullptr); cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl; return 0; }
int *p = new int; shared_ptr<int> p1(p); shared_ptr<int> p2(p); // error, 编译不会报错, 运行会出错
1.2 通过拷贝和移动构造函数初始化
#include <iostream> #include <memory> using namespace std; int main() { // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1 shared_ptr<int> ptr1(new int(520)); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; //调用拷贝构造函数 shared_ptr<int> ptr2(ptr1); cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; shared_ptr<int> ptr3 = ptr1; cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; //调用移动构造函数 shared_ptr<int> ptr4(std::move(ptr1)); cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl; std::shared_ptr<int> ptr5 = std::move(ptr2); cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl; return 0; }
ptr1管理的内存引用计数: 1 ptr2管理的内存引用计数: 2 ptr3管理的内存引用计数: 3 ptr4管理的内存引用计数: 3 ptr5管理的内存引用计数: 3
1.3 通过std::make_shared初始化
通过C++提供的std::make_shared() 就可以完成内存对象的创建并将其初始化给智能指针
template< class T, class... Args > shared_ptr<T> make_shared( Args&&... args );
-
T
:模板参数的数据类型 -
Args&&... args
:要初始化的数据,如果是通过make_shared创建对象,需按照构造函数的参数列表指定
#include <iostream> #include <string> #include <memory> using namespace std; class Test { public: Test() { cout << "construct Test..." << endl; } Test(int x) { cout << "construct Test, x = " << x << endl; } Test(string str) { cout << "construct Test, str = " << str << endl; } ~Test() { cout << "destruct Test ..." << endl; } }; int main() { // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1 shared_ptr<int> ptr1 = make_shared<int>(520); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; shared_ptr<Test> ptr2 = make_shared<Test>(); cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; shared_ptr<Test> ptr3 = make_shared<Test>(520); cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; shared_ptr<Test> ptr4 = make_shared<Test>("我是要成为海贼王的男人!!!"); cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl; return 0; }
ptr1管理的内存引用计数: 1 construct Test... ptr2管理的内存引用计数: 1 construct Test, x = 520 ptr3管理的内存引用计数: 1 construct Test, str = 我是要成为海贼王的男人!!! ptr4管理的内存引用计数: 1 destruct Test ... destruct Test ... destruct Test ...
1.4 通过 reset方法初始化
//函数原型 void reset() noexcept; template< class Y > void reset( Y* ptr ); template< class Y, class Deleter > void reset( Y* ptr, Deleter d ); template< class Y, class Deleter, class Alloc > void reset( Y* ptr, Deleter d, Alloc alloc );
- ptr:指向要取得所有权的对象的指针
- d:指向要取得所有权的对象的指针
- aloc:内部存储所用的分配器
#include <iostream> #include <string> #include <memory> using namespace std; int main() { // 使用智能指针管理一块 int 型的堆内存, 内部引用计数为 1 shared_ptr<int> ptr1 = make_shared<int>(520); shared_ptr<int> ptr2 = ptr1; shared_ptr<int> ptr3 = ptr1; shared_ptr<int> ptr4 = ptr1; cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl; ptr4.reset(); cout << "ptr1管理的内存引用计数: " << ptr1.use_count() << endl; cout << "ptr2管理的内存引用计数: " << ptr2.use_count() << endl; cout << "ptr3管理的内存引用计数: " << ptr3.use_count() << endl; cout << "ptr4管理的内存引用计数: " << ptr4.use_count() << endl; shared_ptr<int> ptr5; ptr5.reset(new int(250)); cout << "ptr5管理的内存引用计数: " << ptr5.use_count() << endl; return 0; }
ptr1管理的内存引用计数: 4 ptr2管理的内存引用计数: 4 ptr3管理的内存引用计数: 4 ptr4管理的内存引用计数: 4 ptr1管理的内存引用计数: 3 ptr2管理的内存引用计数: 3 ptr3管理的内存引用计数: 3 ptr4管理的内存引用计数: 0 ptr5管理的内存引用计数: 1
对于一个未初始化的共享智能指针,可以通过reset方法来初始化,当智能指针中有值的时候,调用reset会使引用计数减1。
1.5 获取原始指针
// 获取原始地址 T* get() const noexcept;
#include <iostream> #include <string> #include <memory> using namespace std; int main() { int len = 128; shared_ptr<char> ptr(new char[len]); // 得到指针的原始地址 char* add = ptr.get(); memset(add, 0, len); strcpy(add, "我是要成为海贼王的男人!!!"); cout << "string: " << add << endl; shared_ptr<int> p(new int); *p = 100; cout << p.get() << " " << *p << endl; return 0; }
string: 我是要成为海贼王的男人!!! 0000026F48FE9410 100
2. weak_ptr
弱引用智能指针
std::weak_ptr
可以看做是shared_ptr
的助手, 它不管理shared_ptr内部的指针。std::weak_ptr没有重载操作符*和->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数
它的主要作用就是作为一个旁观者监视shared_ptr中管理的资源是否存在。
2.1 基本使用方式
初始化
// 默认构造函数 constexpr weak_ptr() noexcept; // 拷贝构造 weak_ptr (const weak_ptr& x) noexcept; template <class U> weak_ptr (const weak_ptr<U>& x) noexcept; // 通过shared_ptr对象构造 template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;
具体使用方法如下:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> sp(new int); weak_ptr<int> wp1; weak_ptr<int> wp2(wp1); weak_ptr<int> wp3(sp); weak_ptr<int> wp4; wp4 = sp; weak_ptr<int> wp5; wp5 = wp3; return 0; }
- weak_ptr
wp1;构造了一个空weak_ptr对象 - weak_ptr
wp2(wp1);通过一个空weak_ptr对象构造了另一个空weak_ptr对象 - weak_ptr
wp3(sp);通过一个shared_ptr对象构造了一个可用的weak_ptr实例对象 - wp4 = sp;通过一个
shared_ptr
对象构造了一个可用的weak_ptr实例对象(这是一个隐式类型转换) - wp5 = wp3;通过一个weak_ptr对象构造了一个可用的weak_ptr实例对象
4.2.1.2 其他常用方法
4.2.1.2.1 use_count()
通过调用std::weak_ptr
类提供的use_count()
方法可以获得当前所观测资源的引用计数
// 函数原型 // 函数返回所监测的资源的引用计数 long int use_count() const noexcept;
修改一下上面的测试程序,添加打印资源引用计数的代码:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> sp(new int); weak_ptr<int> wp1; weak_ptr<int> wp2(wp1); weak_ptr<int> wp3(sp); weak_ptr<int> wp4; wp4 = sp; weak_ptr<int> wp5; wp5 = wp3; cout << "use_count: " << endl; cout << "wp1: " << wp1.use_count() << endl; cout << "wp2: " << wp2.use_count() << endl; cout << "wp3: " << wp3.use_count() << endl; cout << "wp4: " << wp4.use_count() << endl; cout << "wp5: " << wp5.use_count() << endl; return 0; }
测试程序输出的结果为:
use_count: wp1: 0 wp2: 0 wp3: 1 wp4: 1 wp5: 1
虽然弱引用智能指针wp3、wp4、wp5监测的资源是同一个,但是它的引用计数并没有发生任何的变化,也进一步证明了weak_ptr只是监测资源,并不管理资源
。
4.2.1.2.2 expired()
通过调用std::weak_ptr
类提供的expired()
方法来判断观测的资源是否已经被释放,
// 函数原型 // 返回true表示资源已经被释放, 返回false表示资源没有被释放 bool expired() const noexcept;
函数的使用方法如下:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> shared(new int(10)); weak_ptr<int> weak(shared); cout << "1. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl; shared.reset(); cout << "2. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl; return 0; }
测试代码输出的结果:
1. weak is not expired 2. weak is expired
weak_ptr监测的就是shared_ptr管理的资源
当共享智能指针调用shared.reset();
之后管理的资源被释放,因此weak.expired()函数的结果返回true,表示监测的资源已经不存在了。
4.2.1.2.3 lock()
通过调用std::weak_ptr
类提供的lock()
方法来获取管理所监测资源的shared_ptr对象
// 函数原型 shared_ptr<element_type> lock() const noexcept;
函数的使用方法如下:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> sp1, sp2; weak_ptr<int> wp; sp1 = std::make_shared<int>(520); wp = sp1; sp2 = wp.lock(); cout << "use_count: " << wp.use_count() << endl; sp1.reset(); cout << "use_count: " << wp.use_count() << endl; sp1 = wp.lock(); cout << "use_count: " << wp.use_count() << endl; cout << "*sp1: " << *sp1 << endl; cout << "*sp2: " << *sp2 << endl; return 0; }
测试代码输出的结果为:
use_count: 2 use_count: 1 use_count: 2 *sp1: 520 *sp2: 520
- sp2 = wp.lock();通过调用lock()方法得到一个用于管理weak_ptr对象所监测的资源的共享智能指针对象,使用这个对象初始化sp2,此时所监测资源的引用计数为2
- sp1.reset();共享智能指针sp1被重置,weak_ptr对象所监测的资源的引用计数减1
- sp1 = wp.lock();sp1重新被初始化,并且管理的还是weak_ptr对象所监测的资源,因此引用计数加1
- 共享智能指针对象sp1和sp2管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是520
4.2.1.2.4 reset()
通过调用std::weak_ptr
类提供的reset()
方法来清空对象,使其不监测任何资源
// 函数原型如下 void reset() noexcept;
函数的使用非常简单:
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> sp(new int(10)); weak_ptr<int> wp(sp); cout << "1. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl; wp.reset(); cout << "2. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl; return 0; }
测试代码输出的结果为:
1. wp is not expired 2. wp is expired
weak_ptr对象sp被重置之后,变成了空对象,不再监测任何资源,因此wp.expired()返回true
2.2 返回管理this的shared_ptr
如果在一个类中编写了一个函数,通过这个得到管理当前对象的共享智能指针 我们可能会写出如下代码:
#include <iostream> #include <memory> using namespace std; struct Test { shared_ptr<Test> getSharedPtr() { return shared_ptr<Test>(this); } ~Test() { cout << "class Test is disstruct ..." << endl; } }; int main() { shared_ptr<Test> sp1(new Test); cout << "use_count: " << sp1.use_count() << endl; shared_ptr<Test> sp2 = sp1->getSharedPtr(); cout << "use_count: " << sp1.use_count() << endl; return 0; }
执行上面的测试代码,运行中会出现异常,在终端还是能看到对应的日志输出:
use_count: 1 use_count: 1 class Test is disstruct ... class Test is disstruct ...
通过输出的结果可以看到一个对象被析构了两次 其原因是这样的:在这个例子中使用同一个指针this构造了两个智能指针对象sp1和sp2,这二者之间是没有任何关系的,因为sp2并不是通过sp1初始化得到的实例对象。 在离开作用域之后this将被构造的两个智能指针各自析构,导致重复析构的错误。
这个问题可以通过weak_ptr来解决,通过wek_ptr返回管理this资源的共享智能指针对象shared_ptr。
C++11中为我们提供了一个模板类叫做std::enable_shared_from_this<T>
,这个类中有一个方法叫做shared_from_this()
,通过这个方法可以返回一个共享智能指针,在函数的内部就是使用weak_ptr来监测this对象,并通过调用weak_ptr
的lock()
方法返回一个shared_ptr对象。
修改之后的代码为:
#include <iostream> #include <memory> using namespace std; struct Test : public enable_shared_from_this<Test> { shared_ptr<Test> getSharedPtr() { return shared_from_this(); } ~Test() { cout << "class Test is disstruct ..." << endl; } }; int main() { shared_ptr<Test> sp1(new Test); cout << "use_count: " << sp1.use_count() << endl; shared_ptr<Test> sp2 = sp1->getSharedPtr(); cout << "use_count: " << sp1.use_count() << endl; return 0; }
测试代码输出的结果为:
use_count: 1 use_count: 2 class Test is disstruct ...
注意
:在调用enable_shared_from_this
类的shared_from_this()
方法之前
必须要先初始化函数内部weak_ptr
对象,否则该函数无法返回一个有效的shared_ptr
对象
(具体处理方法可以参考上面的示例代码)。
2.3 解决循环引用问题
智能指针如果循环引用会导致内存泄露,比如下面的例子:
#include <iostream> #include <memory> using namespace std; struct TA; struct TB; struct TA { shared_ptr<TB> bptr; ~TA() { cout << "class TA is disstruct ..." << endl; } }; struct TB { shared_ptr<TA> aptr; ~TB() { cout << "class TB is disstruct ..." << endl; } }; void testPtr() { shared_ptr<TA> ap(new TA); shared_ptr<TB> bp(new TB); cout << "TA object use_count: " << ap.use_count() << endl; cout << "TB object use_count: " << bp.use_count() << endl; ap->bptr = bp; bp->aptr = ap; cout << "TA object use_count: " << ap.use_count() << endl; cout << "TB object use_count: " << bp.use_count() << endl; } int main() { testPtr(); return 0; }
测试程序输出的结果如下:
TA object use_count: 1 TB object use_count: 1 TA object use_count: 2 TB object use_count: 2
在测试程序中,共享智能指针ap、bp对TA、TB实例对象的引用计数变为2,在共享智能指针离开作用域之后引用计数只能减为1
这种情况下不会去删除智能指针管理的内存,导致类TA、TB的实例对象不能被析构,最终造成内存泄露。
通过使用weak_ptr可以解决这个问题,只要将类TA或者TB的任意一个成员改为weak_ptr
修改之后的代码如下:
#include <iostream> #include <memory> using namespace std; struct TA; struct TB; struct TA { weak_ptr<TB> bptr; ~TA() { cout << "class TA is disstruct ..." << endl; } }; struct TB { shared_ptr<TA> aptr; ~TB() { cout << "class TB is disstruct ..." << endl; } }; void testPtr() { shared_ptr<TA> ap(new TA); shared_ptr<TB> bp(new TB); cout << "TA object use_count: " << ap.use_count() << endl; cout << "TB object use_count: " << bp.use_count() << endl; ap->bptr = bp; bp->aptr = ap; cout << "TA object use_count: " << ap.use_count() << endl; cout << "TB object use_count: " << bp.use_count() << endl; } int main() { testPtr(); return 0; }
程序输出的结果:
TA object use_count: 1 TB object use_count: 1 TA object use_count: 2 TB object use_count: 1 class TB is disstruct ... class TA is disstruct ...
通过输出的结果可以看到类TA或者TB的对象被成功析构了。
上面程序中,在对类TA成员赋值时ap->bptr = bp;由于bptr是weak_ptr类型,这个赋值操作并不会增加引用计数
,所以bp的引用计数仍然为1,在离开作用域之后bp的引用计数减为0,类TB的实例对象被析构。
在类TB的实例对象被析构的时候,内部的aptr也被析构,其对TA对象的管理解除,内存的引用计数减为1,当共享智能指针ap离开作用域之后,对TA对象的管理也解除了,内存的引用计数减为0,类TA的实例对象被析构。