編譯器合成缺省(無參)的構造函數
1.先考慮一個問題爲什麼c++中有構造函數?
這個可以從語言設計的角度來看這個問題。體現一個實例化的對象生命週期的完整性,一個對象在初始化的時候,讓使用者有機會做些額外的初始化操作。同樣,一個對象是消亡的時候,也要使用者有機會去釋放資源。舉個例子:喫飯前先洗手(構造函數),喫完飯在擦嘴(析構函數),這是個好習慣。但是你不洗手,不擦嘴,也沒關係,只是這不是個好習慣而已(偶爾造成細菌感染,程序異常)。
2.編譯器何時合成缺省構造函數?
好習慣的養成是一個持續的過程,所以有時候編譯器會偷偷的幫你洗手擦嘴。
A.間接的含有構造函數,比如類成員變量含有構造函數,或者是繼承的父類有構造函數。
B.直接或者間接的含有virtual function,比如自己含有virtual function或者是類成員變量,或者是繼承的父類含有。
C.出現虛繼承的現象。
3.那麼構造函數的兄弟拷貝構造?
拷貝構造和缺省的構造也是同樣的A,B,C情況下,編譯器會合成默認的拷貝構造。當然編譯器生成的拷貝構造就是淺拷貝。
4.那麼析構函數呢?
編譯器不關心你有沒有寫析構函數,編譯器都會主動生成析構函數。在析構的時候,編譯會首先調用你寫的析構函數,然後在調用編譯器的析構函數,這個過程對於程序員來說是透明的。從反彙編的角度看,編譯器會生成一個析構函數,但是這個析構函數沒有實質作用,也沒有生成彙編代碼
// 沒有析構函數的類
class NoDctorClass
{
public:
int m_a;
};
// 測試編譯器合成析構函數
void test_compiler_geneator_def_dctor()
{
NoDctorClass no_dctor_class;
// 調用析構函數
no_dctor_class.~NoDctorClass();
no_dctor_class.m_a = 0;
}
// 轉到反彙編的代碼
// NoDctorClass no_dctor_class;
// 調用析構函數,這裏沒有生成任何的反彙編代碼
// no_dctor_class.~NoDctorClass();
// no_dctor_class.m_a = 0;
012AA9FE mov dword ptr [no_dctor_class],0
5.那麼編譯器爲什麼幫你合成(拷貝)構造函數?
簡單一句話,編譯器需要插入一些額外的初始化代碼,來完成一些語言特性。
A.間接的含有缺省構造函數,這時候編譯器發現你自己沒有寫構造函數,但是你又間接含有構造函數。編譯器這時候有兩種做法,一種是心想算了吧我幫你偷偷的生成一個吧,還有一種做法就是編譯器報語法錯,讓程序員自己解決。所以編譯器廠商一商量覺得還是對程序員友好一點,用第一種做法吧。反過來想,如果一門開發語言對程序員不友好,可以說是它的生存期將會非常短。
B.直接或者間接的含有virtual function,因爲含有virtual function,爲了支持多態的特性,那麼每個實例對象都會生成指向虛函數表的指針。但是這個指針在什麼時候初始化呢?自然是(拷貝)構造函數裏面。但是你自己沒有寫,編譯器只好累一點生成(拷貝)構造函數。
C.虛繼承情況,爲了避免子類中含有多餘的成員變量,對象在實例化的時候會生成指向虛基類表的指針。自然就會聯想到在(拷貝)構造函數的時候生成,同樣你自己沒有寫,編譯器管家來替你寫。
6.如何驗證編譯器合成了(拷貝)構造函數?
1.在你的菜單欄找到如下控制檯
2.cd到編譯生成目錄下,找到你生成的obj文件,比如我生成main.obj。
3.執行 dumpbin.exe /all main.obj >> main.txt,將文件格式翻譯爲COFF。
4.查看main.txt 找到,如果所示找到1所對應的函數,下面的2就是編譯器準備插入的代碼,可以看到插入了MatrixA的構造函數。同理,可以驗證其他的情況。
7.知道編譯器在何時合成構造函數那麼又怎麼樣?
1.知道編譯器揹着我們做了那些小動作,讓我們可以更加了解這門語言背後實現的細節,做到胸中自有丘壑。
2.儘量不要依賴編譯器的操作。
3.要站在編譯器的角度去看問題。
8.總結:
構造函數和拷貝構造是你的左膀右臂,建議還是得要好好的利用。儘管有時候你不承認,但是編譯器這個管家還是會偷偷摸摸的給你裝個左膀右臂。如果說構造函數是你的左膀右臂,那麼析構函數就是你的一把強有力的武器用於保護自己,在對象生命結束之後能夠確保資源的正常釋放。
9.代碼示例:
/****************************************************************************
**
** Copyright (C) 2019 [email protected]
** All rights reserved.
**
****************************************************************************/
/*
測試編譯器在何種情況會合成默認的構造函數
同理可證:在何種情況下編譯器會合成默認的拷貝構造函數
*/
#ifndef default_constrcuctor_h
#define default_constrcuctor_h
#include <iostream>
using std::cout;
using std::endl;
namespace defualt_constructor
{
// 含有缺省的構造函數成員類對象
// #define has_default_ctor_member 1
// 繼承含有缺省的構造函數
// #define has_inherit_ctor_base 1
// 含有虛函數成員函數
// #define has_virtual_function 1
// 函數virtual函數的成員類對象
// #define has_virtual_func_member 1
// 父類含有虛函數
// #define has_inherit_virtual_func_base 1
// MatrixC, MatrixB 虛繼承MatrixD
// #define has_virtual_inherit_base 1
// 類成員變量含有copy ctor
// #define has_default_copy_ctor_member 1
// 父類含有拷貝構造函數
#define has_inherit_copy_ctor_base 1
// 含有虛函數成員函數
// #define has_virtual_function_copy_ctor 1
// 類對象成員含有虛函數
// #define has_virtual_function_copy_ctor_member 1
// 父類函數虛函數
// #define has_inherit_virtual_function_copy_ctor_base 1
// 虛繼承copy ctor
// #define has_virtual_inherit_function_copy_ctor_base 1
class MatrixD
{
};
#ifdef has_virtual_inherit_base
class MatrixC : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixC : virtual public MatrixD
#else
class MatrixC
#endif // has_virtual_inherit_base
{
public:
#ifdef has_inherit_ctor_base
MatrixC() { cout << "MatrixC" << endl; }
#elif has_inherit_copy_ctor_base
MatrixC() {}
MatrixC(const MatrixC &rhs) { cout << "MatrixC copy ctor" << endl; }
#elif has_inherit_virtual_function_copy_ctor_base
virtual void VirFun() {}
#elif has_inherit_virtual_func_base
virtual void VirFun() {}
#endif // has_virtual_inherit_base
};
#ifdef has_virtual_inherit_base
class MatrixB : virtual public MatrixD
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixB : virtual public MatrixD
#else
class MatrixB
#endif // has_virtual_inherit_base
{
public:
#ifdef has_default_ctor_member
MatrixB() { cout << "MatrixB" << endl; }
#elif has_virtual_func_member
virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#elif has_default_copy_ctor_member
MatrixB() {}
MatrixB(const MatrixB &rhs) { cout << "copy ctor MatrixB" << endl; }
#elif has_virtual_function_copy_ctor_member
virtual void VirMatrixB() { cout << "virtual MatrixB" << endl; }
#endif // has_default_ctor_member
int m_high;
int m_width;
};
#ifdef has_default_ctor_member
class MatrixA
#elif has_virtual_function
class MatrixA
#elif has_inherit_ctor_base
class MatrixA : public MatrixC
#elif has_inherit_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_func_member
class MatrixA
#elif has_virtual_inherit_base
class MatrixA : public MatrixB, public MatrixC
#elif has_inherit_virtual_func_base
class MatrixA: public MatrixC
#elif has_inherit_virtual_function_copy_ctor_base
class MatrixA : public MatrixC
#elif has_virtual_inherit_function_copy_ctor_base
class MatrixA : public MatrixB, public MatrixC
#else
class MatrixA
#endif // has_default_ctor_member
{
public:
int m_age;
int m_score;
#ifdef has_default_ctor_member
MatrixB matrixB;
#elif has_virtual_function
virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_func_member
MatrixB matrixB;
#elif has_default_copy_ctor_member
MatrixA() {}
MatrixB matrixB;
#elif has_inherit_copy_ctor_base
MatrixA() {}
#elif has_virtual_function_copy_ctor
virtual void VirFunc() { cout << "virtual function" << endl; }
#elif has_virtual_function_copy_ctor_member
MatrixB matrixB;
#endif // has_default_ctor_member
};
void test_compiler_generator_def_ctor()
{
//用dumpbin把.obj文件內容導出成可查看文件my.txt,
// 這個my.txt格式,一般被認爲是COFF:通用對象文件格式(Common Object File Format);
MatrixA matrix;
matrix.m_age = 0;
// 編譯器會在哪些情況下合成默認的構造函數?
// 1.包含一個類成員變量,此成員變量含有默認的缺省構造函數。此時編譯器就會
// 生成默認的構造函數。在這個合成的構造函數中插入代碼調用成員變量的構造函數
// 2.繼承的父類含有缺省的構造函數,此時編譯器也會構造默認的構造函數,在子類合成的構造
// 函數中插入代碼,調用父類的缺省構造
// 3.包含虛函數。不管是子類,父類,還是包含的成員類對象(不包含任何構造函數),只要
// 包含了virtual function,編譯器都會合成默認的構造函數
// 4.含有虛繼承現象,grandfather, parent(虛繼承grand),child再繼承,
// 爲了在構造函數中生成vbtable,虛基類表
// 同樣的道理,copy constrcutor也和constructor也是在同樣的情況下,編譯器會合成默認
// 的構造函數
// A.含有默認的構造函數
// 1.父類有默認的構造函數
// 2.包含類對象的成員變量含有默認構造函數
// B.虛函數
// 1.不管是自己包含虛函數,還是類成員變量有虛函數,還是父類中虛函數
// C.虛繼承
// 1.編譯器爲了在插入vbtable 虛基類表
}
void test_compiler_generator_def_copy_ctor()
{
MatrixA ma;
MatrixA mb = ma;
// 編譯器合成默認copy ctor時機和ctor是一樣的
}
}
#endif // default_constrctor_h