设为首页 加入收藏

TOP

条款25:将constructor和non-member functions虚化(1)
2013-10-07 14:02:23 来源: 作者: 【 】 浏览:70
Tags:条款 constructor non-member functions 虚化

条款25:将constructor和non-member functions虚化(1)

第一次面对"virtual constructors"时,似乎不觉得有什么道理可言。是的,当你手上有一个对象的 pointer或 reference,而你不知道该对象的真正类型是什么的时候,你会调用virtual function(虚函数)以完成"因类型而异的行为"。当你尚未获得对象,但已经确知需要什么类型的时候,你会调用 constructor 以构造对象。那么,谁能够告诉我什么是 virtual constructors 呢?

很简单。虽然 virtual constructors 似乎有点荒谬,但它们很有用(如果你认为荒谬的想法都是没有用的,你如何解释现代物理学的成功?)。假设你写了一个软件,用来处理时事新闻,其内容由文字和图形构成。你可以把程序组织成这样:

  1. class NLComponent {                 // 抽象基类,用于时事消息  
  2. public:                                 // 的组件(components),  
  3.   ...                                   // 其中内含至少一个纯虚函数。  
  4. };  
  5.  
  6. class TextBlock: public NLComponent {  
  7. public:  
  8.   ...                                   // 没有内含任何纯虚函数。  
  9. };  
  10.  
  11. class Graphic: public NLComponent {  
  12. public:  
  13.   ...                                   // 没有内含任何纯虚函数。  
  14. };  
  15.  
  16. class NewsLetter {                  // 一份时事通信是由一系列的  
  17. public:                                 // NLComponent 对象构成的。  
  18.   ...  
  19. private:  
  20.   list<NLComponent*> components;  
  21. }; 

这些 classes 彼此间的关系如下:

NewsLetter 所使用的 list class由 Standard Template Library 提供,后者是 C++(www.cppentry.com) 标准程序库的一部分(见条款E49和条款35)。list 对象的行为就像双向链表(doubly linked lists)--尽管它们不一定得用双向链表实现。

NewsLetter 对象尚未开始运作的时候,可能存储于磁盘中。为了能够根据磁盘上的数据产出一份 Newsletter,如果我们让NewsLetter 拥有一个 constructor 并用 istream 作为自变量,会很方便。这个 constructor 将从 stream 读取数据以便产生必要的核心数据结构:

  1. class NewsLetter {  
  2. public:  
  3.   NewsLetter(istream& str);  
  4.   ...  
  5. };  
  6.  
  7. 此 constructor 的伪代码(pseudo code)可能看起来像这样:  
  8.  
  9. NewsLetter::NewsLetter(istream& str)  
  10. {  
  11.   while (str) {  
  12.     read the next component object from str;  
  13.  
  14.     add the object to the list of this  
  15.     newsletter's components;  
  16.   }  

或者,如果将棘手的东西搬移到另一个名为readComponent的函数,就变成这样:

  1. class NewsLetter {  
  2. public:  
  3.   ...  
  4. private:  
  5.   // 从 str 读取下一个 NLComponent 的数据,  
  6.   // 产生组件(component),并返回一个指针指向它。  
  7.   static NLComponent * readComponent(istream& str);  
  8.   ...  
  9. };  
  10.  
  11. NewsLetter::NewsLetter(istream& str)  
  12. {  
  13.   while (str) {  
  14.     // 将 readComponent 返回的指针加到 components list 尾端,  
  15.     // "push_back"是一个 list member function,用来将对象安插  
  16.     // 到 list 尾端。  
  17.     components.push_back(readComponent(str));  
  18.   }  

思考一下,readComponent 做了些什么事。它产生一个崭新对象,或许是个 TextBlock,或许是个Graphic,视读入的数据而定。由于它产生新对象,所以行为仿若constructor,但它能够产生不同类型的对象,所以我们称它为一个virtual constructor。所谓 virtual constructor 是某种函数,视其获得的输入,可产生不同类型的对象。Virtual constructors 在许多情况下有用,其中之一就是从磁盘(或网络或磁带等)读取对象信息。

有一种特别的 virtual constructor--所谓 virtual copy constructor--也被广泛地运用。Virtual copy constructor 会返回一个指针,指向其调用者(某对象)的一个新副本。基于这种行为,virtual copy constructors 通常以 copySelf 或 cloneSelf 命名,或者像下面一样命名为 clone。很少有其他函数能够比这个函数有更直接易懂的实现方式了:

  1. class NLComponent {  
  2. public:  
  3.   // 声明 virtual copy constructor  
  4.   virtual NLComponent * clone() const = 0;  
  5.   ...  
  6. };  
  7.  
  8. class TextBlock: public NLComponent {  
  9. public:  
  10.   virtual TextBlock * clone() const    // virtual copy constructor  
  11.   { return new TextBlock(*this); }  
  12.   ...  
  13. };  
  14.  
  15. class Graphic: public NLComponent {  
  16. public:  
  17.   virtual Graphic * clone() const      // virtual copy constructor  
  18.   { return new Graphic(*this); }  
  19.   ...  
  20. }; 

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇条款14:缓式优化,之二:(1) 下一篇条款20:协助完成"返回值优..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: