浅析C++构造函数虚拟化

所属分类: 软件编程 / C 语言 阅读数: 125
收藏 0 赞 0 分享

虚拟构造函数

当你有一个指针或引用,但是不知道其指向对象的真实类型是什么时,你可以调用虚拟函数来完成特定类型(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++》。

以上就是浅析C++构造函数虚拟化的详细内容,更多关于C++构造函数虚拟化的资料请关注脚本之家其它相关文章!

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多