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);
}


小结:

构造函数的虚化: 可以使用使用拷贝构造函数,通过返回不同的派生类的指针来模拟出构造不同的对象.

没有成员变量的函数的虚化: 用一个虚函数来封装想要虚化的函数.

时间: 2024-12-25 18:44:07

MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化的相关文章

More Effective C++ 条款25 将constructor和non-member function虚化

1. Virtual constructor 原则上构造函数不能为虚:虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体,但构造函数用于构造对象,在对象构造之前自然没有动态类型的概念,虚与非虚也就无从谈起.所谓的的virtual-constructor实际上是"仿virtual-constructor",它本质上不是constructor,但能够产生不同类型的对象,从而实现"virtual-constructor&q

条款25:考虑写出一个不抛异常的swap函数

条款25:考虑写出一个不抛异常的swap函数 swap函数在C++中是一个非常重要的函数,但实现也非常复杂. 看一个缺省的std::swap函数的实现 namespace std { template<typename T> void swap( T& a , T& b) { T temp(a); a = b; b = temp } } ①内置类型的调用 int a = 2; int b =3; std::swap(a, b); cout<<"a:&quo

MoreEffectiveC++Item35(操作符)(条款5-8)

条款5 对定制的"类型转换函数"保持警惕 条款6 区别increment/decrement操作符的前值和后置形式 条款7 千万不要重载&&,||,和,操作符 条款8 了解不同意义的 new 和 delete

MoreEffectiveC++Item35(效率)(条款16-24)

条款16 谨记80-20法则 80-20 准则说的是大约 20%的代码使用了 80%的程序资源:大约 20%的代码耗用了大约 80%的运行时间:大约 20%的代码使用了 80%的内存:大约 20%的代码执行 80%的磁盘访问:80%的维护投入于大约 20%的代码上:通过无数台机器.操作系统和应用程序上的实验这条准则已经被再三地验证过.80-20 准则不只是一条好记的惯用语,它更是一条有关系统性能的指导方针,它有着广泛的适用性和坚实的实验基础 条款17 考虑使用 lazy evaluation(缓

《Effective C++》学习笔记——条款25

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 四.Designs and Declarations Rule 25:Consider support for a non-throwing swap 规则 25:考虑写出一个不抛异常的 swap 函数 swap 是一个有趣的函数. 原本它只是STL的一部分,而

Effective C++ 条款25 考虑写出一个不抛出异常的swap函数

1. swap是STL的一部分,后来成为异常安全性编程(exception-safe programming)(见条款29)的一个重要脊柱,标准库的swap函数模板定义类似以下: namespace std{ template<typename T> swap(T& lhs,T& rhs){ T temp(lhs); lhs=rhs; rhs=temp; } } 只要T类型支持拷贝构造以及拷贝赋值,标准库swap函数就会调用T的拷贝构造函数和拷贝构造操作符完成值的转换,但对于某

Effective C++笔记_条款25考虑写出一个不抛出异常的swap函数

1 // lib中的swap 2 namespace std { 3 template<typename T> 4 void swap (T& a, T& b) 5 { 6 T temp(a); 7 a = b; 8 b = temp; 9 } 10 } 11 12 // 缺点:需要赋值大量的数据,但是有的时候并不要复制如此多的内容 13 class WidgetImpl { 14 public: 15 //... 16 private: 17 int a, b, c; 18

Effective C++:条款25:考虑写出一个不抛异常的swap函数

(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& b) { T temp(a); a = b; b = temp; } } 这个函数是异常安全性编程的核心,而且是用来处理自我赋值可能性的一个常见机制 可是对某些类型而言,这些复制动作无一必要:当中基本的就是"以指针指向一个对象,内含真正数据"那种类型.多为"pimpl手

effective C++ 条款25 swap

item 25:一个不抛异常的swap函数 标准库有一个swap用于交换两个对象值 namespace std{ template<typename T> void swap(T& a , T& b) { T temp(a) ; a = b ; b = temp ; } } 所以,只要对象的类型T支持copying(copy ctor 和 copy assignment),那么你就可以用这个标准库的swap,但是,你必须保证,你swap的时候,上述操作是你需要的,有的时候,没有