01 成員對象與封閉類
類里有其他對象則該對象叫成員對象;有成員對象的類叫 封閉類;
class CTyre // 輪胎類
{
public:
// 有參數構造函數
// 初始化成員變量m_radius和m_width
CTyre(int r, int w):m_radius(r),m_width(w) { }
private:
int m_radius; // 半徑
int m_width; // 寬度
};
class CEngine // 引擎類
{
// 默認會有無參數構造函數
};
class CCar // 汽車類 -- 封閉類
{
public:
// 有參數構造函數
// 對成員變量m_price初始化,對成員對象tyre初始化
CCar(int p, int tr, int tw):m_price(p),tyre(tr,tw){}
private:
int m_price; // 價格
CTyre tyre; // 成員對象
CEngine engine; // 成員對象
};
int main()
{
CCar car(10000,20,50);
return 0;
}
上例中,如果CCar
類不定義構造函數,則會使用默認的無參構造函數,那么下面的語句會編譯出錯:
CCar car;
因為編譯器不明白CCar
類中的tyre
成員對象該如何初始化。engine
成員對象的初始化則沒問題呢,因為用默認構造函數即可。
任何生成封閉類對象的語句,都要讓編譯器明白,對象中的成員對象,是如何初始化的。
具體的做法是:通過封閉類的構造函數的初始化列表。
02 封閉類構造函數和析構函數的執行順序
- 封閉類對象生成時,先執行所有成員對象的構造函數,然后才執行封閉類的構造函數。
- 成員對象的構造函數執行順序,與在封閉類定義成員對象的順序一致。
- 當封閉類的對象消忙時,只需要記住一句話:先構造的對象,后析構,由於封閉類是最后才被構造的,所以封閉類對象最先析構。
class CTyre // 輪胎類
{
public:
CTyre(){ std::cout << "CTyre 構造函數" << std::endl; }
~CTyre(){ std::cout << "CTyre 析構函數" << std::endl; }
};
class CEngine // 引擎類
{
public:
CEngine(){ std::cout << "CEngine 構造函數" << std::endl; }
~CEngine(){ std::cout << "CEngine 析構函數" << std::endl; }
};
class CCar // 汽車類 -- 3. 最后構造封閉類對象
{
public:
CCar(){ std::cout << "CCar 構造函數" << std::endl; }
~CCar(){ std::cout << "CCar 析構函數" << std::endl; }
private:
CTyre tyre; // 1. 先構構造該成員對象
CEngine engine; // 2. 接着構造該成員對象
};
int main()
{
CCar car;
return 0;
}
執行結果:
CTyre 構造函數
CEngine 構造函數
CCar 構造函數
CCar 析構函數
CEngine 析構函數
CTyre 析構函數
03 封閉類的復制構造函數
class A
{
public:
// 無參數構造函數
A() { std::cout << "A 構造函數" << std::endl; }
// 復制構造函數
A(A & a) { std::cout << "A 復制構造函數" << std::endl; }
};
class B
{
// 若沒有聲明構造函數和復制構造函數,則編譯器會默認生成構造函數和復制構造函數
A a; // 成員對象
};
int main()
{
B b1; // b1對象 和 成員對象a都會執行無參數的構造函數
B b2(b1); // b2對象 和 成員對象a都會執行復制構造函數
return 0;
}
輸出結果:
A 構造函數
A 復制構造函數