構造函數 ,是一種特殊的方法 。主要用來在創建對象時初始化對象, 即為對象成員變量賦初始值,總與new運算符一起使用在創建對象的語句中 。特別的一個類可以有多個構造函數 ,可根據其參數個數的不同或參數類型的不同來區分它們 即構造函數的重載。(摘自百度百科 構造函數 )。
一、最基本的構造函數
1 class Base2 {3 public:4 Base(int var) : m_Var(var)5 {6 }7 PRivate:8 int m_Var;9 };以上構造函數的執行過程:
1)傳參 2)給類數據成員開辟空間 3)執行冒號語法給數據成員初始化 4)執行構造函數括號里面的內容
這里需要說明的是:冒號語法后面的內容相當于int a = 10;(初始化),而構造函數括號里面則是相當于是int a; a = 10;(賦初值)
二、拷貝構造函數
1 class Base 2 { 3 public: 4 Base(int var) : m_Var(var) 5 { 6 } 7 //拷貝構造函數 8 Base(Base &ref) : m_Var(ref.m_Var) 9 {10 }11 private:12 int m_Var;13 };為什么拷貝構造函數的參數只能用引用呢?
這就要從拷貝構造函數式數碼時候觸發開始說起了,以下幾種情況都會自動調用拷貝構造函數:
1)用一個已有的對象初始化一個新對象的時候
2)將一個對象以值傳遞的方式傳給形參的時候
3)函數返回一個對象的時候
所以當一個對象以傳遞值的方式傳一個函數的時候,拷貝構造函數自動的被調用來生成函數中的對象。如果一個對象是被傳入自己的拷貝構造函數,它的拷貝構造函數將會被調用來拷貝這個對象這樣復制才可以傳入它自己的拷貝構造函數,這會導致無限循環直至棧溢出除了當對象傳入函數的時候被隱式調用以外,拷貝構造函數在對象被函數返回的時候也同樣的被調用。(摘自百度百科 拷貝構造函數 )。
拷貝構造函數,一般不需要自己編寫,系統默認的拷貝構造函數就能抗住了,但是有些情況需要在構造的時候開辟空間,這時候就需要拷貝構造函數了,如下代碼是摘自 林銳 博士的 高質量C++編程指南 一文。
1 class String 2 { 3 public: 4 String(const char *str = NULL); // 普通構造函數 5 String(const String &other); // 拷貝構造函數 6 ~ String(void); // 析構函數 7 private: 8 char *m_data; // 用于保存字符串 9 };10 // String 的析構函數11 String::~String(void) 12 {13 delete [] m_data;14 // 由于m_data 是內部數據類型,也可以寫成 delete m_data;15 }16 17 // String 的普通構造函數18 String::String(const char *str) 19 {20 if(str==NULL)21 {22 m_data = new char[1]; // 若能加 NULL 判斷則更好23 *m_data = '/0';24 }25 else26 {27 int length = strlen(str);28 m_data = new char[length+1]; // 若能加 NULL 判斷則更好29 strcpy(m_data, str);30 }31 }32 // 拷貝構造函數33 String::String(const String &other) 34 {35 int length = strlen(other.m_data);36 m_data = new char[length+1]; // 若能加 NULL 判斷則更好37 strcpy(m_data, other.m_data);38 } 三、普通派生類構造函數的寫法
定義派生類對象的時候,會按如下步驟執行構造操作:
1)傳參 2)根據繼承時的聲明順序構造基類 3)給類數據成員開辟空間 4)執行冒號語法后面的語句 5)執行構造函數函數體語句
1 class Base 2 { 3 public: 4 Base(int b) : m_b(b) 5 { 6 } 7 private: 8 int m_b; 9 };10 11 class Derived : public Base12 {13 public:14 //普通派生類構造函數的寫法15 Derived(int b, int d) : Base(b), m_d(d)16 {17 }18 private:19 int m_d;20 };再寫一個多繼承的示例:
1 class Base1 2 { 3 public: 4 Base1(int b1) : m_b1(b1) 5 { 6 } 7 private: 8 int m_b1; 9 };10 11 class Base212 {13 public:14 Base2(int b2) : m_b2(b2)15 {16 }17 private:18 int m_b2;19 };20 21 class Derived : public Base1, public Base222 {23 public:24 Derived(int b1, int b2, int d) : Base1(b1), Base2(b2), m_d(d)25 { //注意冒號語法后面的順序無所謂,創造基類是按照上面的繼承聲明順序來進行的...26 }27 private:28 int m_d;29 };四、含有虛繼承的派生類構造函數的寫法
為何要用到虛繼承?
虛繼承主要是針對多繼承時,出現二義性問題而提出的。比如,如下代碼就需要用到虛繼承,否則的話Derived類繼承時,Base類就會不明確。
虛繼承構造函數的執行按照如下步驟:
1)傳參 2)創建基類,注意這時候需要顯示創建所有“有參構造函數”的基類,包括直接基類,間接基類。 3)給類數據成員開辟空間 4)執行冒號語法 5)執行構造函數函數體
注:你可能會疑惑,如下代碼不是將Base間接基類創建了3次嗎?其實不是這樣的,編譯器是這樣處理的,當最遠的派生類Derived創建了基類Base之后,其直接基類創建Base類的語句將會被忽略掉。
1 class Base 2 { 3 public: 4 Base(int b) : m_b(b) 5 { 6 } 7 private: 8 int m_b; 9 };10 11 class Base1 : virtual public Base12 {13 public:14 Base1(int b, int b1) : Base(b), m_b1(b1)15 {16 }17 private:18 int m_b1;19 };20 21 class Base2 : virtual public Base22 {23 public:24 Base2(int b, int b2) : Base(b), m_b2(b2)25 {26 }27 private:28 int m_b2;29 };30 //虛繼承,避免二義性31 class Derived : public Base1, public Base232 {33 public:34 Derived(int b, int b1, int b2, int d) : Base(b), Base1(b, b1), Base2(b, b2), m_d(d)35 { //注意冒號語法后面的順序無所謂,創造基類是按照上面的繼承聲明順序來進行的...36 }37 private:38 int m_d;39 };五、關于虛析構
虛析構一般伴隨著多態而產生,多態主要方式就是用基類的指針或引用指向或引用派生類,而形成多態。
但是這樣就會存在一個問題,當我們析構的時候,由于是基類的指針,就會調用的是基類的構造函數,從而造成派生內存溢出。為了解決這個問題,引入了虛析構的概念。將基類的構造函數聲明為虛,從而使其在調用析構函數的時候能夠準確的調用派生類的析構函數。
如下代碼必須用到虛析構才能準確的析構派生類,并釋放其占有內存。
1 class Base 2 { 3 public: 4 Base(int b) : m_b(b) 5 { 6 } 7 //虛析構,使基類指針能準確的釋放所指向的派生類里面的內容 8 virtual ~Base() 9 {10 }11 private:12 int m_b;13 };14 15 class Derived : public Base16 {17 public:18 Derived(int b, char *pStr) : Base(b)19 { 20 m_pStr = new char[strlen(pStr)+1];21 strcpy(m_pStr,pStr);22 }23 ~Derived()24 {25 delete m_pStr;26 m_pStr = NULL;27 }28 private:29 char *m_pStr;30 };31 32 int main(void)33 {34 char *pStr = "abcdefg";35 Base *b = new Derived(1,pStr);36 delete b;37 38 return 0;39 }
新聞熱點
疑難解答
圖片精選