1. 程式人生 > >C++中virtual(虛擬函式)的用法詳解

C++中virtual(虛擬函式)的用法詳解

在面向物件的C++語言中,虛擬函式(virtual function)是一個非常重要的概念。因為它充分體現了面向物件思想中的繼承和多型性這兩大特性,在C++語言裡應用極廣。比如在微軟的MFC類庫中,你會發現很多函式都有virtual關鍵字,也就是說,它們都是虛擬函式。難怪有人甚至稱虛擬函式是C++語言的精髓。

        那麼,什麼是虛擬函式呢,我們先來看看微軟的解釋:

        虛擬函式是指一個類中你希望過載的成員函式,當你用一個基類指標或引用指向一個繼承類物件的時候,你呼叫一個虛擬函式,實際呼叫的是繼承類的版本。


                                                               ——摘自MSDN


        這個定義說得不是很明白。MSDN中還給出了一個例子,但是它的例子也並不能很好的說明問題。我們自己編寫這樣一個例子:

[cpp] view plain copy print ?
  1. #include "stdio.h"   
  2. #include "conio.h"  
  3.   
  4. class Parent  
  5.   
  6. {  
  7.       
  8. public:  
  9.       
  10.     char data[20];  
  11.     void Function1();     
  12.     virtual void Function2();   // 這裡宣告Function2是虛擬函式  
  13.       
  14. }parent;  
  15.   
  16. void Parent::Function1()  
  17. {  
  18.     printf("This is parent,function1\n");  
  19. }  
  20.   
  21. void Parent::Function2()  
  22.   
  23. {  
  24.     printf("This is parent,function2\n");  
  25. }  
  26.   
  27. class Child:public Parent  
  28.   
  29. {  
  30.     void Function1();  
  31.     void Function2();  
  32.       
  33. } child;  
  34.   
  35. void Child::Function1()  
  36.   
  37. {  
  38.     printf("This is child,function1\n");  
  39. }  
  40.   
  41. void Child::Function2()  
  42.   
  43. {  
  44.     printf("This is child,function2\n");  
  45. }  
  46.   
  47. int main(int argc, char* argv[])  
  48.   
  49. {  
  50.     Parent *p;  // 定義一個基類指標  
  51.     if(_getch()=='c')     // 如果輸入一個小寫字母c      
  52.         p=&child;         // 指向繼承類物件  
  53.     else      
  54.         p=&parent;       // 否則指向基類物件  
  55.     p->Function1();   // 這裡在編譯時會直接給出Parent::Function1()的入口地址。      
  56.     p->Function2();    // 注意這裡,執行的是哪一個Function2?  
  57.     return 0;  
  58.       
  59. }  
#include "stdio.h" 
#include "conio.h"

class Parent

{
	
public:
	
	char data[20];
	void Function1();	
	virtual void Function2();   // 這裡宣告Function2是虛擬函式
	
}parent;

void Parent::Function1()
{
	printf("This is parent,function1\n");
}

void Parent::Function2()

{
	printf("This is parent,function2\n");
}

class Child:public Parent

{
	void Function1();
	void Function2();
	
} child;

void Child::Function1()

{
	printf("This is child,function1\n");
}

void Child::Function2()

{
	printf("This is child,function2\n");
}

int main(int argc, char* argv[])

{
	Parent *p;  // 定義一個基類指標
	if(_getch()=='c')     // 如果輸入一個小寫字母c	
		p=&child;         // 指向繼承類物件
	else	
		p=&parent;       // 否則指向基類物件
	p->Function1();   // 這裡在編譯時會直接給出Parent::Function1()的入口地址。	
	p->Function2();    // 注意這裡,執行的是哪一個Function2?
	return 0;
	
}

用任意版本的Visual C++或Borland C++編譯並執行,輸入一個小寫字母c,得到下面的結果:

This is parent,function1
This is child,function2

      為什麼會有第一行的結果呢?因為我們是用一個Parent類的指標呼叫函式Fuction1(),雖然實際上這個指標指向的是Child類的物件,但編譯器無法知道這一事實(直到執行的時候,程式才可以根據使用者的輸入判斷出指標指向的物件),它只能按照呼叫Parent類的函式來理解並編譯,所以我們看到了第一行的結果。

        那麼第二行的結果又是怎麼回事呢?我們注意到,Function2()函式在基類中被virtual關鍵字修飾,也就是說,它是一個虛擬函式。虛擬函式最關鍵的特點是“動態聯編”,它可以在執行時判斷指標指向的物件,並自動呼叫相應的函式。
如果我們在執行上面的程式時任意輸入一個非c的字元,結果如下:

This is parent,function1
This is parent,function2

        請注意看第二行,它的結果出現了變化。程式中僅僅呼叫了一個Function2()函式,卻可以根據使用者的輸入自動決定到底呼叫基類中的Function2還是繼承類中的Function2,這就是虛擬函式的作用。我們知道,在MFC中,很多類都是需要你繼承的,它們的成員函式很多都要過載,比如編寫MFC應用程式最常用的CView::OnDraw(CDC*)函式,就必須過載使用。把它定義為虛擬函式(實際上,在MFC中OnDraw不僅是虛擬函式,還是純虛擬函式),可以保證時刻呼叫的是使用者自己編寫的OnDraw。虛擬函式的重要用途在這裡可見一斑。

PS:一定要注意“靜態聯翩 ”和“ 動態聯編 ”的區別,對於我來說,若沒有在VC6.0中親自去測試,憑自己的感覺,當在鍵盤中輸入“c”時,我會覺得由於有

[cpp] view plain copy print ?
  1. p=&child;   
p=&child; 
[cpp] view plain copy print ?
  1. 這一句程式碼,我會認為結果都是  
這一句程式碼,我會認為結果都是
[cpp] view plain copy print ?
  1. This is child,function1  
This is child,function1
[cpp] view plain copy print ?
  1. This is child,function2  
This is child,function2
但實際結果卻是

 
  
   
   
    [cpp] 
     view plain
      copy 
     
     
    print
    ?
    
   
  
  1. This is parent,function1  
This is parent,function1
[cpp] view plain copy print ?
  1. This is child,function2  
This is child,function2
因為雖然實際上這個指標指向的是Child類的物件,但編譯器無法知道這一事實,它只能按照呼叫Parent類的函式來理解並編譯,所以我們看到了第一行的結果。
第二行中呼叫了子類的function2,完全是因為virtual 的功能,virtual實現了動態聯編,
它可以在執行時判斷指標指向的物件,並自動呼叫相應的函式。
 
 當然,如果執行的是
 
p=&parent; 
這一句,該指標很明顯的是指向父類,那麼肯定呼叫的是父類的方法  

轉載於:http://blog.csdn.net/foreverhuylee/article/details/34107615