2011-12-25 40 views
1

這個問題閱讀本教程後,提出了一個例子: http://www.cprogramming.com/tutorial/auto_ptr.html「auto_ptr的」和STL容器:寫錯誤使用

那裏你可以找到下面的語句:此行爲的一個微妙的後果是,汽車 _ ptrs在所有情況下都無法正常工作。例如,將ptr對象與標準模板庫一起使用可能會導致問題,因爲STL中的某些函數可能會複製容器中的對象(如vector容器類)。一個例子是sort函數,它使得容器中的一些對象的副本被排序。因此,這個副本可以輕鬆地刪除容器中的數據!

最令人關注的論文「auto_ptr的」告訴我們,類似以下內容: 「從不使用‘auto_ptr的’與STL容器他們經常模仿他們的元素,同時執行固有的操作例如考慮std::vectorsort!」。

所以我的目標是來編寫說明這一點的代碼示例,或者證明這些示例在理論上只在實踐中是真實的和奇怪的

P.S. @everybody_who_also_knows_that_auto_ptr_is_deprecated 我也知道這個。但是,您是否考慮過可能不允許使用新指針容器的技術原因(遺留代碼或舊編譯器)?此外,這個問題是關於老的和壞的(如果你願意)auto_ptr

+4

我相信你不需要再關注這個了,因爲'auto_ptr **作爲一個整體**現在已經被棄用了。改爲使用'unique_ptr',你也可以把它放在容器中。 – Kos 2011-12-25 17:06:32

+1

@Kos我也讀過新標準。請回答我的問題。停止貼出主題的帖子。 – DaddyM 2011-12-25 17:08:19

+0

@DaddyM好吧,它是關於主題的。 – 2011-12-25 17:18:49

回答

4

我沒有MSVC的權利,但來自G ++的錯誤判斷,我想這就是原因:

auto_ptr<T>只有一個「拷貝構造函數」,它採用可變引用(§D.10.1。 1 [auto.ptr.cons]/2-6):

auto_ptr(auto_ptr& a) throw(); 
template<class Y> auto_ptr(auto_ptr<Y>& a) throw(); 

但是vector::push_back將接受const引用(§23.3.6.1[vector.overview]/2)。

void push_back(const T& x); 

所以不可能通過push_back構造一個auto_ptr,因爲沒有構造函數需要一個const引用。

+0

好的。謝謝。但是我對錯誤信息和原因不感興趣。我對正確的代碼感興趣,這些代碼將說明我在文章中引用的觀點。 – DaddyM 2011-12-25 18:29:40

+1

@DaddyM:'std :: vector > vec2 = vec;',然後迭代'vec'。 – kennytm 2011-12-25 18:58:20

+0

謝謝。你已經展示了一個很好的方式來獲取'auto_ptr_my'!我已經標記了你的評論。 - DaddyM 8分鐘前 – DaddyM 2011-12-25 19:47:25

0

結論是:我什至不能編譯這樣的例子。爲什麼他們阻止我做一些我無法編​​譯的東西?

IIRC,這是相反的:編譯器供應商採取措施阻止您編譯您不應該做的事情。標準寫法的方式,他們可能以代碼編譯的方式實現庫,然後無法正常工作。他們也可以用這種方式實現它,這被認爲是優越的,因爲它是編譯器實際允許阻止你做一些愚蠢的事情的幾次之一:)

+0

我明白你的意思。但讓我說相反的事情。正如KennyTM上面提到的那樣:我無法編譯代碼的原因很簡單:'auto_ptr'設計不允許我這樣做。這個設計是在C++標準中聲明的,它看起來像下面這樣:''auto_ptr(auto_ptr &_Right)throw();'所以,這不是編譯器特性。這基於'auto_ptr'設計。因此,我的問題被放大了:*爲什麼我們要互相告訴:「不要在'STL容器中使用'auto_ptr'」,而這是故意不可能的??? * – DaddyM 2011-12-25 18:50:56

+0

因爲可以編寫一個使用這種構造函數的矢量實現,至少在最初的時候,標準並沒有禁止它這樣做。 – 2011-12-26 02:16:24

-2

正確的答案是「永遠不會使用auto_ptr」 - 它被棄用,從未成爲標準的一部分,正是這裏概述的原因。改用std :: unique_ptr。

+0

這是一個無法回答的問題,只是顯而易見的錯誤。 'std :: auto_ptr'是C++ 98標準的一部分(並且在那裏沒有棄用)。它僅在C++ 11標準中被棄用,這也是'unique_ptr'首次正式推出的地方。 (目前的問題沒有被標記爲C++ 11,雖然在2011年提出)。你將很難用使用'std :: unique_ptr'編寫一個程序(在C++ 11中),而將它改爲'std :: auto_ptr'會導致它失敗(當然仍然在C++ 11中;順便說一句,相反很容易)。 – 2014-08-27 09:37:31

0

STEP 1 讓解決一條筆直的路這個問題:

#include <iostream> 
#include <vector> 
#include <algorithm> 

template<> struct std::less<std::auto_ptr<int>>: public std::binary_function<std::auto_ptr<int>, std::auto_ptr<int>, bool> { 
    bool operator()(const std::auto_ptr<int>& _Left, const std::auto_ptr<int>& _Right) const 
    { // apply operator< to operands 
    return *_Left < *_Right; 
    } 
}; 

int wmain() { 
    using namespace std; 

    auto_ptr<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3)); 
    vector<auto_ptr<int>> vec; 
    vec.push_back(apai3); 
    vec.push_back(apai); 
    vec.push_back(apai2); 

    for (vector<auto_ptr<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i) 
    wcout << i->get() << L'\t'; 

    vector<int> vec2; 
    vec2.push_back(3); 
    vec2.push_back(2); 
    vec2.push_back(5); 

    sort(vec2.begin(), vec2.end(), less<int>()); 

    sort(vec.begin(), vec.end(), less<auto_ptr<int>>()); 

    return 0; 
} 

在MSVCPP11錯誤文本如下: _ 錯誤1個錯誤C2558:類「的std ::自動 _ptr < Ty>':沒有拷貝構造函數可用或拷貝構造函數被聲明爲'explicit'c:\ program files(x86)\ microsoft visual studio 11.0 \ vc \ include \ xmemory0 608

結論是:我甚至不能編譯這樣的例子。爲什麼他們阻止我做一些我無法編​​譯的東西?他們的預防並不總是如此。


STEP 2

我們不能用auto_ptr作爲vector元素類型直接由於auto_ptr設計。但是我們可以按照下面介紹的方式封裝`auto_ptr'。

#include <iostream> 
#include <vector> 
#include <algorithm> 

template<typename T> class auto_ptr_my: public std::auto_ptr<T> { 
public: 
    explicit auto_ptr_my(T *ptr = 0) { 
    this->reset(ptr); 
    } 
    auto_ptr_my<T> &operator=(const auto_ptr_my<T> &right) { 
    *(static_cast<auto_ptr<T> *>(this)) = *(static_cast<auto_ptr<T> *>(const_cast<auto_ptr_my *>(&right))); 
    return *this; 
    } 
    auto_ptr_my(const auto_ptr_my<T>& right) { 
    *this = right; 
    } 
}; 

template<> struct std::less<auto_ptr_my<int>>: public std::binary_function<auto_ptr_my<int>, auto_ptr_my<int>, bool> { 
    bool operator()(const auto_ptr_my<int>& _Left, const auto_ptr_my<int>& _Right) const 
    { // apply operator< to operands 
    return *_Left < *_Right; 
    } 
}; 

int wmain() { 
    using namespace std; 

    auto_ptr_my<int> apai(new int(1)), apai2(new int(2)), apai3(new int(3)); 

    vector<auto_ptr_my<int>> vec; 
    vec.push_back(apai3); 
    vec.push_back(apai); 
    vec.push_back(apai2); 

    for (vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i) 
    wcout << **i << L'\t'; 

    sort(vec.begin(), vec.end(), less<auto_ptr_my<int>>()); 

    for (vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i) 
    wcout << **i << L'\t'; 

    return 0; 
} 

此代碼以及表示auto_ptr可以使用具有vectorsort無內存泄漏和崩潰


STEP 3 作爲KennyTM貼在下面:

return 0;語句添加以下代碼:

std::vector<auto_ptr_my<int>> vec2 = vec; 

for (vector<auto_ptr_my<int>>::const_iterator i(vec2.cbegin()) ; i != vec2.cend() ; ++i) 
    wcout << **i << L'\t'; 
wcout << std::endl; 

for (vector<auto_ptr_my<int>>::const_iterator i(vec.cbegin()) ; i != vec.cend() ; ++i) 
    wcout << **i << L'\t'; 
wcout << std::endl; 

...和得到內存泄漏!


結論 有時候,我們可以使用auto_ptr與容器不可見的崩潰,有時沒有。無論如何,這是不好的做法。 但是不要忘記auto_ptr的設計方式使得你不能直接使用STL容器和算法:反對你必須編寫一些包裝代碼。最後使用auto_ptr與STL容器是您自己的風險。例如,某些sort的實現不會在處理vector元素時導致崩潰,但其他實現將直接導致崩潰。

這個問題有學術目的。 感謝KennyTM提供的STEP 3崩潰示例!

0

從你寫的內容來看,你已經知道關於容器auto_ptr的所有知識以及它們爲什麼不安全。

因此,我假設您對auto_ptr s集裝箱的興趣純粹是以教學爲導向。我理解你在試圖構建一個有意的反例時的挫敗感:事實上,大多數標準容器的實現者已經實施瞭解決方法,以避免意外觸發auto_ptr的破壞的語義。

所以,這裏是我寫我自己恰恰是教學的例子:

class MyClass { 
    int a; 
public: 
    MyClass (int i) : a(i) { } 
    int get() const { return a; } 
}; 

int main() { 
    constexpr unsigned size = 10; 
    std::vector< std::auto_ptr<MyClass> > coap; 
    coap.resize(size); 

    for (unsigned u=0; u<size; u++) 
    coap[u] = std::auto_ptr<MyClass>(new MyClass(rand() % 50)); 

    std::sort(coap.begin(), coap.end(), 
      [](std::auto_ptr<MyClass> a, 
       std::auto_ptr<MyClass> b) { return a->get() < b->get(); }); 
} 

使用g ++ 4.9.2編譯它會導致一個可執行文件,將很好地段錯誤。

您可以通過使用類型推演重寫上面的例子更加簡潔:

std::sort(coap.begin(), coap.end(), 
      [](auto a, auto b) { return a->get() < b->get(); }); 

請注意,這個問題是不是在具體實施std::sort,這似乎是auto_ptr -safe。這是在比較lambda函數,我傳遞到std::sort,故意接受它的參數的價值,從而摧毀容器中的對象每次執行比較時。

如果您更改了lambda以便它通過引用接收其參數,如下所示,即使您正在做一些概念上錯誤的操作,大多數STL實現實際上也會正常運行。

std::sort(coap.begin(), coap.end(), 
      [](const std::auto_ptr<MyClass> & a, 
       const std::auto_ptr<MyClass> & b) { return a->get() < b->get(); }); 

祝你好運!