那么如何解除循环引用呢?
weak_ptr
使用weak_ptr 来打破循环引用,它与一个 shared_ptr 绑定,但却不参与引用计数的计算,不论是否有 weak_ptr 指向,一旦最后一个指向对象的 shared_ptr 被销毁,对象就会被释放。weak_ptr 像是 shared_ptr 的一个助手。同时,在需要时,它还能摇身一变,生成一个与它绑定的 shared_ptr 共享引用计数的新 shared_ptr。总而言之,weak_ptr 的作用就是:在需要时变出一个 shared_ptr,在其他时候不干扰 shared_ptr 的引用计数。
它没有重载 * 和 -> 运算符,因此不可以直接通过 weak_ptr 访问对象,典型的用法是通过 lock() 成员函数来获得 shared_ptr,进而使用对象。下面是 weak_ptr 的一般用法:
std::shared_ptr<int> sh = std::make_shared<int>(); // 用一个shared_ptr初始化 std::weak_ptr<int> w(sh); // 变出 shared_ptr std::shared_ptr<int> another = w.lock(); // 判断weak_ptr所观察的shared_ptr的资源是否已经释放 bool isDeleted = w.expired(); |
我们看看它如何来解决上面的循环引用。
/* * @filename: 打破循环引用.cpp * @author: Tanswer * @date: 2018年01月10日 22:43:45 * @description: */ #include <iostream> #include <string> #include <memory> using namespace std; class Children; class Parent{ public: ~Parent(){ cout << "Parent destructor" << endl; } weak_ptr<Children> children; //注意这里 }; class Children{ public: ~Children(){ cout << "Children destructor" << endl; } weak_ptr<Parent> parent; //注意这里 }; void Test() { shared_ptr<Parent> pParent(new Parent()); shared_ptr<Children> pChildren(new Children()); if(pParent && pChildren){ pParent -> children = pChildren; pChildren -> parent = pParent; } // 看一下各自的引用计数 cout << "pParent use_count: " << pParent.use_count() << endl; cout << "pChildren use_count: " << pChildren.use_count() << endl; } int main() { Test(); return 0; } |
结果如下:
可以看到各自的引用计数分别为1,而且函数执行完毕后,各自指向的对象得到析构,这样来解除了上面的循环引用。
当然解决循环引用的方法不止这一种,我们还可以当只剩下最后一个引用的时候手动打破循环引用释放对象;或者当 Parent 的生命期超过 Children 的生命期时,Children 改用一个普通指针指向 Parent。这两种手段都需要程序员去手动控制,比较麻烦而且容易出错,所以一般就使用 weak_ptr 。
错误用法二:多个无关的shared_ptr管理同一裸指针
int *a = new int; shared_ptr<int> p1(a); shared_ptr<int> p2(a); cout << "p1 的引用计数: "<< p1.use_count() << endl; cout << "p2 的引用计数: "<< p2.use_count() << endl; |
p1 和 p2 同时管理同一裸指针 a,这样操作的话,p1 和 p2 拥有两个独立的引用计数器(初始化 p2 时,直接用的裸指针,没有办法获取 p1 的引用计数),于是会导致 a 被 delete 两次,分别由 p1 和 p2 的析构导致。
为了避免这种情况,永远不要将 new 用在 shared_ptr 构造函数参数列表以外的地方,或者干脆不用 new,改用 make_shared。
但是下面的情况会绕过上面这种说法,请看:
class A { public: std::shared_ptr<A> getShared_ptr() { return std::shared_ptr<A>(this); } }; int main() { std::shared_ptr<A> pa = std::make_shared<A>(); std::shared_ptr<A> pbad = pa->getShared_ptr(); return 0; } |
程序直接崩掉,pa 和 pbad 也拥有各自独立的引用计数器,和上面一样。
总而言之,管理同一资源的 shared_ptr 只能由同一个初始化 shared_ptr 通过一系列赋值或者拷贝构造途径得来。更抽象地说,管理同一资源的 shared_ptr 的构造顺序必须是一个无环有向的连通图,无环能够保证没有循环引用,连通性能够保证每个 shard_ptr 都来自相同的源。
enable_shared_from_this
那如何解决“生成 this 指针的 shared_ptr” 的问题?使用 enable_shared_from_this,这是一个基类:
template<typename T>
class enable_shared_from_this {
public:
shared_ptr<T> shared_from_this();
}
如果想要一个由 shared_ptr 管理的类 A 对象能够在方法内部得到 this 指针的 shared_ptr,且返回的 shared_ptr 和管理这个类的 shared_ptr 共享引用计数,只需让这个类派生自 enable_shared_from_this < A> 即可,之后调用 shared_from_this() 即可获得正确的 shared_ptr。
muduo 中的 TcpConnection Class 就是继承 enable_shared_from_this。
class TcpConnection : boost::noncopyable,
public boost::enable_shared_from_this<TcpConnection>
那么上面的例子就可以改成这样:
/* * @filename: shared_ptr_test1.cpp * @author: Tanswer * @date: 2018年01月12日 00:04:29 * @description: */ #include <iostream> #include <memory> using namespace std; class A : public enable_shared_from_this<A> { public: A() { cout << "constructor..." << endl; } ~A() { cout << "destructor..." << endl; } }; int main() { shared_ptr<A> pa = make_shared<A>(); shared_ptr<A> pgood = pa -> shared_from_this(); return 0; } |
上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。