2013-11-05 47 views
2

我在閱讀中的item28時遇到了一個問題更有效的C++。在這個項目中,作者向我們表明我們可以使用SmartPtr中的成員模板,以使SmartPtr<Cassette>可以轉換爲SmartPtr<MusicProduct>
以下代碼與本書中的代碼不同,但效果相同。這是Item28中「更有效的C++」中的錯誤嗎?

#include <iostream> 

class Base {}; 
class Derived : public Base {}; 

template<typename T> 
class smart { 
public: 
    smart(T* ptr) 
     : ptr(ptr) 
    {} 

    template<typename U> 
    operator smart<U>() 
    { 
     return smart<U>(ptr); 
    } 

    ~smart() 
    { 
     delete ptr; 
    } 
private: 
    T* ptr; 
}; 

void test(const smart<Base>&) {} 

int main() 
{ 
    smart<Derived> sd(new Derived); 
    test(sd); 
    return 0; 
} 

確實可以在沒有編譯錯誤的情況下進行編譯。但是當我運行可執行文件時,我得到了一個核心轉儲。我認爲這是因爲轉換運算符的成員函數會產生一個臨時智能,它具有指向sd中相同ptr的指針(它的類型爲smart<Derived>)。所以delete指令運行兩次。更重要的是,在通話測試後,我們再也不能使用sd,因爲sd中的ptr已經被刪除。
現在我的問題是:

  • 我的想法是對的嗎?或者我的代碼與書中的原始代碼不一樣?
  • 如果我的想法是對的,有沒有辦法做到這一點?

非常感謝您的幫助。

+1

您的代碼與本書中的原始代碼不同。本書中代碼的意圖是顯示智能指針即使是模板化的,它們可以繞過「Base」和「Derived」而成爲不同的類型,儘管它們在相同的類層次結構中。你的實現演示了這種類型轉換機制,但是在調用'delete'之前沒有引用計數檢查,它與智能指針不同,這使它更接近智能ptr正在做的事情:'〜smart(){if(reference_count_ <1)delete ptr ;}'。通常情況下,一些互斥鎖可以保護refcount檢查和'delete'之間的計數器。 –

+0

哦,我明白你的意思了。原始代碼沒有顯示構造函數和析構函數的實現。我在測試代碼中錯誤地使用了'std :: auto_ptr'的ctor和dtor,它沒有引用計數,因此在這種情況下不適用。非常感謝。 – particle128

回答

0

是的,你已經相當準確地描述了你的代碼的問題。

至於如何使它工作:就像平常一樣,當你遇到來自淺拷貝的問題時:做一個深層拷貝。也就是說,不是僅僅創建另一個指向相同數據的指針,而是需要克隆數據,並讓第二個對象指向數據的克隆而不是原始數據。

或者,使用引用計數的指針,並在執行副本時增加引用計數,並在副本銷燬時將其減少。當計數達到零(而不是之前)刪除指針數據。

一般來說:避免做這一切。假設您使用的是相對最新的編譯器,標準庫應該已經包含一個shared_ptr和一個unique_ptr,它可以處理您智能指針需要的lot

+0

感謝您的回答。如果我使用'shared_ptr',它是否可以避免在'shared_ptr '傳遞給'test(const shared_ptr &)'後破壞原始指針?如果是這樣,它是如何處理這個問題的? – particle128

+0

@ particle128:'shared_ptr'使用引用計數,就像前面在答案中所描述的那樣。我不記得它是否提供了這種轉換 - 我必須檢查確定。 –

+0

我明白了。謝謝〜 – particle128

0

您的解釋是正確的,轉換運算符將創建一個不同的對象,該對象持有指向同一個基礎對象的指針。一旦超出範圍,它將被銷燬,並且它將依次撥打delete

不知道我明白最後一個問題,如果你問的是否這可以是有用的,它可以是有用的,如果正確實施。例如,如果使用std::shared_ptr而不是原始指針並手動分配/刪除內存,那麼它將工作得很好。在其他情況下,甚至可能沒有動態分配的對象......這只是一個工具,在有意義的地方使用它。