|
雖然boost.shared_ptr是個(gè)非常好的東西,使用它可以使得c++程序不需要考慮內(nèi)存釋放的問題,但是還是有很多必須注意的地方。下面羅列了一些本人在實(shí)際工作中經(jīng)常碰到的使用shared_ptr出問題的幾種情況。 1. shared_ptr多次引用同一數(shù)據(jù),如下: { int* pInt = new int[100]; boost::shared_ptr<int> sp1(pInt); // 一些其它代碼之后… boost::shared_ptr<int> sp2(pInt); } 這種情況在實(shí)際中是很容易發(fā)生的,結(jié)果也是非常致命的,它會(huì)導(dǎo)致兩次釋放同一塊內(nèi)存,而破壞堆。 2. 使用shared_ptr包裝this指針帶來的問題,如下: { public: tester() ~tester() { std::cout << "析構(gòu)函數(shù)被調(diào)用!\n"; }
public: boost::shared_ptr<tester> sget() { return boost::shared_ptr<tester>(this); } }; int main() { tester t; boost::shared_ptr<tester> sp = t.sget(); // … return 0; } 也將導(dǎo)致兩次釋放t對(duì)象破壞堆棧,一次是出棧時(shí)析構(gòu),一次就是shared_ptr析構(gòu)。若有這種需要,可以使用下面代碼。 class tester : public boost::enable_shared_from_this<tester> { public: tester() ~tester() { std::cout << "析構(gòu)函數(shù)被調(diào)用!\n"; }
public: boost::shared_ptr<tester> sget() { return shared_from_this(); } }; int main() { boost::shared_ptr<tester> sp(new tester); // 正確使用sp 指針。 sp->sget(); }
3. shared_ptr循環(huán)引用導(dǎo)致內(nèi)存泄露,代碼如下: class parent; class child; typedef boost::shared_ptr<parent> parent_ptr; typedef boost::shared_ptr<child> child_ptr; { public: ~parent() { std::cout <<"父類析構(gòu)函數(shù)被調(diào)用.\n"; }
public: child_ptr children; };
{ public: ~child() { std::cout <<"子類析構(gòu)函數(shù)被調(diào)用.\n"; }
public: parent_ptr parent; };
int main() { parent_ptr father(new parent()); child_ptr son(new child); // 父子互相引用。 father->children = son; son->parent = father; return 0; } 如上代碼,將在程序退出前,father的引用計(jì)數(shù)為2,son的計(jì)數(shù)也為2,退出時(shí),shared_ptr所作操作就是簡(jiǎn)單的將計(jì)數(shù)減1,如果為0則釋放,顯然,這個(gè)情況下,引用計(jì)數(shù)不為0,于是造成father和son所指向的內(nèi)存得不到釋放,導(dǎo)致內(nèi)存泄露。 4. 在多線程程序中使用shared_ptr應(yīng)注意的問題。代碼如下: { public: tester() {} ~tester() {} // 更多的函數(shù)定義… };
void fun(boost::shared_ptr<tester> sp) { // !!!在這大量使用sp指針. boost::shared_ptr<tester> tmp = sp; }
int main() { boost::shared_ptr<tester> sp1(new tester); // 開啟兩個(gè)線程,并將智能指針傳入使用。 boost::thread t1(boost::bind(&fun, sp1)); boost::thread t2(boost::bind(&fun, sp1));
t1.join(); t2.join(); return 0; } 這個(gè)代碼帶來的問題很顯然,由于多線程同是訪問智能指針,并將其賦值到其它同類智能指針時(shí),很可能發(fā)生兩個(gè)線程同時(shí)在操作引用計(jì)數(shù)(但并不一定絕對(duì)發(fā)生),而導(dǎo)致計(jì)數(shù)失敗或無效等情況,從而導(dǎo)致程序崩潰,如若不知根源,就無法查找這個(gè)bug,那就只能向上帝祈禱程序能正常運(yùn)行。 可能一般情況下并不會(huì)寫出上面這樣的代碼,但是下面這種代碼與上面的代碼同樣,如下: class tester { public: tester() {} ~tester() {} public: boost::shared_ptr<int> m_spData; // 可能其它類型。 }; tester gObject; void fun(void) { // !!!在這大量使用sp指針. boost::shared_ptr<int> tmp = gObject.m_spData; }
int main() { // 多線程。 boost::thread t1(&fun); boost::thread t2(&fun);
t1.join(); t2.join(); return 0; } 情況是一樣的。要解決這類問題的辦法也很簡(jiǎn)單,使用boost.weak_ptr就可以很方便解決這個(gè)問題。第一種情況修改代碼如下: class tester { public: tester() {} ~tester() {} // 更多的函數(shù)定義… };
void fun(boost::weak_ptr<tester> wp) { boost::shared_ptr<tester> sp = wp.lock; if (sp) { // 在這里可以安全的使用sp指針. } else { std::cout << “指針已被釋放!” << std::endl; } } int main() { boost::shared_ptr<tester> sp1(new tester); boost.weak_ptr<tester> wp(sp1); // 開啟兩個(gè)線程,并將智能指針傳入使用。 boost::thread t1(boost::bind(&fun, wp)); boost::thread t2(boost::bind(&fun, wp));
t1.join(); t2.join(); return 0; } boost.weak_ptr指針功能一點(diǎn)都不weak,weak_ptr是一種可構(gòu)造、可賦值以不增加引用計(jì)數(shù)來管理shared_ptr的指針,它可以方便的轉(zhuǎn)回到shared_ptr指針,使用weak_ptr.lock函數(shù)就可以得到一個(gè)shared_ptr的指針,如果該指針已經(jīng)被其它地方釋放,它則返回一個(gè)空的shared_ptr,也可以使用weak_ptr.expired()來判斷一個(gè)指針是否被釋放。 boost.weak_ptr不僅可以解決多線程訪問帶來的安全問題,而且還可以解決上面第三個(gè)問題循環(huán)引用。Children類代碼修改如下,即可打破循環(huán)引用: class child { public: ~child() { std::cout <<"子類析構(gòu)函數(shù)被調(diào)用.\n"; }
public: boost::weak_ptr<parent> parent; }; 因?yàn)?/span>boost::weak_ptr不增加引用計(jì)數(shù),所以可以在退出函數(shù)域時(shí),正確的析構(gòu)。 |
|
|