類 - 2【C++ Primer 學習筆記 - 第十二章】

普通的非 const 成員函數中,this 是一個 const 指針。
指針所保存的地址不能改變,但是可以改變其指向的對象。

但是,const 成員函數中,this 就是一個 const 類型的 const 指針。

既不能改變 this 的指針值,也不能改變其指向的對象。


如果增加 const 成員函數 display ,在 cout 中打印內容。
則:display 返回的是 const 引用,因此,display 將無法嵌入到一個長表達式中。
代碼如下:

myScreen.move(4, 0).set('#').display(cout);
myScreen.display(cout).set('#'); // 這裏 display 返回的 const 引用,無法使用 set 方法。將報錯

基於 const 的重載
爲解決上述問題,必須定義兩個 display 操作,一個是 const 的,一個是 非const 的。
const 對象只能使用 const 成員。非const 對象可以使用任一成員,非 const 成員是一個更好的匹配。


class Screen
{
public:	
	typedef string::size_type index;
	Screen& display(ostream &os)
	{
		do_display(os);
		return *this;
	}

	const Screen& display(ostream &os) const
	{
		do_display(os);
		return *this;
	}

private:
	void do_display(ostream &os) const
	{
		// 可變數據成員 access_ctr 即使在 const 成員函數中,
		// 也是可以修改的
		++access_ctr;
		os << contents;
	}

	// 可變數據成員 access_ctr 加上 mutable 關鍵字
	mutable size_t access_ctr;
	string contents;	
	index cursor;
	index height, width;
};
Screen myScreen(5,3);
const Screen blank(5,3);
// set 返回的是 非const 對象
// 因此,會匹配 display 的 非const 重載版本
myScreen.set('#').display(cout);

// blank 是 const 對象,匹配 display 的 const 重載版本
blank.display(cout);

構造函數不能聲明爲 const

class Sales_item
{
public:
	Sales_item() const;	// 報錯
};


構造函數可以定義在 類的內部,或者外部
構造函數的初始化式只在構造函數的定義,而不是聲明中指定

class Sales_item
{
public:
	Sales_item(const string&);
private:
	string isbn;
	int units_sold;
	double revenue;
};
// 顯示初始化
Sales_item::Sales_item(const string &book):
isbn(book), units_sold(0), revenue(0.0){}

// 雖然,沒有進行顯示初始化,
// 但是,實際上,構造函數在執行之前,都要初始化 isbn 成員。
// 沒有爲類成員提供初始化,則編譯器隱式地使用成員類型的默認構造函數
// 內置類型的成員,不會進行隱式初始化,
// 對於它們,進行初始化、或者在執行階段進行賦值,都同樣的效果。
// 在執行構造函數的函數體時,isbn 已經有值了,只是在執行之後,值被覆蓋
// 構造函數分2個階段執行:初始化階段、函數體內普通的計算執行階段
Sales_item::Sales_item(const string &book)
{
	isbn = book;
	units_sold = 0;
	revenue = 0.0;
}

某些成員必須在構造函數的初始化列表中進行初始化。
比如:沒有默認構造函數的類類型成員、 const 或者引用類型 的成員。
非類類型的數據成員,進行賦值,或者,使用初始化式,在結果和性能上,是等價的。
但,有2個例外(const 類型、 引用類型)

class ConstRef
{
public:
	ConstRef(int ii);
private:
	int i;
	const int ci;
	int &ri;
};
ConstRef::ConstRef(int ii)
{
	i = ii;
	ci = ii;	// Error:const 類型,無法賦值
	ri = i;	// Error:引用類型,也要在初始化列表中指定
}
ConstRef::ConstRef(int ii):i(ii), ci(ii), ri(ii){};  // 正確

成員初始化的次序
成員被初始化的次序,是跟,成員定義的次序一致的。
通常,初始化的次序,無關緊要。但也有例外:

class X
{
	int i;
	int j;
public:
	// i先被定義,再定義 j
	// 因此,以下初始化的實際過程:
	// 先用未初始化的 j,來初始化i
	// 再用 val 初始化 j	
	X(int val):j(val), i(j){};
	
	// 建議寫法如下:
	// 使用形參來初始化成員,初始化列表的順序,
	// 跟成員定義的次序一致。
	// 並且避免使用對象的數據成員,來初始化其他成員
	// X(int val):i(val), j(val){};	
};

Sale_item(const string &book, int cnt, double price):
isbn(book), units_sold(cnt), revenue(cnt * price){}
// 初始化式,可以是任意的表達式(cnt * price)

類類型的數據成員的初始化式

Sale_item():isbn(10, '9'), units_sold(0), revenue(0) {}
// isbn(10, '9') 使用了 string 的構造函數,生成一個string,由10個9組成


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章