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. Linux查找含有某字符串的文本文件

    转自:http://www.cnblogs.com/wangkongming/p/4476933.html 如果你想在当前目录下 查找"hello,world!"字符串,可以这样 ...

  2. sparkSQL实战详解

    摘要   如果要想真正的掌握sparkSQL编程,首先要对sparkSQL的整体框架以及sparkSQL到底能帮助我们解决什么问题有一个整体的认识,然后就是对各个层级关系有一个清晰的认识后,才能真正的 ...

  3. [转]将oracle数据库的编码变成utf-8

    1.改客户端字符集:通过WINDOWS的运行菜单运行Regedit,修改注册表 Start -> Run -> Rededit <-| Under registry Editor - ...

  4. GSM900TCP/UDP连接

    TCP发送:AT+CIPSTART="TCP","122.0.114.244",1001返回:OK CONNECT OK 发送: AT+CIPSEND > ...

  5. python中的参数传递和返回值

    python中的参数传递类似java,有着自己的内存回收机制,这和C++有着很大的差别. 1.函数的参数传递: >>> a = [, , ] >>> def fun ...

  6. Codeforces Round #365 (Div. 2) E - Mishka and Divisors(转化成01-背包)

    http://codeforces.com/contest/703/problem/E 题意: 给出n个数和一个k,计算出至少要多少个数相乘才是k的倍数. 思路:这道题目参考了杭电大神的代码http: ...

  7. 转 : CSS Modules详解及React中实践

    https://zhuanlan.zhihu.com/p/20495964 CSS 是前端领域中进化最慢的一块.由于 ES2015/2016 的快速普及和 Babel/Webpack 等工具的迅猛发展 ...

  8. hdu 1241 搬寝室 水dp

    搬寝室 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Desc ...

  9. springboot获取配置文件中的内容

    代码: GrilApplication.java @SpringBootApplication public class GrilApplication { public static void ma ...

  10. java代码实现递归

    think in java 书中使用递归分析 代码如下: public class Snake implements Cloneable { private Snake next; private c ...