第十一章:什么时间不适宜使用虚函数?
第九章没有用虚函数解决了一个相当困难的问题,第十章解决了同样的问题,但是必须采用虚函数。
11-1 使用的情况
如果只关注程序的行为,同时没有继承关系,那么函数是否为虚函数无关紧要。
一个例子:一个整数数组的IntArray类:
class IntArray {
public:
//....
unsigned size() const;
int& operator[] {unsigned n};
};
我们可以写一个函数来将数组的所有元素设置为零。
void zero{IntArray &x)
{
for(int i = 0; i < x.size(); i++)
x[n] = 0;
}
类似这样的情况,应该设置为虚函数。如果有人希望从IntArray派生出一个类IntFileArray,该类的文件中而不是直接在内存中保存这些整数。如果IntArray::operator[]和IntrArray::size()是虚函数,那么zero函数在IntFileArray对象中也能正常运行。
11-2 不适用的情况
虚函数的代价并不是很高昂,但也不是免费的午餐。
有些情况下非虚函数能够正确运行,而虚函数却不行。
不是所有的类都是为了继承而设计的
11-2-1 效率
想要知道某个程序在这方面的实际开销,必须在不同的机器上测量开销,通过对内存引用进行计数来获得一个大概值是可能的。
int& IntArray::operator[] (unsigned n)
{
if (n>= arraysize)
throw "subsctipt out of range");
return data[n];
}
除了调用函数的开销外,还需要3个内存引用,以便分别获得n, arraysize, 和data的值。怎样将这样开销和调用虚函数的开销进行比较?此外怎样将这个开销与调用非虚成员函数的开销进行比较?
因为假设开销足够大,所以把这个函数内联。因此,一个好的实现通过对象使用operator[]时根本不会因入新得开销。通过指针或者引用operator[]的开销可能与3个内存引用有关:一个是对指针本身的;成员函数初始化this指针的;调用返回序列的。因此通过指针或者引用来调用这个函数所花的时间是两倍。调用一个虚函数通常有三个内存引用取出:描述对象类型的表的地址值;虚函数的地址;本对象的偏移量。所以三倍于执行时间。
class InputBuffer {
public:
//...
virtual int get();
//...
};
计算缓冲区的行数。
int countlines(InputBuffer& b)
{
int n = 0;
int c;
while((c= b.ger()) != EOF) {
if (c == '')
n++;
}
return n;
}
这个函数对于所有InpubBuffer 的派生类都有效这一点来说,他是很灵活的,每个对get的调用都是虚函数调用,所以要消耗掉6个内存引用。3个函数固有,3个虚函数开销。
如果认识到,使用缓冲区的应用程序很可能是要一次性的访问多个字符,那么应InputBuffer 类的设计做的好的多。
class InputBuffer {
public:
//.....
int get() {
if(next >= limit)
return refill();
return *next++;
}
protected:
virtual int refill();
private:
char * nect;
char * limit;
};
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/409557/viewspace-892342/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/409557/viewspace-892342/