1. 程式人生 > >《隨筆十四》—— C++中的 “ 委託建構函式 (C++11)”

《隨筆十四》—— C++中的 “ 委託建構函式 (C++11)”

目錄

委託建構函式


委託建構函式


● 一個委託建構函式使用它所屬類的其他建構函式執行它自己的初始化過程, 在委託建構函式內,成員初始值列表只有一個唯一的入口, 就是類名本身(就是同類的其他建構函式的類名)。 類名後面的引數列表必須與類中另外一個建構函式匹配。

class Sales_data
{
	friend istream &read(istream &is, Sales_data &item);
public:
	//非委託建構函式使用對應的實參初始化成員
	Sales_data(const string &s, unsigned cnt, double price) :bookNo(s), units_sold(cnt), revenue(price*cnt) {}

	//其餘建構函式全都委託給另外一個建構函式
	Sales_data() : Sales_data("", 0, 0) {}
	Sales_data(const string &s) : Sales_data(s, 0, 0) {}

	Sales_data(istream &is) : Sales_data()
	{
		read(is, *this);
	}
private:
	//資料成員
	string bookNo;
	unsigned units_sold = 0;
	double revenue = 0.0;
};
istream &read(istream &is, Sales_data &item)
{
	double price = 0;
	is >> item.bookNo >> item.units_sold >> price;
	item.revenue = price * item.units_sold;
	return is;
}

在這個 Sales_data 類中,除了第一個建構函式外其他的都委託了它們的工作。第一個建構函式接受三個實參,使用這些實參初始化資料成員,然後結束工作。我們定義預設建構函式令其使用三個引數的建構函式完成初始化過程, 它也無須執行其他任務, 這一點從空的建構函式體能看得出來。接受一個string的建構函式同樣委託給了個三引數的版本。

 

接受 istream& 的建構函式也是委託建構函式,它委託給了預設建構函式,  預設建構函式又接著委託給三個引數建構函式的版本。當這些受委託的建構函式執行完後, 接著執行 istream&建構函式體的內容。

它的建構函式體呼叫read函式讀取給定的istream.

 

注意: 當一個建構函式委託給另一個建構函式時,受委託的建構函式的初始值列表和函式體被依次執行。 在 Sales_data 類中, 受委託的建構函式體恰好是空的。 假如函式體包含程式碼的話,將先執行這些程式碼, 然後控制權才會交還給委託者的函式體。

 

在貼一段完整的程式碼感受一下:

class Date
{
public:
	//非委託建構函式使用對應的實參初始化成員
	Date(int year, int month, int day)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "帶三個引數的建構函式被呼叫!" << endl;
	}
	//其餘建構函式全都委託給另一個建構函式
	Date() :Date(1990, 1, 1)
	{
		cout << "預設建構函式被呼叫!" << endl;
	}
	Date(int year) :Date()
	{
		cout << "帶一個建構函式被呼叫!" << endl;
	}
	void show()
	{
		cout << "分別輸出它們的值:" << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date myDate;
	myDate.show();
	cout << endl;

	Date myDate1(2018);
	myDate1.show();
	cout << endl;

	Date myDate2(2018,6,21);
	myDate2.show();
	system("pause");
	return 0;
}
輸出結果為:

帶三個引數的建構函式被呼叫!
預設建構函式被呼叫!
分別輸出它們的值:1990 1 1

帶三個引數的建構函式被呼叫!
預設建構函式被呼叫!
帶一個建構函式被呼叫!
分別輸出它們的值:1990 1 1

帶三個引數的建構函式被呼叫!
分別輸出它們的值:2018 6 21

注意: 不要遞迴委託

class A
{
private:
	int i = 5;
	string str = "初始值";
public:
	A(string ss) :A(555)   //錯誤,這裡委託給了int型引數的建構函式,形成遞迴委託,這樣就會在兩個函式中來會執行,形成死迴圈(死遞迴)的建構函式
	{
		str = ss;
	}
	A(int ii) :A("OK") //錯誤,這裡委託給了string型引數的建構函式,形成遞迴委託
	{
		//不能寫成AA(int ii):A(),i(ii)    
		//委託建構函式不能再利用初始化器初始化其他資料成員  
		i = ii;
	}
	void show() {
		cout << "i=" << i << ",str=" << str << endl;
	}
};
int main()
{
	A a(10);
	a.show();
}

 


注意 : 在使用委託建構函式時,不能再進行成員列表初始化,而只能在函式體內進行初始化其他成員變數。那麼我們在委託其他建構函式構造物件時,一些成員變數以預設值構造了,在函式體內進行初始化就不叫初始化了,只能叫重新賦值了。

class A
{
private:
    int a;
    int b;
    char c;
    char d;
public:
    A(int num0, int num1, char C) :a(num0), b(num1), c(C) {}

    A(int num0, char C) :A(num0, 0, C) {}//b預設初始化為0

    A(int num0) :A(num0, 'p') { b = 1; }//b重新賦值為1

    void getMembers()
    {
        cout << a << " " << b << " " << c << " " << d << endl;
    }
};

在委託第二個建構函式構造時,b被初始化為0,這裡我們在函式體內重新賦值為1,那麼b到底是0還是1呢?結果是1。函式體內的初始化要晚於成員列表初始化,即委託其他建構函式構造完後,在進行函式體內的賦值。

注意 : 一個建構函式想要委託另一個建構函式,被委託的建構函式應該包含最大數量的引數,初始化較多的成員變數。被委託的建構函式並不用包含所有成員變數(沒有包含的成員變數,以預設值進行初始化),然後被其他建構函式委託。