日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

为什么auto_ptr智能指针不能作为STL标准容器的元素

發布時間:2023/12/20 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么auto_ptr智能指针不能作为STL标准容器的元素 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上個星期的博客shared_ptr源碼剖析里其實遺漏了一個問題:為什么auto_ptr不可以作為STL標準容器的元素,而shared_ptr可以?? 我在網上看了好多篇講shared_ptr的文章里講到了這個問題,不過大多文章只是簡單兩筆帶過。我研究了一下這個問題,發現還是有挺多有價值的內容,所以把這個問題單獨成一篇博客和大家分享。

先從表象上看看這個問題,假如有這樣的一段代碼,是否能夠運行?

?

int costa_foo() {vector< auto_ptr<int> > v(10);int i=0;for(;i<10;i++){ v[i]=auto_ptr<int>(new int(i));} }

答案是否定的,甚至這段代碼是無法編譯通過的。g++編譯器會報下面這個錯誤:

?

?

In file included from /usr/include/c++/4.4/memory:51,from foo.cpp:x: /usr/include/c++/4.4/bits/stl_construct.h: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = std::auto_ptr<int>, _T2 = std::auto_ptr<int>]’: /usr/include/c++/4.4/bits/stl_uninitialized.h:187: instantiated from ‘static void std::__uninitialized_fill_n<<anonymous> >::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = std::auto_ptr<int>*, _Size = unsigned int, _Tp = std::auto_ptr<int>, bool <anonymous> = false]’ /usr/include/c++/4.4/bits/stl_uninitialized.h:223: instantiated from ‘void std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = std::auto_ptr<int>*, _Size = unsigned int, _Tp = std::auto_ptr<int>]’ /usr/include/c++/4.4/bits/stl_uninitialized.h:318: instantiated from ‘void std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>&) [with _ForwardIterator = std::auto_ptr<int>*, _Size = unsigned int, _Tp = std::auto_ptr<int>, _Tp2 = std::auto_ptr<int>]’ /usr/include/c++/4.4/bits/stl_vector.h:1035: instantiated from ‘void std::vector<_Tp, _Alloc>::_M_fill_initialize(size_t, const _Tp&) [with _Tp = std::auto_ptr<int>, _Alloc = std::allocator<std::auto_ptr<int> >]’ /usr/include/c++/4.4/bits/stl_vector.h:230: instantiated from ‘std::vector<_Tp, _Alloc>::vector(size_t, const _Tp&, const _Alloc&) [with _Tp = std::auto_ptr<int>, _Alloc = std::allocator<std::auto_ptr<int> >]’ foo.cpp:22: instantiated from here /usr/include/c++/4.4/bits/stl_construct.h:74: error: passing ‘const std::auto_ptr<int>’ as ‘this’ argument of ‘std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = int, _Tp = int]’ discards qualifiers

錯誤出在這一行:

vector< auto_ptr<int> > v(10);

?

?

這個錯誤是什么含義呢,我們看stl_construct.h的74行所在的函數:

64 /**65 * Constructs an object in existing memory by invoking an allocated66 * object's constructor with an initializer.67 */68 template<typename _T1, typename _T2>69 inline void70 _Construct(_T1* __p, const _T2& __value)71 {72 // _GLIBCXX_RESOLVE_LIB_DEFECTS73 // 402. wrong new expression in [some_]allocator::construct74 ::new(static_cast<void*>(__p)) _T1(__value);75 }

我來直接說這個函數的作用:把第二個參數__T2& value拷貝構造一份,然后復制到T1這個指針所指向的位置。它是如何做到的呢?

?

看第第74行, 這里使用new的方法和我們平常所見到的似乎略有不同。 這是一個placement new。 placement new的語法是:

new(p) T(value)

placement new并不會去堆上申請一塊內存,而是直接使用指針p指向的內存,將value對象拷貝一份放到p指向的內存上去。

?

看到這里我就知道為什么編譯器在編譯本文開頭的那段代碼時會報這段錯誤” /usr/include/c++/4.4/bits/stl_construct.h:74: error: passing ‘const std::auto_ptr<int>’ as ‘this’ argument of ‘std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = int, _Tp = int]’ discards qualifiers" 了。通過查看auto_ptr的源代碼(位置在 c++頭文件目錄的 backward/auto_ptr.h)可以發現, auto_ptr并沒有一個參數為const auto_ptr& 的拷貝構造函數。換而言之, auto_ptr進行拷貝構造的時候,必需要修改作為參數傳進來的那個auto_ptr。

auto_ptr的拷貝構造函數是這樣寫的:

auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }

可以看出來, 在拷貝構造一個auto_tr的時候, 必需要把參數的那個auto_ptr給release掉,然后在把參數的_M_ptr指針賦值給自己的_M_ptr指針。補充說明一下, _M_ptr是auto_ptr的一個成員,指向auto_ptr管理的那塊內存,也就是在auto_ptr生命周期之外被釋放掉的那塊內存。

當看到這里的時候,整個問題已經能解釋通了。STL容器在分配內存的時候,必須要能夠拷貝構造容器的元素。而且拷貝構造的時候,不能修改原來元素的值。而auto_ptr在拷貝構造的時候,一定會修改元素的值。所以STL元素不能使用auto_ptr。

不過,還有一個很重要的問題沒有解釋。那就是為什么在設計auto_ptr的時候,拷貝構造要修改參數的值呢?

其實這問題很簡單,不看代碼也可以解釋清楚。auto_ptr內部有一個指針成員_M_ptr,指向它所管理的那塊內存。而拷貝構造的時候,首先把參數的_M_ptr的值賦值給自己的_M_ptr,然后把參數的_M_ptr指針設成NULL,。如果不這樣設計,而是直接把參數的_M_ptr指針賦值給自己的, 那么兩個auto_ptr的_M_ptr指向同一塊內存,在析構auto_ptr的時候就會出問題: 假如兩個auto_ptr的_M_ptr指針指向了同一塊內存,那么第一個析構的那個auto_ptr指針就把_M_ptr指向的內存釋放掉了,造成后一個auto_ptr在析構時釋要放一塊已經被釋放掉的內存,這明顯不科學,會產生程序的段錯誤而crash掉。

而shared_ptr則不存在這個問題, 在拷貝構造和賦值操作的時候,只會引起公用的引用計數的+1,不存在拷貝構造和賦值操作的參數不能是const的問題。

總結:

1 auto_ptr不能作為STL標準容器的元素。

2 auto_ptr在拷貝復制和賦值操作時,都會改變參數。這是因為兩個auto_ptr不能管理同一塊內存。

----------------------------------------------------------------------------------

2013年8月16日

?

看來真是有學而時習之的必要。 10分鐘之前我竟然在搜這個問題,而全然忘了以前自己研究過。

不過看到stackoverflow上的回答覺得比我寫的簡單多了, 抄過來:

原文鏈接

http://stackoverflow.com/questions/111478/why-is-it-wrong-to-use-stdauto-ptr-with-standard-containers

The C++ Standard says that an STL element must be "copy-constructible" and "assignable." In other words, an element must be able to be assigned or copied and the two elements are logically independent.std::auto_ptrdoes not fulfill this requirement.

Take for example this code:

class X { };std::vector<std::auto_ptr<X> > vecX; vecX.push_back(new X);std::auto_ptr<X> pX = vecX[0]; // vecX[0] is assigned NULL

To overcome this limitation, you should use the?std::unique_ptr,?std::shared_ptr?or?std::weak_ptr?smart pointers or the boost equivalents if you don't have C++11.?Here is the boost library documentation for these smart pointers.

?

然后又去翻了一下auto_ptr的源碼,? 看到operator =的代碼這樣寫的:

223?????? operator=(auto_ptr& __a) throw()
224?????? {
225???? reset(__a.release());
226???? return *this;
227?????? }

?

多個auto_ptr不能管理同一片內存, 執行=的時候,就把原來的auto_ptr給干掉。其實從邏輯上來講,如果多個auto_ptr管理同一塊內存肯定有問題。第一個auto_ptr析構的時候就應該把內存釋放掉了, 第二個auto_ptr再析構的時候,就要去釋放已經被釋放的內存了, 程序肯定掛掉。

總結

以上是生活随笔為你收集整理的为什么auto_ptr智能指针不能作为STL标准容器的元素的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。