C++构造函数虚拟化

虚拟构造函数

当你有一个指针或引用,但是不知道其指向对象的真实类型是什么时,你可以调用虚拟函数来完成特定类型(type-specific)对象的行为。仅当你还没拥有一个对象但是你确切地知道想要对象的类型时,你才会调用构造函数。那么虚拟构造函数又从何谈起呢?
例如假设你编写一个程序,用来进行新闻报道的工作,一条新闻报道由文字或图片组成。你可以这样管理它们:

class NLComponent { //用于 newsletter components 的抽象基类
public:
    ... //包含至少一个纯虚函数
}; 

class TextBlock: public NLComponent {
public:
    ... // 不包含纯虚函数
}; 

class Graphic: public NLComponent {
public:
    ... // 不包含纯虚函数
};

class NewsLetter { // 一个 newsletter 对象由NLComponent 对象的链表组成
public:
    NewsLetter(istream& str);
    ...

private:
    list<NLComponent*> components;
};

在NewsLetter中使用的list类是一个标准模板类(STL)。对象NewLetter不运行时就会存储在磁盘上。为了能够通过位于磁盘的替代物来建立Newsletter对象,让NewLetter的构造函数带有istream参数是一种很方便的方法。当构造函数需要一些核心的数据结构时,它就从流中读取信息。此构造函数的伪代码是这样的:

NewsLetter::NewsLetter(istream& str)
{
    while (str) {
    从str读取下一个component对象;
    把对象加入到newsletter的 components
    对象的链表中去;
    }
}

或者,把这种技巧用于另一个独立出来的函数叫做readComponent,如下所示:

class NewsLetter {
public:
    ...

private:
    // 为建立下一个NLComponent对象从str读取数据,
  // 建立component 并返回一个指针。
    static NLComponent * readComponent(istream& str);
    ...
};

NewsLetter::NewsLetter(istream& str)
{
    while (str) {
        // 把readComponent返回的指针添加到components链表的最后,
        // "push_back" 一个链表的成员函数,用来在链表最后进行插入操作。
        components.push_back(readComponent(str));
    }
}

考虑一下readComponent所做的工作。它根据所读取的数据建立了一个新对象,或是TextBlock或是Graphic。因为它能建立新对象,它的行为与构造函数相似,而且因为它能建立不同类型的对象,我们称它为虚拟构造函数。虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。

虚拟拷贝构造函数

还有一种特殊种类的虚拟构造函数――虚拟拷贝构造函数――也有着广泛的用途。虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。因为这种行为特性,虚拟拷贝构造函数的名字一般都是copySelf,cloneSelf或者是象下面这样就叫做clone。很少会有函数能以这么直接的方式实现它:

class NLComponent {
public:
    // declaration of 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);
    }
    ...
};

类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。因此”拷贝”的含义与真正的拷贝构造函数相同。如果真正的拷贝构造函数只做了简单的拷贝,那么虚拟拷贝构造函数也做简单的拷贝。如果真正的拷贝构造函数做了全面的拷贝,那么虚拟拷贝构造函数也做全面的拷贝。

注意上述代码的实现利用了最近才被采纳的较宽松的虚拟函数返回值类型规则。被派生类重定义的虚拟函数不用必须与基类的虚拟函数具有一样的返回类型。如果函数的返回类型是一个指向基类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。这不是C++的类型检查上的漏洞,它使得又可能声明象虚拟构造函数这样的函数。这就是为什么TextBlock的clone函数能够返回TextBlock*和Graphic的clone能够返回Graphic*的原因,即使NLCompo-nent的clone返回值类型为NLComponent*。

在NLComponent中的虚拟拷贝构造函数能让实现NewLetter的(正常的)拷贝构造函数变得很容易:

class NewsLetter {
public:
    NewsLetter(const NewsLetter& rhs);
    ...

private:
    list<NLComponent*> components;
};

NewsLetter::NewsLetter(const NewsLetter& rhs)
{
    // 遍历整个rhs链表,使用每个元素的虚拟拷贝构造函数
    // 把元素拷贝进这个对象的component链表。
    // 有关下面代码如何运行的详细情况,请参见条款35。
    for (list<NLComponent*>::const_iterator it = rhs.components.begin(); it != rhs.components.end(); ++it)
    {
        // "it" 指向rhs.components的当前元素,调用元素的clone函数,
        // 得到该元素的一个拷贝,并把该拷贝放到
        //这个对象的component链表的尾端。
        components.push_back((*it)->clone());
    }
}

遍历被拷贝的NewsLetter对象中的整个component链表,调用链表内每个元素对象的虚拟构造函数。我们在这里需要一个虚拟构造函数,因为链表中包含指向NLComponent对象的指针,但是我们知道其实每一个指针不是指向TextBlock对象就是指向Graphic对象。无论它指向谁,我们都想进行正确的拷贝操作,虚拟构造函数能够为我们做到这点。

以上内容基本都来自《More Effective C++》。

作者:阿凡卢

出处:http://www.cnblogs.com/luxiaoxun/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

http://www.cnblogs.com/luxiaoxun/archive/2012/08/12/2635344.html

时间: 2024-10-18 23:08:03

C++构造函数虚拟化的相关文章

java中父类与子类, 不同的两个类中的因为构造函数由于递归调用导致栈溢出问题

/* 对于类中对成员变量的初始化和代码块中的代码全部都挪到了构造函数中, 并且是按照java源文件的初始化顺序依次对成员变量进行初始化的,而原构造函数中的代码则移到了构造函数的最后执行 */ import static java.lang.System.out; public class PersonDemo { public static void main(String[] args) { //*********测试父类与子类之间的循环调用的问题 out.println("main1&quo

C++对象模型(一):The Semantics of Constructors The Default Constructor (默认构造函数什么时候会被创建出来)

本文是 Inside The C++ Object Model, Chapter 2的部分读书笔记. C++ Annotated Reference Manual中明确告诉我们: default constructor会在需要的时候被编译器产生出来.注意,这里是编译器需要,而不是程序需要.后来的C++ Standard 95修改了这种说法,但是实质上仍是相同的: For class X, if there is none user declared constrator, one default

如何在服务器上虚拟化多个windows供实验室同学们使用?

问题描述 如何在服务器上虚拟化多个windows供实验室同学们使用? 实验室现有一台比较空闲的IBM x3850 X5服务器,而实验室同学大多电脑性能较差,不堪使用.能不能用虚拟化技术或者Windows 多账户登录等功能(无所谓具体方法)让大家的电脑可以很方便连接使用这台服务器呢(通过网线,在学校的普通交换机下).小弟不太精通这个,还需要什么信息请大家指出.先谢谢大家了. 解决方案 虚拟化可以使用windows azure pack+windows server+hyper-vhttp://we

虚拟化-xen4如何通过配置文件添加pci设备

问题描述 xen4如何通过配置文件添加pci设备 操作系统为CentOS6.5 配置文件路径为/var/lib/xend/domains/虚拟机UUID/config.sxp 通过xm creat命令创建半虚拟化虚拟机 如何在该文件中添加PCI设备呢?PCI设备的设备号为05:0.0

微软目标:2011下半年推服务器应用虚拟化

微软将于2011下半年推服务器应用虚拟化.对于这个目标微软至少从2008年开始就一直谈论服务器应用虚拟化的可能性.在近日召开的TechEd大会上,微软定下了这目标:在2011年下半年推出System Center Virtual Machine Manager v.Next. 服务器应用虚拟化将会给微软用户一款用于将传统应用迁移到云中的强大工具.服务器应用虚拟化也许类似于微软现有的客户端App-V产品,它允许用户将应用封装到虚拟单元中,每个单元都是作为一种独立且无状态环境可进行保存和管理的. 微

为什么不能再构造函数中执行大量的内存分配、文件读写等复杂操作??

问题描述 为什么不能再构造函数中执行大量的内存分配.文件读写等复杂操作?? 大婶们啊:为什么不能再构造函数中执行大量的内存分配.文件读写等复杂操作??? 解决方案 可以啊!谁告诉你不行的? 只是在构造函数做太复杂的操作,当出错时发现错误有时会很困难.特别是定义为全局变量时,程序还没有运行.就出错了. 解决方案二: 构造函数主要进行一些初始化工作,复杂的工作放到成员函数中处理,这样比较符合OOP设计 解决方案三: 以牺牲对象分配的时间来换取代码的简单行· 是可以的·! 但是不推荐 解决方案四: 这

数组对象使用构造函数初始化的问题

问题描述 数组对象使用构造函数初始化的问题 解决方案 楼主你这样写不行的,只有定义的时候才能那么初始化 类里面加个成员函数用来设置变量的值SetValue class student { string name; double score; static int num; static double sum; public: student(string n = "no name", double s = 0):name(n),score(s) { num++; sum += scor

FreeBSD 10.0增强了虚拟化功能

FreeBSD宣布发布FreeBSD 10.0-RELEASE.主要新特性包括:默认包管理工具pkg:增强虚拟化,加入bhyve.virtio和原生准虚拟化驱动支持在微软的Hyper-V中把FreeBSD作为一个客操作系统:在clang作为默认编译器的架构上不再安装GCC:ZFS文件系统加入SSD的TRIM支持,支持高性能 LZ4压缩算法:支持 Raspberry Pi:等等. 传送门: http://www.freebsd.org/zh_CN/where.html 查看本栏目更多精彩内容:ht

java-PowerMockito来mock 构造函数的问题

问题描述 PowerMockito来mock 构造函数的问题 public class HttpProtocolHandler { private static HttpProtocolHandler httpProtocolHandler = new HttpProtocolHandler(); public static HttpProtocolHandler getInstance() { return httpProtocolHandler; } /** * 私有的构造方法 */ pri