C++在空類下默默編寫並調用了哪些函數
什麼時候empty class不再是個空類呢?當C++處理之後。如果沒聲明,編譯器就會給它聲明一個copy構造函數、一個copy assignment操作符和一個析構函數。此外如果沒有聲明任何構造函數,編譯器也會給你聲明一個default構造函數。所有這些函數都是public且inline。因此,如果你寫下:
class Empty
{
};
這就好像你寫下這樣的代碼:
class Empty
{
public:
Empty() {} // default構造函數
Empty(const Empty& rhs) {} // copy構造函數
~Empty() {} // 析構函數
Empty& operator=(const Empty& rhs) {} // copy assignment操作符
};
惟有當這些函數被需要,它們纔會被編譯器創建出來。程序中需要它們是很平常的事。下面代碼造成上述每一個函數被編譯器產出:
Empty e1; // default構造函數
// 析構函數
Empty e2(e1); // copy構造函數
e2 = e1; // copy assignment操作符
• default構造函數和析構函數
default構造函數和析構函數主要是給編譯器一個地方用來放置“藏身幕後”的代碼,像是調用base classes和non-static成員變量的構造函數和析構函數。注意,編譯器產生的析構函數是個non-virtual,除非這個class的base class自身聲明有virtual析構函數
• copy構造函數和copy assignment操作符
copy構造函數和copy assignment操作符,編譯器創建的版本只是單純地將來源對象的每一個non-static成員變量拷貝到目標對象。考慮一個NamedObject template,它允許你將一個個名稱和類型爲T的對象產生關聯:
template <typename T>
class NamedObject
{
public:
NamedObject(const char* name, const T& value);
NamedObject(const std::string& name, const T& value);
...
private:
std::string nameValue;
T objectValue;
};
由於其中聲明瞭一個構造函數,編譯器於是不再爲它創建default構造函數。這很重要,意味如果你用心設計一個class,其構造函數要求實參,你就無須擔心編譯器會毫無掛慮地爲你添加一個無實參構造函數(即default構造函數)而遮蓋掉你的版本。
NamedObject既沒有聲明copy構造函數,也沒有聲明copy assignment操作符,所以編譯器會爲它創建那些函數。現在,看看copy構造函數的用法:
NamedObject<int> no1("Smallest Prime Number", 2);
NamedObject<int> no2(no1); // 調用copy構造函數
編譯器生成的copy構造函數必須以no1.nameValue和vo1.objectValue爲初值設定no2.nameValue和no2.objectValue。兩者之中,nameValue的類型是string,而標準string有個copy構造函數,所以no2.nameValue的初始化方式是調用string的copy構造函數並以no1.nameValue爲實參。另一個成員NamedObject::objectValue的類型是int,那是個內置類型,所以no2.objectValue會以“拷貝no1.objectValue內的每一個bits”來完成初始化。
編譯器爲NamedObject所生的copy assignment操作符,其行爲基本上與copy構造函數如出一轍,但一般而言只有當代碼合法且有適當機會證明它有意義。萬一兩個條件有一個不符合,編譯器會拒絕爲class生出operator=。
• 總結
編譯器可以暗自爲class創建default構造函數、copy構造函數、copy assignment操作符,以及析構函數。