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. jmeter+http接口测试

    参考: http://blog.csdn.net/github_27109687/article/details/71968662   Jmeter接口测试+压力测试 http://blog.csdn ...

  2. web worker 的传值方式以及耗时对比

    背景 前一阵子开发的项目 pptx 导入, 由于自己的代码问题,引起了个性能问题,一个 40p 的 pptx 文件,转换成 json 数据,大概要耗时 60s+ ,虽然后面发现是某个使用频率非常高的函 ...

  3. c语言数据类型字节长度

    突然间就想到了long和int到底什么区别(发现有很多问题都是突然间想到的),然后百度.google各种查找,各种书籍:<C++ Primer>.<C程序设计语言>查看,终于明 ...

  4. linux 搭建Java环境

    一.下载jdk/jre文件 下载链接 二.安装Java环境 1.解压文件到     /usr/java    目录 # tar zxvf jre-8u60-linux-x64.gz 2.配置环境变量 ...

  5. poj-2259 team queue(数据结构)

    第一遍看的时候立即想到了哈希表. 再想时觉得两个队列,一个用来排队伍之间的顺序,一个用来排队伍内部成员的顺序即足够了. DEQUE的时候先判断哪只队伍排在队首,之后再让该队伍中的首队员出列. 整体没有 ...

  6. db2 函数、存储过程示例

    1.函数 --drop function getMaxDate; create FUNCTION getMaxDate (y int, m int ) returns date begin DECLA ...

  7. 【链接】Eclipse的Debug调试技巧

    Eclipse的Debug调试技巧大全 https://mp.weixin.qq.com/s/bORg9YxJiby2WenYRrXY-w 使用Eclipse调试Java程序的10个技巧 https: ...

  8. codeforces 578c - weekness and poorness - 三分

    2017-08-27 17:24:07 writer:pprp 题意简述: • Codeforces 578C Weakness and poorness• 给定一个序列A• 一个区间的poornes ...

  9. HDU 3594 Cactus(仙人掌问题)

    http://acm.hdu.edu.cn/showproblem.php?pid=3594 题意: 一个有向图,判断是否强连通和每条边只在一个环中. 思路: 仙人掌问题. 用Tarjan算法判断强连 ...

  10. 对dataframe中某一列进行计数

    本来是一项很简单的任务...但很容易忘记搞混..所以还是记录一下 方法一: df['col'].value_counts() 方法二: groups = df.groupby('col') group ...