http://blog.csdn.net/djh512/article/details/8973606
1.virtual關鍵字主要是什么作用?
c++中的函數調用默認不適用動態綁定。要觸發動態綁定,必須滿足兩個條件:第一,指定為虛函數;第二,通過基類類型的引用或指針調用。
由此可見,virtual主要主要是實現動態綁定。
2.那些情況下可以使用virtual關鍵字?
virtual可用來定義類函數和應用到虛繼承。
友元函數 構造函數 static靜態函數 不能用virtual關鍵字修飾;
普通成員函數 和析構函數 可以用virtual關鍵字修飾;
3.virtual函數的效果
- class GrandFather
- {
- public:
- GrandFather() {}
- virtual void fun()
- {
- cout << "GrandFather call function!" << endl;
- }
- };
- class Father : public GrandFather
- {
- public:
- Father() {}
- void fun()
- {
- cout << "Father call function!" << endl;
- }
- };
- class Son : public Father
- {
- public:
- Son() {}
- void fun()
- {
- cout << "Son call function!" << endl;
- }
- };
- void print(GrandFather* father)
- {
- father->fun();
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- Father * pfather = new Son;
- pfather->fun();
- GrandFather * pgfather = new Father;
- print(pgfather);
- return 0;
- }
Father call function
4.virtual的繼承性
只要基函數定義了virtual,繼承類的該函數也就具有virtual屬性
即 GrandFather Father Son同時定義virtual void fun()與GrandFather一個定義virtual void fun效果是一樣的
5.虛析構函數
- class GrandFather
- {
- public:
- GrandFather() {}
- virtual void fun()
- {
- cout << "GrandFather call function!" << endl;
- }
- ~GrandFather()
- {
- cout << "GrandFather destruction!" << endl;
- }
- };
- class Father : public GrandFather
- {
- public:
- Father() {}
- void fun()
- {
- cout << "Father call function!" << endl;
- }
- ~Father()
- {
- cout << "Father destruction!" << endl;
- }
- };
- class Son : public Father
- {
- public:
- Son() {}
- void fun()
- {
- cout << "Son call function!" << endl;
- }
- ~Son()
- {
- cout << "Son destruction!" << endl;
- }
- };
- void print(GrandFather* p)
- {
- p->fun();
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- Father * pfather = new Son;
- delete pfather;
- return 0;
- }
以上代碼輸出:Father destruction!
GrandFather destruction!
執行了Son的構造函數,沒執行Son的析構函數,故把GrandFather的析構函數設置為virtual
則輸出: Son destruction!
Father Destruction!
GrandFather destruction!
6. 純虛函數
純虛函數定義如下:
- class GrandFather
- {
- public:
- GrandFather() {}
- virtual void fun() = 0
- {
- cout << "GrandFather call function!" << endl;
- }
- virtual ~GrandFather()
- {
- cout << "GrandFather destruction!" << endl;
- }
- };
純虛函數為后代類提供可覆蓋的接口,但這個類中的版本決不會調用。
含有(或繼續)一個或多個純虛函數的類是抽象基類,抽象基類不能實例化!
繼承類只有重寫這個接口才能被實例化
7.虛繼承
虛繼承主要解決交叉繼承帶來的問題。這里給出一片參考文章
c++虛繼承。
給一個例子如下
- class GrandFather
- {
- public:
- GrandFather() {}
- void fun()
- {
- cout << "GrandFather call function!" << endl;
- }
- virtual ~GrandFather()
- {
- cout << "GrandFather destruction!" << endl;
- }
- };
- class Father1 : public GrandFather
- {
- public:
- Father1() {}
- void fun()
- {
- cout << "Father call function!" << endl;
- }
- };
- class Father2 : public GrandFather
- {
- public:
- Father2() {}
- void fun()
- {
- cout << "Father call function!" << endl;
- }
- };
- class Son : public Father1, public Father2
- {
- public:
- Son() {}
- //void fun()
- //{
- // cout << "Son call function!" << endl;
- //}
- };
- void print(GrandFather* p)
- {
- p->fun();
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- Son* son = new Son;
- son->fun();
- return 0;
- }
編譯時會提示報錯對fun的訪問不明確
如果Father1和Father2都用虛繼承繼承GrandFather類則可以解決這個問題
8. 構造函數和析構函數中的虛函數
如果在構造函數或析構函數中調用虛函數,則運行的是為構造函數或析構函數自身類型定義的版本
9.
虛函數的實現機制
關於虛函數的實現機制,這里給出一篇認為寫得蠻清楚的文章。
c++虛函數實現機制
10.小結
關於virtual關鍵字的用法總結如上,有錯誤或者總結不到位的情況請能幫本人指出!
11.例子
- class classA
- {
- public:
- classA()
- {
- clear();
- }
- virtual ~classA()
- {
- }
- void clear()
- {
- memset(this , 0 , sizeof(*this));
- }
- virtual void func()
- {
- printf("func\n");
- }
- };
- class classB : public classA
- {
- };
- int main(void)
- {
- classA oa;
- classB ob;
- classA * pa0 = &oa;
- classA * pa1 = &ob;
- classB * pb = &ob;
- oa.func(); // 1
- ob.func(); // 2
- pa0->func(); // 3
- pa1->func(); // 4
- pb->func(); // 5
- return 0;
- }
func
func
出錯
func
func
談談我的理解,當
classA oa;
oa.func();
不存在動態調用的過程,所以func雖然是虛函數,但是函數調用不通過虛表訪問,所以即使
找不到虛表地址也沒有關系
- memset(this , 0 , sizeof(*this));
在執行classB ob;的時候,注意memset的是classA的地址,所有ob的虛表是存在的
即是如下,通過指針或引用(動態綁定)訪問oa的func函數(需要從虛表訪問),會出錯
訪問ob的func和函數,無論靜態訪問還是動態訪問,都不會出錯
當把classB的代碼改成如下時
- class classB : public classA
- <pre name="code" class="cpp" style="font-weight: bold;">{</pre><pre name="code" class="cpp" style="font-weight: bold;"> classB()
- {
- clear();
- }
- virtual ~classB()
- {
- }
- void clear()
- {
- memset(this , 0 , sizeof(*this));
- }</pre><br>
- <pre></pre>
- <pre name="code" class="cpp" style="font-weight: bold;">};</pre>輸出為
func
func
出錯
出錯
出錯