從字面來看,談論“虛擬構造函數”沒有意義。當有一個指針或引用,但是不知道其指向對象的真實類型是什么時,可以調用虛擬函數來完成特定類型(type-specific)對象的行為。僅當還沒擁有一個對象但是又確切地知道想要的對象的類型時,才會調用構造函數。那么虛擬構造函數又從何談起呢?
很簡單。盡管虛擬構造函數看起來好像沒有意義,其實它們有非常的用處.例如,假設編寫一個程序,用來進行新聞報道的工作,每一條新聞報道都由文字或圖片組成。可以這樣管理它們:
class NLComponent {//用于 newsletter components
public:// 的抽象基類
... //包含至少一個純虛函數
};
class TextBlock: public NLComponent {
public:
... // 不包含純虛函數
};
class Graphic: public NLComponent {
public:
... // 不包含純虛函數
};
class NewsLetter { // 一個 newsletter 對象
public:// 由NLComponent 對象
... // 的鏈表組成
private:
list<NLComponent*> components;
};
在NewsLetter中使用的list類是一個標準模板類(STL)。list類型對象的行為特性有些象雙向鏈表,盡管它沒有以這種方法來實現。對象NewLetter不運行時就會存儲在磁盤上。為了能夠通過位于磁盤的替代物來建立Newsletter對象,讓NewLetter的構造函數帶有istream參數是一種很方便的方法。當構造函數需要一些核心的數據結構時,它就從流中讀取信息:
class NewsLetter {
public:
NewsLetter(istream& str);
...
};
此構造函數的偽代碼是這樣的:
NewsLetter::NewsLetter(istream& str)
{
while (str) {
從str讀取下一個component對象;
把對象加入到newsletter的 components對象的鏈表中去;
}
}
或者,把這種技巧用于另一個獨立出來的函數叫做readComponent,如下所示:
class NewsLetter {
public:
...
private:
// 為建立下一個NLComponent對象從str讀取數據,
// 建立component 并返回一個指針。
static NLComponent * readComponent(istream& str);
...
};
NewsLetter::NewsLetter(istream& str)
{
while (str) {
// 把readComponent返回的指針添加到components鏈表的最后,
// \"push_back\" 一個鏈表的成員函數,用來在鏈表最后進行插入操作。
components.push_back(readComponent(str));
}
}
考慮一下readComponent所做的工作。它根據所讀取的數據建立了一個新對象,或是TextBlock或是Graphic。因為它能建立新對象,它的行為與構造函數相似,而且因為它能建立不同類型的對象,我們稱它為虛擬構造函數。虛擬構造函數是指能夠根據輸入給它的數據的不同而建立不同類型的對象。虛擬構造函數在很多場合下都有用處,從磁盤(或者通過網絡連接,或者從磁帶機上)讀取對象信息只是其中的一個應用。
還有一種特殊種類的虛擬構造函數――虛擬拷貝構造函數――也有著廣泛的用途。虛擬拷貝構造函數能返回一個指針,指向調用該函數的對象的新拷貝。因為這種行為特性,虛擬拷貝構造函數的名字一般都是copySelf,cloneSelf或者是象下面這樣就叫做clone。很少會有函數能以這么直接的方式實現它:
class NLComponent {
public:
// declaration of virtual copy constructor
virtual NLComponent * clone() const = 0;
...
};
class TextBlock: public NLComponent {
public:
virtual TextBlock * clone() const// virtual copy
{ return new TextBlock(*this); } // constructor
...
};
class Graphic: public NLComponent {
public:
virtual Graphic * clone() const// virtual copy
{ return new Graphic(*this); } // constructor
...
};