2012-02-07 160 views
31

考慮類層次結構,其中A是基類,B源自A爲什麼隱式拷貝構造函數調用基類拷貝構造函數並且定義的拷貝構造函數沒有?

如果在B中未定義複製構造函數,則編譯器將合成一個。當被調用時,這個拷貝構造函數將調用基類拷貝構造函數(即使合成的,如果用戶沒有提供)。

#include <iostream> 

class A { 
    int a; 
public: 
    A() { 
     std::cout << "A::Default constructor" << std::endl; 
    } 

    A(const A& rhs) { 
     std::cout << "A::Copy constructor" << std::endl; 
    } 
}; 

class B : public A { 
    int b; 
public: 
    B() { 
     std::cout << "B::Default constructor" << std::endl; 
    } 
}; 

int main(int argc, const char *argv[]) 
{ 
    std::cout << "Creating B" << std::endl; 
    B b1; 
    std::cout << "Creating B by copy" << std::endl; 
    B b2(b1); 
    return 0; 
} 

輸出:

Creating B 
A::Default constructor 
B::Default constructor 
Creating B by copy 
A::Copy constructor 

如果用戶定義B它自己的拷貝構造函數被調用時,這個拷貝構造函數會調用基類的默認構造函數,除非到呼叫基類複製構造函數是明確存在的(例如,在初始化列表中)。

#include <iostream> 

class A { 
    int a; 
public: 
    A() { 
     std::cout << "A::Default constructor" << std::endl; 
    } 

    A(const A& rhs) { 
     std::cout << "A::Copy constructor" << std::endl; 
    } 
}; 

class B : public A { 
    int b; 
public: 
    B() { 
     std::cout << "B::Default constructor" << std::endl; 
    } 
    B(const B& rhs) { 
     std::cout << "B::Copy constructor" << std::endl; 
    } 
}; 

int main(int argc, const char *argv[]) 
{ 
    std::cout << "Creating B" << std::endl; 
    B b1; 
    std::cout << "Creating B by copy" << std::endl; 
    B b2(b1); 
    return 0; 
} 

輸出:

Creating B 
A::Default constructor 
B::Default constructor 
Creating B by copy 
A::Default constructor 
B::Copy constructor 

我的問題是,爲什麼沒有用戶定義的拷貝構造函數調用基類的拷貝構造函數的默認行爲?

+2

如果是這樣的默認情況下,你會如何指定你不希望發生這種情況? – PlasmaHH 2012-02-07 14:48:47

+0

@PlasmaHH'ParentClass()'在初始化程序列表中。我相信這仍然會很不一致和令人困惑。 – 2012-02-07 14:50:14

+0

@MarkB:的確,我希望他在思考時得出同樣的結論...... – PlasmaHH 2012-02-07 14:52:27

回答

7

這就是隱式拷貝構造函數的定義方式(調用默認值沒有意義)。只要你定義了任何構造函數(複製或其他),它的正常自動行爲就是調用默認的父構造函數,所以對於一個特定的用戶定義的構造函數改變它是不一致的。

1

簡單的(可能是trite)答案是因爲你沒有告訴它。既然你正在編寫派生的拷貝構造函數,你完全可以控制它的行爲。如果未指定對基部的調用,並且編譯器會生成通過調用基類默認構造函數來初始化基類的代碼。

8

所有基礎子構造函數調用父級默認構造函數。這是標準的定義。正如您所指出的,如果你想基B類叫A的拷貝構造函數,你必須明確地提出要求

#include <iostream> 

class A { 
int a; 
public: 
A() { 
    std::cout << "A::Default constructor" << std::endl; 
} 

A(const A& rhs) { 
    std::cout << "A::Copy constructor" << std::endl; 
} 
}; 

class B : public A { 
int b; 
public: 
B() { 
    std::cout << "B::Default constructor" << std::endl; 
} 
B(const B& rhs):A(rhs) { 
    std::cout << "B::Copy constructor" << std::endl; 
} 
}; 

int main(int argc, const char *argv[]) 
{ 
std::cout << "Creating B" << std::endl; 
B b1; 
std::cout << "Creating B by copy" << std::endl; 
B b2(b1); 
return 0; 
} 

這是因爲編譯器無法知道每個不同的構造,其母公司constuctor應被調用,因此我們有默認構造函數對於所有其他的你必須明確地聲明它們。

+5

'所有基類子構造函數調用父類的默認構造函數.' ...除了隱式拷貝構造函數:)! – 2012-02-07 17:14:40

+0

隱式拷貝構造函數是編譯器提供的最原始操作的明確名稱,即源的按位副本到目標的拷貝。 IMO只是一個奇特的名字。 – shakthi 2017-05-19 19:33:31