【C++】shared_ptr中循环引用问题

本文探讨了C++中Parent和Child类通过智能指针形成的循环引用问题,展示了如何通过使用weak_ptr来打破引用循环,确保析构函数正确执行。通过实例和输出结果,解释了weak_ptr的作用及其在避免内存泄漏中的关键作用。
摘要由CSDN通过智能技术生成

测试代码如下:

代码中含有两个类,Parent和Child。

Parent类成员中有一个Child类的智能指针。

Child类成员中有一个Parent类的智能指针。

 #include <iostream>
 #include <memory>
 ​
 class Child;
 typedef std::shared_ptr<Child> ChildPtr;
 class Parent;
 typedef std::shared_ptr<Parent> ParentPtr;
 ​
 class Parent
 {
 public:
     Parent(){std::cout << "Parent hello\n";}
     ~Parent(){std::cout << "Parent bye\n";}
     void setSon(ChildPtr& c){son=c;}
 private:
     ChildPtr son;
 };
 ​
 class Child
 {
 public:
     Child(){std::cout << "Child hello\n";}
     ~Child(){std::cout << "Child bye\n";}
     void setParent(ParentPtr& p){parent=p;}
 private:
     ParentPtr parent;
 };
 ​
 void testParnentAndChild()
 {
     ParentPtr p(new Parent());
     ChildPtr c(new Child());
     p->setSon(c);
     c->setParent(p);
 }
 ​
 int main()
 {
     testParnentAndChild();
     return 0;
 }

运行后得到如下结果:

 

显而易见的是,testParnentAndChild()函数中的p和c对象构造成功,但是在离开该作用域时没有调用Parent和Child类的析构函数。这与shared_ptr内存管理机制有所矛盾。

将testParnentAndChild()函数进行修改后在运行,能得到以下输出

 void testParnentAndChild()
 {
     ParentPtr p(new Parent());
     ChildPtr c(new Child());
     std::cout << "Before set\n";
     std::cout << "p_useconut: " << p.use_count() << std::endl;
     std::cout << "c_useconut: " << c.use_count() << std::endl;
     p->setSon(c);
     c->setParent(p);
     std::cout << "After set\n";
     std::cout << "p_useconut: " << p.use_count() << std::endl;
     std::cout << "c_useconut: " << c.use_count() << std::endl;
 }

 

由运行结果可以的看出,在进行set操作之后,p和c的引用计数分别+1,这是因为p中的成员p→son引用了c,c中成员c→parent引用了p,所以在离开函数作用域时,因为p和c失效了,即使p和c的引用计数-1,p和c的引用计数也不为0。所以无法调用析构函数(shared_ptr中引用计数为0时调用析构函数)。

解决方法:将其中一个对象的类成员修改为weak_ptr,打破循环引用。

 #include <iostream>
 #include <memory>
 ​
 class Child;
 typedef std::shared_ptr<Child> ChildPtr;
 typedef std::weak_ptr<Child> ChildWeakPtr;
 class Parent;
 typedef std::shared_ptr<Parent> ParentPtr;
 ​
 class Parent
 {
 public:
     Parent(){std::cout << "Parent hello\n";}
     ~Parent(){std::cout << "Parent bye\n";}
     void setSon(ChildPtr& c){son=c;}
 private:
     ChildWeakPtr son;//修改为weak_ptr
 };
 ​
 class Child
 {
 public:
     Child(){std::cout << "Child hello\n";}
     ~Child(){std::cout << "Child bye\n";}
     void setParent(ParentPtr& p){parent=p;}
 private:
     ParentPtr parent;
 };
 ​
 void testParnentAndChild()
 {
     ParentPtr p(new Parent());
     ChildPtr c(new Child());
     std::cout << "Before set\n";
     std::cout << "p_useconut: " << p.use_count() << std::endl;
     std::cout << "c_useconut: " << c.use_count() << std::endl;
     p->setSon(c);
     c->setParent(p);
     std::cout << "After set\n";
     std::cout << "p_useconut: " << p.use_count() << std::endl;
     std::cout << "c_useconut: " << c.use_count() << std::endl;
 }
 ​
 int main()
 {
     testParnentAndChild();
     return 0;
 }

其运行结果为:

 

可以看到,离开testParnentAndChild()函数作用域时,p和c都成功的调用了其析构函数。观察输出结果可以发现,在set之后,c的usecount并没有增加,这是因为使用weak_ptr时不会影响其引用计数(weak_ptr在外部引用计数不为0时有效)。

所以在离开testParnentAndChild()函数作用域时,一旦c能正常析构,c也就不会再引用p,打破了循环引用,保证p也能正常析构。

shared_ptr循环引用是指多个shared_ptr实例互相引用,导致对象无法被释放的情况。shared_ptr采用引用计数的智能指针,可以指向同一个动态对象,并维护了一个共享的引用计数器。当多个shared_ptr实例相互引用时,它们的引用计数无法降为零,从而导致内存泄漏。 为了解决shared_ptr引起的循环引用问题,可以使用weak_ptr指针。weak_ptr是一种弱引用,不能单独使用,只能配合shared_ptr使用。相比之下,weak_ptr并不增加引用计数,它只是提供了对被共享对象的一个非拥有的引用。通过使用weak_ptr,可以打破shared_ptr之间的循环引用,使对象能够正常释放。 在C++,我们可以使用weak_ptr的lock()函数来获取一个可用的shared_ptr对象,从而访问被共享对象的成员函数和成员变量。weak_ptr没有重载*和->运算符,因此无法直接访问对象,但可以使用lock()函数来获取shared_ptr,并通过该shared_ptr来访问对象的成员。 通过使用weak_ptr解决shared_ptr循环引用问题,可以避免内存泄漏和资源占用过多的情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++11智能指针之weak_ptr详解](https://download.csdn.net/download/weixin_38740201/14841441)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [C++ 智能指针循环引用问题](https://blog.csdn.net/qq_28584889/article/details/88726324)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [C++智能指针(share_ptr)及其循环引用问题](https://blog.csdn.net/feikudai8460/article/details/104747490)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值