#include <iostream>
using namespace std;
#include<memory>
// 1. 基本使用方法
// 弱引用智能指针 std::weak_ptr 可以看做是 shared_ptr 的助手,它不管理 shared_ptr 内部的指针。std::weak_ptr 没有重载操作符 * 和 ->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视 shared_ptr 中管理的资源是否存在。
// 1.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;
// 在 C++11 中,weak_ptr 的初始化可以通过以上提供的构造函数来完成初始化,具体使用方法如下
void test()
{
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;
// weak_ptr<int> wp1; 构造了一个空 weak_ptr 对象
// weak_ptr<int> wp2(wp1); 通过一个空 weak_ptr 对象构造了另一个空 weak_ptr 对象
// weak_ptr<int> wp3(sp); 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象
// wp4 = sp; 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象(这是一个隐式类型转换)
// wp5 = wp3; 通过一个 weak_ptr 对象构造了一个可用的 weak_ptr 实例对象
}
// 1.2 其他常用方法
// 1.2.1 use_count()
// 通过调用 std::weak_ptr 类提供的 use_count() 方法可以获得当前所观测资源的引用计数,函数原型如下:
// C++
// 1
// 2
// // 函数返回所监测的资源的引用计数
// long int use_count() const noexcept;
// 修改一下上面的测试程序,添加打印资源引用计数的代码:
void test2()
{
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; //0
cout << "wp2: " << wp2.use_count() << endl; //0
cout << "wp3: " << wp3.use_count() << endl; //1
cout << "wp4: " << wp4.use_count() << endl; //1
cout << "wp5: " << wp5.use_count() << endl; //1
//通过打印的结果可以知道,虽然弱引用智能指针wp3,wp4,wp5监测的资源是同一个,但是它的引用
//计数并没有发生任何的改变,也进一步证明了weak_ptr只是监测资源,并不管理资源。
}
//1.2.3 expired()
//通过调用std::weak_ptr类提供的expired()方法来判断观测的资源是否已经被释放。
void test3()
{
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;
//weak_ptr监测的就是shared_ptr 管理的资源,当共享智能指针调用shared.reset();之后管理的资源被释放,
//因此weak_expired()函数的结果返回true,表示监测的资源已经不存在了。
}
//1.2.3 lock()
//通过调用std:;weak_ptr类提供的lock()方法来获取管理所监测资源的shared_ptr对象。
void test4()
{
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;
// sp2 = wp.lock(); 通过调用 lock() 方法得到一个用于管理 weak_ptr 对象所监测的资源的共享智能指针对象,使用这个对象初始化 sp2,此时所监测资源的引用计数为 2
// sp1.reset(); 共享智能指针 sp1 被重置,weak_ptr 对象所监测的资源的引用计数减 1
// sp1 = wp.lock();sp1 重新被初始化,并且管理的还是 weak_ptr 对象所监测的资源,因此引用计数加 1
// 共享智能指针对象 sp1 和 sp2 管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是 520
}
//1.2.4reset()
//通过调用std::weak_ptr类提供的reset()方法来清空对象,使其不监测任何资源
//2返回管理this的shared_ptr
//如果在类中编写了一个函数,通过这个得到管理当前对象的共享智能指针,我们可能会写出如下代码:
struct Test{
shared_ptr<Test>getSharedPtr()
{
return shared_ptr<Test>(this);
}
~Test()
{
cout<<"class Test id distruct..."<<endl;
}
};
void test5()
{
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;
// 通过输出的结果可以看到一个对象被析构了两次,其原因是这样的:在这个例子中使用同一个指针 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 对象。
}
struct Test2:public enable_shared_from_this<Test2>
{
shared_ptr<Test2>getSharedPtr()
{
return shared_from_this();
}
~Test2()
{
cout<<"class Test2 id distruct..."<<endl;
}
};
void test6()
{
shared_ptr<Test2> sp1(new Test2);
cout<<"use_count:"<<sp1.use_count()<<endl;
shared_ptr<Test2> sp2=sp1->getSharedPtr();
cout<<"use_count"<<sp1.use_count()<<endl;
}
//3解决循环引用问题
//智能指针如果循环引用会导致内存泄漏,比如下面的例子
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 test7()
{
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;
}
//在测试程序中,共享智能指针 ap、bp 对 TA、TB 实例对象的引用计数变为 2,在共享智能指针离开作用域之后引用计数只能减为1,这种情况下不会去删除智能指针管理的内存,导致类 TA、TB 的实例对象不能被析构,最终造成内存泄露。通过使用 weak_ptr 可以解决这个问题,只要将类 TA 或者 TB 的任意一个成员改为 weak_ptr,修改之后的代码如下:
// 通过输出的结果可以看到类 TA 或者 TB 的对象被成功析构了。
// 上面程序中,在对类 TA 成员赋值时 ap->bptr = bp; 由于 bptr 是 weak_ptr 类型,这个赋值操作并不会增加引用计数,所以 bp 的引用计数仍然为 1,在离开作用域之后 bp 的引用计数减为 0,类 TB 的实例对象被析构。
// 在类 TB 的实例对象被析构的时候,内部的 aptr 也被析构,其对 TA 对象的管理解除,内存的引用计数减为 1,当共享智能指针 ap 离开作用域之后,对 TA 对象的管理也解除了,内存的引用计数减为 0,类 TA 的实例对象被析构
int main() {
test2();
test3();
test4();
test5();
test6();
test7();
return 0;
}