浅析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++》。

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

(0)

相关推荐

  • C++ 构造函数中使用new时注意事项

    使用new初始化对象中的指针成员时遇到的问题 在构造函数中使用new初始化指针成员,那么析构函数中就必须使delete,并且new对应delete, new[]则对应于delete[]. 在有多个构造函数的情况下,必须以相同的方式使用new,要不用new,要不用new[],因为只存在一个析构函数,所有的构造函数都必须与虚构函数相兼容. PS. 当然在构造函数中使用new初始化指针的时候,可以把指针初始化为空(0/NULL 或者是C++11中的nullptr),因为delete不管有没带[]都与空

  • C++中的移动构造函数及move语句示例详解

    前言 本文主要给大家介绍了关于C++中移动构造函数及move语句的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看一个小例子: #include <iostream> #include <cstring> #include <cstdlib> #include <vector> using namespace std; int main() { string st = "I love xing"; vec

  • C++中构造函数的参数缺省的详解

    C++中构造函数的参数缺省的详解 前言: 构造函数中参数的值既可以通过实参传递,也可以指定为某些默认值,即如果用户不指定实参值,编译系统就使形参取默认值.在构造函数中也可以采用这样的方法来实现初始化. #include <iostream> using namespace std; class A { public : A(int aa=0,int bb=00); //在声明构造函数时指定默认参数 int volume( ); int a; int b; }; int main( ) { A

  • C++语法详解之封装、构造函数、析构函数

    大家先了解下什么是构造函数,什么是析构函数,作用是什么? 构造函数(方法)是对象创建完成后第一个被对象自动调用的方法.它存在于每个声明的类中,是一个特殊的成员方法.作用是执行一些初始化的任务.Php中使用__construct()声明构造方法,并且只能声明一个. 析构函数(方法)作用和构造方法正好相反,是对象被销毁之前最后一个被对象自动调用的方法.是PHP5中新添加的内容作用是用于实现在销毁一个对象之前执行一些特定的操作,诸如关闭文件和释放内存等. 下面在通过具体例子看下C++语法详解之封装.构

  • 详解C++ 拷贝构造函数

    拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象.拷贝构造函数通常用于: 通过使用另一个同类型的对象来初始化新创建的对象. 复制对象把它作为参数传递给函数. 复制对象,并从函数返回这个对象. 如果在类中没有定义拷贝构造函数,编译器会自行定义一个.如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数.拷贝构造函数的最常见形式如下: classname (const classname &obj) { // 构造函数的主体 } 在这里,o

  • 详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

    C++中一般创建对象,拷贝或赋值的方式有构造函数,拷贝构造函数,赋值函数这三种方法.下面就详细比较下三者之间的区别以及它们的具体实现 1.构造函数 构造函数是一种特殊的类成员函数,是当创建一个类的对象时,它被调用来对类的数据成员进行初始化和分配内存.(构造函数的命名必须和类名完全相同) 首先说一下一个C++的空类,编译器会加入哪些默认的成员函数 默认构造函数和拷贝构造函数 析构函数 赋值函数(赋值运算符) 取值函数 **即使程序没定义任何成员,编译器也会插入以上的函数! 注意:构造函数可以被重载

  • 详解C++ 编写String 的构造函数、拷贝构造函数、析构函数和赋值函数

    详解C++ 编写String 的构造函数.拷贝构造函数.析构函数和赋值函数 编写类String 的构造函数.析构函数和赋值函数,已知类String 的原型为: class String { public: String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数 String & operate =(const String &ot

  • C++类继承之子类调用父类的构造函数的实例详解

    C++类继承之子类调用父类的构造函数的实例详解 父类HttpUtil: #pragma once #include <windows.h> #include <string> using namespace std; class HttpUtil { private: LPVOID hInternet; LPVOID hConnect; LPVOID hRequest; protected: wchar_t * mHostName; short mPort; string send

  • c++ 子类构造函数初始化及父类构造初始化的使用

    我们知道,构造方法是用来初始化类对象的.如果在类中没有显式地声明构造函数,那么编译器会自动创建一个默认的构造函数:并且这个默认的构造函数仅仅在没有显式地声明构造函数的情况下才会被创建创建. 构造函数与父类的其它成员(成员变量和成员方法)不同,它不能被子类继承.因此,在创建子类对象时,为了初始化从父类中继承来的成员变量,编译器需要调用其父类的构造函数.如果子类的构造函数没有显示地调用父类的构造函数,则默认调用父类的无参构造函数,至于什么事显式调用,在下面会详细说明!关于子类中构造函数的构造原则,总

  • C++ 中构造函数的实例详解

    C++ 中构造函数的实例详解 c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初学者有所帮助. 1. 构造函数是干什么的 class Counter { public: // 类Counter的构造函数 // 特点:以类名作为函数名,无返回类型 Counter() { m_value = 0; } private: // 数据成员 int m_va

随机推荐