0

臨時死引用的與-O3-O2編譯也許當以下最低十歲上下的程序段錯誤,但-O0執行罰款(與鏗鏘4.0):檢測或避免在編譯時

#include <iostream> 

class A { 
public: 
    virtual void me() const { std::cerr << "hi!\n"; } 
}; 

class B { 
public: 
    B(const A& a_) : a(a_) {} 
    virtual void me() const { a.me(); } 

private: 
    const A& a; 
}; 

class C { 
public: 
    C(const B& b_) : b(b_) {} 
    void me() const { b.me(); } 

public: 
    const B& b; 
}; 

int main() { 
    C c = C(A()); 
    c.me(); 
} 

的原因是該c.b被初始化到從一個臨時A構造B類的臨時對象的引用。構造c.C()退出後,臨時B走了,但對它的引用仍然c.b

我可以採用什麼好的做法,以避免出現這種情況,因爲我無法控制的BA執行?是否有靜態分析儀能夠檢測到這種情況? (我的scan-build版本沒有發現問題。)

相關:Detect dangling references to temporary

+0

好,U現在,右值引用。它們只綁定到臨時對象。我想也許這可以作爲一個只有左值的ref-wrapper抽象出來。 –

+0

通常,不要聲明類型引用的成員變量。改用指針。如果你在構造函數中得到一個指針,結果代碼將會很明顯。 – rodrigo

+0

@rodrigo:這聽起來很有趣。我想這也可以工作,如果我只聲明構造函數參數作爲指針,然後初始化引用?我寧願不重寫所有使用引用的代碼... – krlmlr

回答

1

我個人不喜歡有成員變量的引用。它們不能被複制或移動,並且該類的用戶必須確保該對象超出參考本身。一個異常可能是一個內部輔助類,它被設計成只用作臨時對象,比如函子。

更換一個指針引用應該是簡單的,然後如果你也是在構造函數中使用指針:

class C { 
public: 
    C(const A *a_) : a(a_) {} 
private: 
    const A *a; 
}; 

...等等。如果類是非常大的,你覺得懶惰,不想更改成員,你可以改變的構造:

class C { 
public: 
    C(const A *a_) : a(*a_) {} 
private: 
    const A &a; 
}; 

爲了濫用這個類,作爲OP說,你必須寫類似:

C c = C(&A()); 

然後錯誤應該是顯而易見的:採用指針來臨時是一個非常糟糕的主意! PS:我會爲你的構造函數添加一個explicit,只是爲了避免它參與自動轉換,我認爲這是自己的問題的一部分。

+0

謝謝。我不能改變'B'或'A'類,如果你的意思是加上'explicit'。 – krlmlr

+0

@krimir不,我的意思是增加一個字面的'explicit C(const A * a_)',這樣構造函數就不會作爲類型轉換的一部分被隱式調用。意外的調用你的構造函數 – rodrigo

2

我就從BC獲得單獨的類(甚至可能使用模板類)。

這些類將含有變成ab指東西的非基準部件。

我然後實現這些派生類所需的拷貝構造函數/賦值運算符,以防止懸空引用。

(然後,我會與BC的作者進行強有力的對話)。

+0

這是一個現有的代碼庫,有什麼方法可以找到這種反模式的所有用途? – krlmlr