2015-03-13 83 views
2

下面是一個簡單的例子:一個變量聲明創建了多個實例

class A { 
public: 
    A() { 
    printf("%p C1\n", this); 
    } 
    A(const char* p) { 
    printf("%p C2\n", this); 
    } 
}; 

int main(int argc, char *argv[]) { 
    A a; 
    a = "abc"; 
} 

在代碼中,雖然A聲明只有一次,也有越來越創建的A兩個實例。構造函數被調用兩次。我可以在VS 2013以及gnu C++中重現這一點。

想知道這是行爲錯誤還是規範的一部分。

+0

您可以通過定義一個'A ::運算符=(爲const char *)避免臨時' (或者更好的'A :: operator =(std :: string const&)')。 – Walter 2015-03-13 16:56:22

+0

'想知道這個行爲是否是一個錯誤'您使用了全世界數以千計的人和公司使用的兩種編譯器,並且從這些不重要的代碼中獲得了相同的結果。鑑於此,這是一個錯誤有什麼機會? – PaulMcKenzie 2015-03-13 17:05:12

+0

@MatthewMoss我在評論最後的問題真的是諷刺。該程序是一個玩具程序,g ++和Visual Studio被成千上萬的人使用。如果這是一個錯誤,它會在整個網絡上報告(再加上兩個獨立的編譯器廠商會用這樣一個非常簡單的程序產生相同的錯誤)。 – PaulMcKenzie 2015-03-13 17:38:51

回答

4

因爲你還沒有禁用自動生成的賦值運算符或拷貝構造函數,類實際上是這樣的編譯器:

class A { 
public: 
    A() { 
    printf("%p C1\n", this); 
    } 
    A(const A& rhs) { } 
    A(const char* p) { 
    printf("%p C2\n", this); 
    } 
    A& operator=(const A& rhs) { return *this; } 
}; 

所以a = "abc"被解釋爲a.operator=(A("abc"))

它需要一個const A&作爲operator=的參數,它可以構造,因爲您提供了構造函數A(const char*)

您可以通過顯式構造函數來防止意外轉換。

class A { 
public: 
    A() { 
    printf("%p C1\n", this); 
    } 
    explicit A(const char* p) { 
    printf("%p C2\n", this); 
    } 
}; 

那麼這應該無法編譯:

int main(int argc, char *argv[]) { 
    A a; 
    a = "abc"; 
} 

除非你明確構建:a = A("abc");

+0

'顯式'的好處。 – 2015-03-13 16:59:44

+0

謝謝,@ChristianHackl。除非你非常清楚你正在做什麼,否則將轉換構造函數設爲'explicit'是一個好習慣。也許甚至不是...... – 2015-03-13 17:00:55

6

它是規範的一部分。當你這樣做:

a = "abc"; 

臨時A對象被創建窗體上的RHS的​​表達式使用A(const char* p)構造。這用於爲a賦值。

如果你這樣做,而不是

A a = "abc"; 

你只看到一個構造函數調用。

+3

爲了完整性,您可以重載'operator =(const char *)'來接管賦值,提供比創建臨時對象更好的匹配。 – chris 2015-03-13 16:54:29