1. 程式人生 > >17 More Effective C++—條款25(建構函式與非成員函式的虛化)

17 More Effective C++—條款25(建構函式與非成員函式的虛化)

1 建構函式的虛化

1 構造虛擬函式

建構函式與類的名稱相同,因此,此處所指的虛化,並非讓建構函式變成虛擬函式,而是讓其具有類似虛擬函式的行為:

當條件不同時,不同的物件會被創建出來。

下面的程式碼展示了“虛建構函式”的應用。

class Base {
}
class Derive1 : public Base {
}
class Derive2: public Base {
}

Base* constructObject(std::istream& inStream) {
	Base* obj;
	if (/*condition0*/) {
		// create Base's object
	} else if (/*condition1*/) {
		// create Derive1's object;
	} else {
		// create Derive2's object
	}
	return obj;
}

2 “虛複製建構函式”

如下程式碼所示。C++11中,虛擬函式允許返回型別不同,因此,Base, Derive1, Derive2三個型別的clone可以看作時override。這樣,每次呼叫clone函式,可以根據相應的物件,來進行復制。

class Base {
	public:
		virtual Base* clone() const;
}
class Derive1 : public Base {
	public:
		virtual Derive1* clone() const;
}
class Derive2 : public Base {
	public:
		virtual Derive2* clone() const;
}
void func()
{
	std::vector<Base*> objList, targetList;
	for (int idx = 0; idx < objList.size(); ++idx) {
		targetList.push_back(objList->clone());  // 關鍵程式碼
	}
}

clone()通常直接呼叫函式的“複製建構函式”。程式碼如下:

Base* Base::clone()
{
	return new Base(*this);
}
Derive1* Derive1::clone()
{
	return new Derive1(*this);
}
... 

總結:通過增加一層虛擬函式clone,我們實現了,根據不同型別來進行復制構造的操作,從而實現多型。

2 非成員函式地虛化

流操作“<<”用於輸出記憶體中的內容。現在,我希望,操作符“<<”能根據不同型別來輸出內容。下面將討論兩種情況。

1 型別之間沒有關係

比如,我手頭有3個類,定義如下。他們之間沒有任何關係,那麼就需要針對這三個型別,過載"<<"。這樣,就可以實現虛化——針對不同型別,能都在執行期間動態調整。

class My1 {
}
class My2 {
}
class My3 {
}
std::ostream operator << (std::ostream& out, const My1& obj);
std::ostream operator << (std::ostream& out, const My2& obj);
std::ostream operator << (std::ostream& out, const My3& obj);

實際上,這與虛擬函式的定義有一定差別。虛擬函式需要限定如下幾個條件。因此,我們引出第二種情況。

1 “虛”行為,針對的是有繼承關係的類。
2 表面上看是基類的行為,實際上可能是子類的行為。
3 基類和子類的行為不完全相同。

2 非成員函式的虛行為

現在有若干水果類,繼承與定義關係如下。那麼,我們需要根據不同的水果型別,來輸出其名字。那麼就可以利用虛擬函式。

class Fruit {
	pulic:
		virtual std::string print();
}
class Apple : public Fruit {
	public:
		virtual std::string print();
}

class BananaApple : public Apple {
	public:
		virtual std::string print();
}
std::ostream operator << (std::ostream& out, const Fruit& obj)
{
	obj.print(); // 根據多型,上面定義的三個類中的print都可以被呼叫
}

上面過載的<<,展示了我們可以傳入基類,然後利用基類的多型性,從而實現非成員函式的多型性。