shared_ptr中迴圈引用問題
阿新 • • 發佈:2021-02-09
測試程式碼如下:
程式碼中含有兩個類,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也能正常析構。