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

2014-11-24 02:21:11 · 作者: · 浏览: 4
Letter {
public:
NewsLetter(const NewsLetter& rhs);//拷贝构造函数
...
private:
list components;
};
NewsLetter::NewsLetter(const NewsLetter& rhs)
{
// 遍历整个rhs链表,使用每个元素的虚拟拷贝构造函数
// 把元素拷贝进这个对象的component链表。
for (list::const_iterator it =
rhs.components.begin();
it != rhs.components.end();
++it) {
// "it" 指向rhs.components的当前元素,调用元素的clone函数,
// 得到该元素的一个拷贝,并把该拷贝放到
// 这个对象的component链表的尾端。
components.push_back((*it)->clone());
}
}

class NewsLetter {
public:
NewsLetter(const NewsLetter& rhs);//拷贝构造函数
...
private:
list components;
};
NewsLetter::NewsLetter(const NewsLetter& rhs)
{
// 遍历整个rhs链表,使用每个元素的虚拟拷贝构造函数
// 把元素拷贝进这个对象的component链表。
for (list::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对象。无论它指向谁,我们都想进行正确的拷贝操作,虚拟构造函数能够为我们做到这点。


虚拟化非成员函数


就像构造函数不能成为虚函数一样,非成员函数也不能成为真正的虚函数。但是,既然一个函数能够构造出不同类型的新对象是可以理解的,那么同样也存在这样的非成员函数,可以根据其参数的动态类型的不同来展现出不同的特性。
假设你想为TextBlock和Graphic对象实现一个输出操作符。显而易见的方法是虚拟化这个输出操作符。
输出操作符是 operator<<,这里为了使得输出操作符是类的成员函数(支持虚函数),你必须把函数参数 ostream&放在操作符的右边,作为右参数。
[cpp]
class NLComponent {
public:
// 对输出操作符的不寻常的声明
virtual ostream& operator<<(ostream& str) const = 0;
...
};
class TextBlock: public NLComponent {
public:
// 虚拟输出操作符(同样不寻常)
virtual ostream& operator<<(ostream& str) const;
};
class Graphic: public NLComponent {
public:
// 虚拟输出操作符 (让就不寻常)
virtual ostream& operator<<(ostream& str) const;
};
虽然这样做是可以的,但是我们看看当调用的时候会发生什么
TextBlock t;
Graphic g;
...
t << cout;
//你需要把cout作为右操作数
// 通过virtual operator<<
//把t打印到cout中。
// 不寻常的语法
g << cout; //通过virtual operator<<
//把g打印到cout中。
//不寻常的语法

class NLComponent {
public:
// 对输出操作符的不寻常的声明
virtual ostream& operator<<(ostream& str) const = 0;
...
};
class TextBlock: public NLComponent {
public:
// 虚拟输出操作符(同样不寻常)
virtual ostream& operator<<(ostream& str) const;
};
class Graphic: public NLComponent {
public:
// 虚拟输出操作符 (让就不寻常)
virtual ostream& operator<<(ostream& str) const;
};
虽然这样做是可以的,但是我们看看当调用的时候会发生什么
TextBlock t;
Graphic g;
...
t << cout;
//你需要把cout作为右操作数
// 通过virtual operator<<
//把t打印到cout中。
// 不寻常的语法
g << cout; //通过virtual operator<<
//把g打印到cout中。
//不寻常的语法

这与我们正常熟悉的使用方法相违背,但是为了能够回到正常的语法上来,我们必须把operator<<移出TextBlock 和 Graphic类,但是如果我们这样做,就不能再把它声明为虚拟了。
另一种方法是为打印操作声明一个虚拟函数(例如print)把它定义在TextBlock 和 Graphic类里。但是如果这样,打印TextBlock 和 Graphic对象的语法就与使用operator<<做为输出操作符的其它类型的对象不一致了,这些解决方法都不很令人满意。我们想要的是一个称为operator<<的非成员函数,其具有象print虚拟函数的行为特性。
我们定义operator<< 和print函数,让前者调用后者!
[cpp]
class NLComponent {
public:
virtual ostream& print(ostream& s) const = 0;
...
};
class TextBlock: public NLComponent {
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);
}

class NLComponent {
public:
virtual ostream& print(ostream& s) const = 0;
...
};
class TextBlock: public NLComponent {
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);
}

具有虚拟行为的非成员函数很简单。你编写一个虚拟函数来完成工作,然后再写一个非虚拟函数,它什么也不做只是调用这个虚拟函数。为了避免这个句法花招引起函数调用开销,你当然可以内联这个非虚拟函数。