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 = ;
...
};
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 = ;
...
};
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虚化的更多相关文章

  1. MoreEffectiveC++Item35 条款27: 要求或禁止对象产生于heap中

    一 要求对象产生在heap中 阻止对象产生产生在non-heap中最简单的方法是将其构造或析构函数声明在private下,用一个public的函数去调用起构造和析构函数 class UPNumber ...

  2. MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数

    一 允许零个或一个对象 我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类 ...

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

    条款16 谨记80-20法则 条款17 考虑使用 lazy evaluation(缓释评估) 条款18 分期摊还预期的计算成本 条款19 了解临时对象的来源 条款20 协助完成"返回值的优化 ...

  4. MoreEffectiveC++Item35(异常)(条款9-15)

    条款9 使用析构函数防止内存泄漏 条款10 在构造函数中防止内存泄漏 条款11 禁止异常信息传递到析构函数外 条款12 理解"抛出一个异常''与"传递一个参数"或调用一个 ...

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

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

  6. MoreEffectiveC++Item35(基础议题)(条款1-4)

    条款1:区别指针和引用 条款2:最好使用C++转换操作符 条款3: 绝对不要以多态的方式处理数组 条款4: 避免无用的缺省构造函数 条款1:区别指针和引用 1.指针(pointer) 使用[*/-&g ...

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

    (一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...

  8. effective C++ 条款25 swap

    item 25:一个不抛异常的swap函数 标准库有一个swap用于交换两个对象值 namespace std{ template<typename T> void swap(T& ...

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

    当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...

随机推荐

  1. golang test 单元测试

    golang自家的单元测试做的很好了,自需要"文件名_test.go" 就可以在里面写单元测试,而且go test命令也很强大,可以只运行单个测试函数,在goland 可以点击单元 ...

  2. 关于JavaScript对象,你所不知道的事(二)- 再说属性

    说完了对象那些不常用的冷知识,是时候来看看JavaScript中对象属性有哪些有意思的东西了. 不出你所料,对象属性自然也有其相应的特征属性,但是这个话题有点复杂,让我们先从简单的说起,对象属性的分类 ...

  3. POJ 2051 argus(简单题,堆排序or优先队列)

    又是一道数据结构题,使用堆来进行权值调整和排序,每次调整都是o(n)的复杂度,非常高效. 第一眼看题觉得可以用优先队列来做,应该也很简单. 事实上多数优先队列都是通过堆来实现的. 写的时候还是出了一些 ...

  4. IPTABLES拒绝某个IP某项服务,并记录到日志(rhel7实例)

    #iptables -I INPUT -p icmp -s 192.168.0.1 -j DROP                 \\在INPUT链中插入:如果检测到从192.168.0.1发过来的 ...

  5. java 枚举的写法

    1.第一种形式: public interface PrealertConstants { enum platformCodeEnum{ CAINIAO(1), CLOSED(-5), OFFLINE ...

  6. 《用 Python 学微积分》笔记 2

    <用 Python 学微积分>原文见参考资料 1. 13.大 O 记法 比较两个函数时,我们会想知道,随着输入值 x 的增长或减小,两个函数的输出值增长或减小的速度究竟谁快谁慢.通过绘制函 ...

  7. ThinkPHP开发笔记-前后端数据交互

    此处就是 Controller 和 View 相互传数据. 1.Controller 向 View 的页面传数据.在控制器中把变量传递给模板,使用 assign 方法对模板变量赋值.例如: 在Cont ...

  8. 用gitolite搭建git server

    在Ubuntu上测试安装一下git server,为后面项目的代码管理做准备.记录流水账如下, 中间关于git 命令的使用说明不做过多解释,需要了解的请google或者直接git help: 我用到了 ...

  9. 关于ckeditor 之 上传功能

    度了很多文章,看了很多关于ckeditor配置上传功能的文章,没一个写得清楚的, 就是简单的根目录下.config.js 增加 config.filebrowserUploadUrl="/a ...

  10. raid1磁盘更换---测试

    安装centos6.71. CentOS安装过程配raid.参考:http://www.360doc.com/content/13/1209/21/14661619_335823338.shtml. ...