将构造函数和非成员函数“虚拟化” (一)

2014-11-24 02:21:11 · 作者: · 浏览: 3

在讨论内容之前先看下面的一个程序,它用来进行新闻报道工作,每一条新闻报道都由文字或图片组成。
[cpp]
class NLComponent { //用于 newsletter components
public: // 的抽象基类
... //包含至少一个纯虚函数
};
class TextBlock: public NLComponent {
public:
... // 不包含纯虚函数
};
class Graphic: public NLComponent {
public:
... // 不包含纯虚函数
};
class NewsLetter { // 一个 newsletter 对象
public: // 由NLComponent 对象
... // 的链表组成
private:
list components;
};

class NLComponent { //用于 newsletter components
public: // 的抽象基类
... //包含至少一个纯虚函数
};
class TextBlock: public NLComponent {
public:
... // 不包含纯虚函数
};
class Graphic: public NLComponent {
public:
... // 不包含纯虚函数
};
class NewsLetter { // 一个 newsletter 对象
public: // 由NLComponent 对象
... // 的链表组成
private:
list components;
};

类结构:


NewLetter在不运行时存储在磁盘上,所以让它的构造函数带有istream参数,可以从流中读取信息。
class NewsLetter {
public:
NewsLetter(istream& str);
...
};


伪代码:

NewsLetter::NewsLetter(istream& str)
{
while (str) {
从str读取下一个component对象;
把对象加入到newsletter的 components对象的链表中去;
}
}
或者把这一读数据的功能单独抽象出来,弄一个readComponent,例如
[cpp]
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));//在这调用
}
}

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根据读取的数据建立一个新的对象,由于它能够建立新对象,所以行为与构造函数相似,并且它能根据输入的不同建立不同类型的对象,因此我们称它为虚拟构造函数。
虚拟构造函数是指能够根据输入给它的数据的不同而建立不同类型的对象。虚拟构造函数在很多场合下都有用处,从磁盘(或者通过网络连接,或者从磁带机上)读取对象信息只是其中的一个应用。
还有一种特殊种类的虚拟构造函数 虚拟拷贝构造函数 也有着广泛的用途。虚拟拷贝构造函数能返回一个指针,指向调用该函数的对象的新拷贝。因为这种行为特性,虚拟拷贝构造函数的名字一般都是copySelf,cloneSelf或者是象下面这样就叫做clone。很少会有函数能以这么直接的方式实现它:
[cpp]
class NLComponent {
public:
// 声明一个虚拟拷贝构造函数
virtual NLComponent * clone() const = 0;
...
};
class TextBlock: public NLComponent {
public:
virtual TextBlock * clone() const // virtual copy
{ return new TextBlock(*this); } // constructor
...
};
class Graphic: public NLComponent {
public:
virtual Graphic * clone() const // virtual copy
{ return new Graphic(*this); } // constructor
...
};

class NLComponent {
public:
// 声明一个虚拟拷贝构造函数
virtual NLComponent * clone() const = 0;
...
};
class TextBlock: public NLComponent {
public:
virtual TextBlock * clone() const // virtual copy
{ return new TextBlock(*this); } // constructor
...
};
class Graphic: public NLComponent {
public:
virtual Graphic * clone() const // virtual copy
{ return new Graphic(*this); } // constructor
...
};

正如我们看到的,类的虚拟拷贝构造函数只是调用它们真正的拷贝构造函数。因此“拷贝”的含义与真正的拷贝构造函数相同。如果真正的拷贝构造函数只做了简单的拷贝,那么虚拟拷贝构造函数也做简单的拷贝。如果真正的拷贝构造函数做了全面的拷贝,那么虚拟拷贝构造函数也做全面的拷贝
注意上面的代码实现采用了较为宽松的虚拟函数返回值类型规则。被派生类重定义的虚函数不用必须与基类的虚拟函数具有一样的返回值。如果函数的返回类型是一个指向基类的指针(或一个引用),那么派生类的函数可以返回一个指向基类的派生类的指针(或引用)。这不是C++的类型检查上的漏洞,它使得有可能声明象虚拟构造函数这样的函数。这就是为什么TextBlock的clone函数能够返回TextBlock*和Graphic的clone能够返回Graphic*的原因,即使NLComponent的clone返回值类型为NLComponent*。
在NLComponent中的虚拟拷贝构造函数能让实现NewLetter的(正常的)拷贝构造函数变得很容易:
[cpp]
class News