您好,登錄后才能下訂單哦!
虛擬構造函數
當你有一個指針或引用,但是不知道其指向對象的真實類型是什么時,你可以調用虛擬函數來完成特定類型(type-specific)對象的行為。僅當你還沒擁有一個對象但是你確切地知道想要對象的類型時,你才會調用構造函數。那么虛擬構造函數又從何談起呢?
例如假設你編寫一個程序,用來進行新聞報道的工作,一條新聞報道由文字或圖片組成。你可以這樣管理它們:
class NLComponent { //用于 newsletter components 的抽象基類 public: ... //包含至少一個純虛函數 }; class TextBlock: public NLComponent { public: ... // 不包含純虛函數 }; class Graphic: public NLComponent { public: ... // 不包含純虛函數 }; class NewsLetter { // 一個 newsletter 對象由NLComponent 對象的鏈表組成 public: NewsLetter(istream& str); ... private: list<NLComponent*> components; };
在NewsLetter中使用的list類是一個標準模板類(STL)。對象NewLetter不運行時就會存儲在磁盤上。為了能夠通過位于磁盤的替代物來建立Newsletter對象,讓NewLetter的構造函數帶有istream參數是一種很方便的方法。當構造函數需要一些核心的數據結構時,它就從流中讀取信息。此構造函數的偽代碼是這樣的:
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 constructor { return new TextBlock(*this); } ... }; class Graphic: public NLComponent { public: virtual Graphic * clone() const // virtual copy constructor { return new Graphic(*this); } ... };
類的虛擬拷貝構造函數只是調用它們真正的拷貝構造函數。因此”拷貝”的含義與真正的拷貝構造函數相同。如果真正的拷貝構造函數只做了簡單的拷貝,那么虛擬拷貝構造函數也做簡單的拷貝。如果真正的拷貝構造函數做了全面的拷貝,那么虛擬拷貝構造函數也做全面的拷貝。
注意上述代碼的實現利用了最近才被采納的較寬松的虛擬函數返回值類型規則。被派生類重定義的虛擬函數不用必須與基類的虛擬函數具有一樣的返回類型。如果函數的返回類型是一個指向基類的指針(或一個引用),那么派生類的函數可以返回一個指向基類的派生類的指針(或引用)。這不是C++的類型檢查上的漏洞,它使得又可能聲明象虛擬構造函數這樣的函數。這就是為什么TextBlock的clone函數能夠返回TextBlock*和Graphic的clone能夠返回Graphic*的原因,即使NLCompo-nent的clone返回值類型為NLComponent*。
在NLComponent中的虛擬拷貝構造函數能讓實現NewLetter的(正常的)拷貝構造函數變得很容易:
class NewsLetter { public: NewsLetter(const NewsLetter& rhs); ... private: list<NLComponent*> components; }; NewsLetter::NewsLetter(const NewsLetter& rhs) { // 遍歷整個rhs鏈表,使用每個元素的虛擬拷貝構造函數 // 把元素拷貝進這個對象的component鏈表。 // 有關下面代碼如何運行的詳細情況,請參見條款35。 for (list<NLComponent*>::const_iterator it = rhs.components.begin(); it != rhs.components.end(); ++it) { // "it" 指向rhs.components的當前元素,調用元素的clone函數, // 得到該元素的一個拷貝,并把該拷貝放到 //這個對象的component鏈表的尾端。 components.push_back((*it)->clone()); } }
遍歷被拷貝的NewsLetter對象中的整個component鏈表,調用鏈表內每個元素對象的虛擬構造函數。我們在這里需要一個虛擬構造函數,因為鏈表中包含指向NLComponent對象的指針,但是我們知道其實每一個指針不是指向TextBlock對象就是指向Graphic對象。無論它指向誰,我們都想進行正確的拷貝操作,虛擬構造函數能夠為我們做到這點。
以上內容基本都來自《More Effective C++》。
以上就是淺析C++構造函數虛擬化的詳細內容,更多關于C++構造函數虛擬化的資料請關注億速云其它相關文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。