2012-05-31 45 views
14

以下是實際問題的簡化版本。代碼似乎不會調用Base::operator=(int),而是生成一個臨時對象Derived,並將其複製。爲什麼不使用基賦值運算符,因爲函數簽名似乎完全匹配?這個簡化的例子不會顯示任何不良影響,但原始代碼在析構函數中有一個副作用,導致各種破壞。爲什麼派生類不使用基類操作符=(賦值操作符)?

#include <iostream> 
using namespace std; 

class Base 
{ 
public: 
    Base() 
    { 
     cout << "Base()\n"; 
    } 

    Base(int) 
    { 
     cout << "Base(int)\n"; 
    } 

    ~Base() 
    { 
     cout << "~Base()\n"; 
    } 

    Base& operator=(int) 
    { 
     cout << "Base::operator=(int)\n"; 
     return *this; 
    } 
}; 

class Derived : public Base 
{ 
public: 
    Derived() 
    { 
     cout << "Derived()\n"; 
    } 

    explicit Derived(int n) : Base(n) 
    { 
     cout << "Derived(int)\n"; 
    } 

    ~Derived() 
    { 
     cout << "~Derived()\n"; 
    } 
}; 

class Holder 
{ 
public: 
    Holder(int n) 
    { 
     member = n; 
    } 

    Derived member; 
}; 

int main(int argc, char* argv[]) 
{ 
    cout << "Start\n"; 
    Holder obj(1); 
    cout << "Finish\n"; 

    return 0; 
} 

的輸出是:

Start 
Base() 
Derived() 
Base(int) 
Derived(int) 
~Derived() 
~Base() 
Finish 
~Derived() 
~Base() 

http://ideone.com/TAR2S

回答

19

這是一個編譯器生成的operator=方法和member function hiding之間的微妙相互作用。由於Derived類沒有聲明任何operator =成員,因此編譯器隱含地生成了一個:Derived& operator=(const Derived& source)。此運算符= 隱藏運算符=在基類中,因此無法使用。編譯器仍然可以通過使用Derived(int)構造函數創建一個臨時對象並使用隱式生成的賦值運算符複製該對象,從而完成分配。

因爲執行隱藏的功能是隱式生成的,並且不是源的一部分,所以很難發現。

通過在構造函數int上使用explicit關鍵字可以發現這個問題 - 編譯器會發出錯誤,而不是自動生成臨時對象。在原始代碼中,隱式轉換是一個很好用的功能,所以不使用explicit

的解決方法是相當簡單的,派生類可以在定義明確拉從基類:

using Base::operator=; 

http://ideone.com/6nWmx

+0

好消息!我假設'operator ='是唯一一個表現出這種行爲的人(對構造函數沒有意義,也沒有任何其他編譯器生成的方法)。 –

+0

@LuchianGrigore,我希望如此!這一個引起我們各種頭部劃傷,我不想再次遇到它。 –

+0

單參數(能)構造函數應該幾乎總是自動讓你輸入'explicit'。 –

相關問題