智能指針與容器使用時的問題


from https://linkxzhou.github.io/post/stl%E6%99%BA%E8%83%BD%E6%8C%87%E9%92%88%E4%B8%8E%E5%AE%B9%E5%99%A8%E7%BB%93%E5%90%88%E4%BD%BF%E7%94%A8%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88/#

智能指針簡介

由於C++語言沒有自動內存回收機制,程序員每次new出來的內存都要手動delete,為了有效緩解忘記內存delete,因此c++中auto_ptr、unique_ptr和shared_ptr這類將類封裝成指針的模板對象,並在析構函數里編寫delete語句刪除指針指向的內存空間;對於編譯器來說,智能指針實際上是一個棧對象,並非指針類型,在棧對象生命期即將結束時,智能指針通過析構函數釋放有它管理的堆內存,所以智能指針都重載了“operator->”操作符,直接返回對象的引用,用以操作對象。那么先看幾個栗子:

class SimpleTest { public: SimpleTest(int test_id) : test_id_(test_id) {} ~SimpleTest() {} void DoSomething() { std::cout << "DoSomething ... : " << test_id_ << std::endl; } private: int test_id_; }; void TestAutoPtr1() { std::cout << "TestAutoPtr1 Test" << std::endl; std::auto_ptr<SimpleTest> my_ptr(new SimpleTest(1)); if (my_ptr.get()) // 判斷指針是否為空 { my_ptr->DoSomething(); } } void TestAutoPtr2() { std::cout << "TestAutoPtr2 Test" << std::endl; std::auto_ptr<SimpleTest> my_ptr(new SimpleTest(2)); if (my_ptr.get()) // 判斷指針是否為空 { std::auto_ptr<SimpleTest> my_ptr0; // 創建一個新的std::auto_ptr<SimpleTest>對象 my_ptr0 = my_ptr; // 復制舊的 my_ptr 給 my_ptr0 my_ptr0->DoSomething(); my_ptr->DoSomething(); } } void TestAutoPtr3() { std::cout << "TestAutoPtr3 Test" << std::endl; std::shared_ptr<SimpleTest> my_ptr(new SimpleTest(3)); if (my_ptr.get()) // 判斷指針是否為空 { std::shared_ptr<SimpleTest> my_ptr0; // 創建一個新的std::auto_ptr<SimpleTest>對象 std::cout << "[1]UseCount: " << my_ptr.use_count() << std::endl; my_ptr0 = my_ptr; // 復制舊的 my_ptr 給 my_ptr0 my_ptr0->DoSomething(); my_ptr->DoSomething(); std::cout << "[2]UseCount: " << my_ptr.use_count() << std::endl; } } void TestAutoPtr4() { std::cout << "TestAutoPtr4 Test" << std::endl; std::unique_ptr<SimpleTest> my_ptr(new SimpleTest(4)); if (my_ptr.get()) // 判斷指針是否為空 { std::unique_ptr<SimpleTest> my_ptr0; // 創建一個新的std::auto_ptr<SimpleTest>對象 my_ptr0 = std::move(my_ptr); // 復制舊的 my_ptr 給 my_ptr0 my_ptr0->DoSomething(); my_ptr->DoSomething(); } } 

那么問題來了:TestAutoPtr1、TestAutoPtr2、TestAutoPtr3、TestAutoPtr4這幾個函數,那幾個會出現crash呢?現在這里賣個關子,如果想了解智能指針童鞋在文章最后會給出相應的答案,下面先進入文章的問題:“stl智能指針與容器結合使用會發生什么”。

stl智能指針與容器結合使用會發生什么

先上一段代碼:

void TestAutoPtr5() { std::cout << "TestAutoPtr5 Test" << std::endl; std::vector< std::unique_ptr<SimpleTest> > vc; vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(5))); vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(6))); vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(7))); // 1----- for (std::vector< std::unique_ptr<SimpleTest> >::iterator iter = vc.begin(); iter != vc.end(); iter++) { (*iter)->DoSomething(); } // 2----- vc.resize(5); for (std::vector< std::unique_ptr<SimpleTest> >::iterator iter = vc.begin(); iter != vc.end(); iter++) { (*iter)->DoSomething(); } } 

那么這段代碼會發生什么?這段代碼會發生crash,但是如果注釋掉2下面那段代碼,就沒有問題。其實這段是由於resize引起的,vector對於空間不夠的情況下,resize會擴展空間,那么同時會將對應的容器元素默認構造,那么std::unique_ptr默認構造會產生一個為NULL的指針,如果直接使用沒有用get()判斷那么就會core,但是如果vector的空間是夠了(比如vc.resize(3)),resize不會產生新的元素,那么使用迭代器就不會有問題。
以上只是舉了std::unique_ptr智能指針,使用其他的轉移值也是一樣,所以在使用容器的時候一定要注意容器是不是將元素里面的指針值轉移或者智能指針有默認構造函數,將指針賦值為空(對於沒有默認構造函數的智能指針不會有問題,因為一般在編譯的時候就報錯了,如:scoped_ptr)。

以此可看出智能指針盡量不要指向vector容器類型,因為當vector擴容時,智能指針便不再生效,引起程序的崩潰或未定義的行為。
=========================以上是我踩得坑,記錄一下,方便大家翻閱=========================

回答第一節的問題:TestAutoPtr1不會core、TestAutoPtr2會core、TestAutoPtr3不會core、TestAutoPtr4會core,具體可以了解一下智能指針的指針轉移過程。

引用

[1]http://blog.csdn.net/xt_xiaotian/article/details/5714477
[2]effective c++


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM