首页 > 代码库 > MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化
MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化
1.virtual constructor
在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所涉及的
virtual-constructor实际上是"仿virtual-constructor.
假设你设计一个软件,用来处理新闻事件,它由文字和图形构成
class NLComponent { //用于 newsletter components public: // 的抽象基类 ... //包含至少一个纯虚函数 }; class TextBlock: public NLComponent { public: ... // 不包含纯虚函数 }; class Graphic: public NLComponent { public: ... // 不包含纯虚函数 }; class NewsLetter { // 一个 newsletter 对象 public: // 由 NLComponent 对象 ... // 的链表组成 private: list<NLComponent*> components; };
这些class的彼此关系如下图
NewsLetter可能存储于磁盘中,为了从磁盘中读取NewsLetter对象到内存中,它可能有一个接受istream&参数的构造函数:
class NewsLetter { public: NewsLetter(istream& str); ... }; //此构造函数的伪代码是这样的: NewsLetter::NewsLetter(istream& str) { while (str) { //从 str 读取下一个 component 对象; //把对象加入到 newsletter 的 components 对象的链表中去; } }
NewsLetter(istream& str)又可以将其主要功能交由一个readCompnent函数实现:
class NewsLetter { public: NewsLetter(istream& str); ... private: //从str读取下一个NLCompnent的数据,产生组件(compnent),并返回一个指针指向它 static NLCompnent* readCompnent(istream& str); list<NLCompnent*> compnents; ... }; NewsLetter::NewsLetter(istream& str){ while (str) { //将readCompnent返回的指针加到compnents list尾端 compnents.push_back(readCompnent(str)); } }
函数readCompnent产生一个新的NLCompnent子类对象(TextBlock或Graphic),并返回一个NLCompnent指针,由于它能够产生不同类型对象,因此称它为一个virtual constructor.Virtual constructor在很多情况下都很有用,其中之一就是从磁盘(或网络或磁带)读取对象信息.
2.virtual copy constructor
一种特殊的virtual constructor——copy virtual constructor,它返回一个指针指向其调用者的新副本,指针的动态类型由调用它的对象的类型决定.TextBlock和Graphic的copy virtual constructor的可以像这样:
class NLComponent { public: // 声明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); } ... };
这样就clone其实就是实现virtual copy constructor
虽然derived class重新定义base class的虚函数时,但是声明返回值得类型(父类返回指针或引用,那派生类中也返回指针或引用)必须要与base class中相同,但如果函数返回类型是指向base class的指针(或引用),那么derived class可以返回指向其derived class的指针(或引用).因此以上clone虽然是虚函数,但其返回的指针类型可以不同
下面我们实现NewsLetter实现(正常的)copy constructor:
class NewsLetter { public: NewsLetter(const NewsLetter& rhs); ... private: list<NLComponent*> components; }; NewsLetter::NewsLetter(const NewsLetter& rhs){ //遍历rhs的list,运用每个元素的virtual copy constructor将元素复制到此对象的compnents list中. for (list<NLComponent*>::const_iterator it =rhs.components.begin();it != rhs.components.end();++it) //it指向rhs.compnents的目前元素,调用该元素的clone函数取得一个副本并加到本对象的compnents list的尾端 components.push_back((*it)->clone()); }
3.Non-Member Functions 的行为虚化
正如constructors无法被虚化,non-member function原则上也无法被虚化——它连成员函数都不是.考虑要为TextBlock和Graphic实现<<操作符,要使<<对TextBlock和Graphic实现不同的行为,直接的思路就是将<<虚化,但实际上这无法实现:<<的第一个操作数是ostream&,也就是说<<要作为member function,就只能成为ostream类的成员,但这是不可能的.因此<<只能为non-member函数,这是可以采取和virtual constructor类似的策略实现virtual non-member function
class NLComponent { public: virtual ostream& print(ostream& s) const = 0; ... }; class TextBlock: public NLComponent { bbs.theithome.com public: virtual ostream& print(ostream& s) const; ... }; class Graphic: public NLComponent { public: virtual ostream& print(ostream& s) const; ... }; inline ostream& operator<<(ostream& s, const NLComponent& c){ return c.print(s); }
小结:
构造函数的虚化: 可以使用使用拷贝构造函数,通过返回不同的派生类的指针来模拟出构造不同的对象.
没有成员变量的函数的虚化: 用一个虚函数来封装想要虚化的函数.
MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化