本章主要講述了虛函數的不適用情況:
- 虛函數有時會帶來很大的消耗;
- 虛函數不總是提供所需的行為;
- 有時我們寫一個類時,可能不想考慮派生問題。
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 };
作者希望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 };
執行情況:
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