2013-08-18 84 views
2

我有一個非常簡單的類來處理回調。來電者要求經理進行回叫。只要Callback對象被調用者持有,它仍然有效。然而,一旦對象死亡,它的析構函數會將管理器中的指針設置爲NULL,以便在下次遇到它時知道它將被丟棄。將返回一個對象調用它的析構函數嗎?

...或者至少,這是我追求的基本想法。

class Manager{ 
public: 
    void executeOnList() { ... } 

    Callback requestCallback(Drawable * target){ 
      Drawable ** ptr = list.add(target); 
      return Callback(ptr); // <-- the point of interest 
    } 
private: 
    List list; 
}; 


class Callback{ 
    friend Manager; 
private: 
    Callback(Drawable ** targetPtr){ 
     drawablePtr = targetPtr; 
    } 
public: 
    ~Callback(){ 
     (*drawablePtr) = NULL; // <-- dtor of interest 
    } 
private: 
    Drawable ** drawablePtr; 
}; 

我的問題是,是否會Manager::requestCallback()呼叫Callback返回結構,它的調用者之前的析構函數?

如果是這樣的話,是否有任何方法可以防止這種情況發生,同時(或多或少)維持Callback功能背後的基本思想?

+0

這取決於您是否獲得[返回值優化](http://en.wikipedia.org/wiki/Return_value_optimization)。在你的例子中,我希望你會。 – juanchopanza

+0

@juanchopanza - 我已經閱讀過這篇文章,雖然看起來有點冒險,但沒有明確要求... –

+0

對此你沒有辦法做到這一點(當然,有些編譯器可以關閉它,但看起來像悲觀化)。您不應該依賴RVO *而不是*發生。 – juanchopanza

回答

3

每一個對象,它是在棧上時,自動將其超出範圍被破壞。也就是說,在概念上,臨時對象從函數返回時會超出範圍。也就是說,當返回一個拷貝對象時,編譯器可能會忽略它,在這種情況下,return語句中的臨時對象似乎根本不存在。但是,如果複製刪除發生或不是一個優化,並不能保證。

只取決於析構函數似乎不適用於你的情況。然而,可能的工作是區分傳遞的臨時對象和被保持的對象(被命名或指向)。基本思想是考慮指向指向一個對象擁有的資源,並僅在實際所有者被銷燬時(類似於std::unique_ptr<T>)或所有所有者被銷燬(類似於std::shared_ptr<T>)才重置它。假設你可以使用r值引用,你可以得到兩個表單都是正確的,否則你只能獲得共享所有權。

這裏的單一所有權邏輯如何能看起來像一個簡要介紹:

class Callback{ 
    friend Manager; 
private: 
    Callback(Drawable ** targetPtr){ 
     drawablePtr = targetPtr; 
    } 
public: 
    Callback(Callback&& other): 
     drawablePtr(other.drawablePtr) { 
     other.drawablePtr = 0; 
    } 
    ~Callback(){ 
     if (drawablePtr) { 
      (*drawablePtr) = 0; 
     } 
    } 
private: 
    Drawable ** drawablePtr; 
}; 

如果您不能使用R-值語義你仍然可以使用相同的邏輯,實際上,卻是有在創建副本時,「資源」被命名對象意外盜取的風險。使用移動構造函數可以避免這種風險。

+0

哦,這很聰明。我不得不在它背後的邏輯之前重新閱讀複製構造函數,這會很好地工作,謝謝! –

+0

@Clairvoire:請注意,「複製構造函數」實際上是一個_move構造函數_。它也可以使用複製構造函數,雖然有些不尋常和危險的語義,這就是爲什麼我使用移動構造函數來竊取資源是正常的。 –

2

您可以添加一個移動構造函數(C++ 11)或拷貝析構函數:

class Callback{ 
    friend Manager; 
private: 
    explicit Callback(Drawable** targetPtr) : drawablePtr(targetPtr) {} 
    // C++11 
    Callback(Callback&& rhs) : drawablePtr(rhs.drawablePtr) { rhs.drawablePtr = NULL; } 
    // else 
    Callback(Callback& rhs) : drawablePtr(rhs.drawablePtr) { rhs.drawablePtr = NULL; } 
public: 
    ~Callback(){ 
     if (drawablePtr) { (*drawablePtr) = NULL; } 
    } 
private: 
    Drawable ** drawablePtr; 
}; 
+0

我完全忘記檢查指針指針的有效性,很好的理解!我正在使用VS2010,現在顯示C++ 11已經過時了。也許我會很幸運,無論如何它會移動構造器 –

0

好吧,臨時回調對象將在requestCallback返回後被銷燬,這將導致Drawable *設置爲NULL,並且複製的Callback對象也不起作用。

移動確實有效,但它不是唯一的解決方案。實際上,這就像C++ 98中的auto-ptr一樣,只是將資源從一個偷到另一個。

相關問題