本章主要讲述了虚函数的不适用情况:
- 虚函数有时会带来很大的消耗;
- 虚函数不总是提供所需的行为;
- 有时我们写一个类时,可能不想考虑派生问题。
1、虚函数有时会带来很大的消耗:
调用一个虚函数通常由3个内存引用取出:
- 从对象取出描述对象类型的表的地址值;
- 取出虚函数的地址;
- 在可能的较大外围对象中,取出本对象的地址值。
虚函数的开销问题值得关注吗?
这要取决于具体应用。显然,成员函数越大,变为虚函数就越不会是问题。实际上,同样的观点也适用于边界检查:去掉它就会减掉函数本身的3个内存引用中的一个,所以我们有理由所边界检查使函数慢了50%。文中以一个Class InputBuffer的例子,通过分析占用内存引用的数量,解释如何使用虚函数得到更高的效率。
2、虚函数可能导致非预期的行为
文中写了两个缓冲区类,用来存取整数:class IntArray和class IntBlock:
1 class IntArray
2 {
3 public:
4 IntArray(unsigned);
5 int&operator[] (unsigned);
6 //...
7 };
8
9 class IntBlock : public IntArray
10 {
11 public:
12 IntBlock(int l, int h) : low(l), high(h) {};
13 IntArray(l > h ?0: h-l+1) {};
14 int&operator [] (int n)
15 {
16 return IntArray::operator[](n - low);
17 };
18 private:
19 int low, high;
20 };
2 {
3 public:
4 IntArray(unsigned);
5 int&operator[] (unsigned);
6 //...
7 };
8
9 class IntBlock : public IntArray
10 {
11 public:
12 IntBlock(int l, int h) : low(l), high(h) {};
13 IntArray(l > h ?0: h-l+1) {};
14 int&operator [] (int n)
15 {
16 return IntArray::operator[](n - low);
17 };
18 private:
19 int low, high;
20 };
作者希望IntArray的最小边界从0开始,IntBlock的最小边界从1开始,所以只有operator[]为虚函数,才可以正确的访问IntBlock的元素。
3、不是所有的类都是通用的
类的接口由公有成员组成,类的实现由其他东西组成,类可以有两种用户:使用该类对象的人和从这个类派生新类的人。
每个类都有第一种用户,即使这个唯一的用户是类设计者本人,但是有的类则绝对不允许第二种用户。
特殊的析构函数
如果打算让你的类支持集成,那么即使你不使用其他虚函数,可能还是需要一个虚析构函数。
虚函数和非虚函数之间的区别只有在下面特定的环境下才会体现出来:当使用一个基类指针或引用一个派生类对象是。
当下面两件事情同时发生时就需要析构函数了:
- 有需要析构函数的事情发生;
- 它发生这样一种上下文中:指向一个基类的指针或者引用都有一个静态类型,并且实际上都指向一个派生类的对象。
文中案例:
1 #include <iostream>
2
3 usingnamespace std;
4
5 class Base
6 {
7 public:
8 void f()
9 {
10 cout <<"Base::f()"<< endl;
11 };
12 virtualvoid g()
13 {
14 cout <<"Base::g()"<<endl;
15 };
16 };
17
18 class Derived : public Base
19 {
20 public:
21 void f()
22 {
23 cout <<"Derived::f()"<<endl;
24 };
25 virtualvoid g()
26 {
27 cout <<"Derived::g()"<<endl;
28 };
29 };
2
3 usingnamespace std;
4
5 class Base
6 {
7 public:
8 void f()
9 {
10 cout <<"Base::f()"<< endl;
11 };
12 virtualvoid g()
13 {
14 cout <<"Base::g()"<<endl;
15 };
16 };
17
18 class Derived : public Base
19 {
20 public:
21 void f()
22 {
23 cout <<"Derived::f()"<<endl;
24 };
25 virtualvoid g()
26 {
27 cout <<"Derived::g()"<<endl;
28 };
29 };
执行情况:
1 Base base;
2 Derived derived;
3 Base* bp =&base;
4 Base* bq =&derived;
5 Derived* dp =&derived;
6 bp->f(); //Base::f
7 bp->g(); //Base::g
8 bq->f(); //Base::f
9 bq->g(); //Derived::g
10 dp->f(); //Derived::f
11 dp->g(); //Derived::g
2 Derived derived;
3 Base* bp =&base;
4 Base* bq =&derived;
5 Derived* dp =&derived;
6 bp->f(); //Base::f
7 bp->g(); //Base::g
8 bq->f(); //Base::f
9 bq->g(); //Derived::g
10 dp->f(); //Derived::f
11 dp->g(); //Derived::g