日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

初识C++之虚函数

發(fā)布時間:2024/9/27 c/c++ 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 初识C++之虚函数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1、什么是虛函數(shù)
  在基類中用virtual關鍵字修飾,并在一個或多個派生類中被重新定義的成員函數(shù),用法格式為:
  virtual 函數(shù)返回類型 函數(shù)名(參數(shù)表)
   {
    函數(shù)體
   }
   虛函數(shù)是實現(xiàn)多態(tài)性的關鍵,通過指向派生類的基類指針或引用,訪問派生類中同名覆蓋成員函數(shù)。
  
看兩個例子:
①沒有定義基類的Fun函數(shù)為虛函數(shù):

#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std;class Base { public:void Fun(){cout << "Base::Fun()" << endl;} private:int data; };class Derived : public Base { public:void Fun(){cout << "Derived::Fun()" << endl;} private:int data; };int main() {Derived d;Base* pb = &d;pb->Fun();return 0; }


可以看到此時基類指針雖然指向的是派生類,但仍然調(diào)用的是基類的Fun函數(shù)。

②定義了基類的Fun函數(shù)為虛函數(shù):

#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std;class Base { public:virtual void Fun() //注意這兒加了關鍵字virtual{cout << "Base::Fun()" << endl;} private:int data; };class Derived : public Base { public:void Fun(){cout << "Derived::Fun()" << endl;} private:int data; };int main() {Derived d;Base* pb = &d;pb->Fun();return 0; }


可以看到此時基類指針指向派生類后,其調(diào)用Fun函數(shù)時就是調(diào)用派生類的Fun函數(shù)了,從這兒也能看出虛函數(shù)是實現(xiàn)多態(tài)的關鍵。

2、虛函數(shù)的實現(xiàn)
①在基類中用virtual關鍵字修飾成員函數(shù),使之成為一個虛函數(shù),在派生類中重寫該函數(shù),使之完成新的功能。

②派生類中重寫該虛函數(shù)時,要求重寫的函數(shù)的函數(shù)名、參數(shù)列表、返回值都與基類虛函數(shù)相同,函數(shù)體根據(jù)需要選擇是否重寫。若不重寫,則直接完成繼承于基類。
PS:C++規(guī)定,當一個成員函數(shù)被定義為虛函數(shù)時,其派生類中的同名函數(shù)都自動變?yōu)樘摵瘮?shù),因此,派生類在重新定義該虛函數(shù)時,可加virtual關鍵字,也可以不加,但為了層次的清晰與代碼的易讀性,最好還是加上(那感覺前面說的都是廢話)。

③定義一個基類的指針,使之指向同一類族中需要調(diào)用的函數(shù)所在類的對象。
PS:基類與派生類的賦值關系可以參考下面這篇博客中的第六條:
http://blog.csdn.net/ljx_5489464/article/details/51114534

④通過該指針調(diào)用虛函數(shù),此時調(diào)用的就是指針指向的對象的同名函數(shù)了。

用一個實例來說明上述實現(xiàn)機制:

#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std;class Base { public:virtual void Fun() //加virtual關鍵字{cout << "Base::Fun()" << endl;} private:int data; };class Derived : public Base { public:void Fun(){//重寫Fun函數(shù)的函數(shù)體,當然,這兒不是必須要重寫的cout << "Derived::Fun()" << endl;} private:int data; };class Derived_Derived : public Derived { public:void Fun(){//重寫Fun函數(shù)的函數(shù)體,當然,這兒不是必須要重寫的cout << "Derived_Derived::Fun()" << endl;} private:int data; };int main() {Derived_Derived d;Derived* pb = &d;pb->Fun();return 0; }


我在Derived類中并沒有在Fun函數(shù)前加virtual關鍵字,但pb指針調(diào)用的卻是Derived_Derived類中的Fun函數(shù),而非Derived類中的,這就說明了只要基類的成員函數(shù)被定義為虛函數(shù)時,派生類的同名函數(shù)就會變?yōu)樘摵瘮?shù)。

注意:虛函數(shù)只能出現(xiàn)在類的繼承層次中,只能定義類的成員函數(shù)為虛函數(shù),不能定義類外的普通函數(shù)為虛函數(shù):

#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std;virtual void Fun() //注意這兒加了關鍵字virtual {cout << "Base::Fun()" << endl; }int main() {Fun();return 0; }

以上代碼會報錯:

3、虛析構函數(shù)
  析構函數(shù)執(zhí)行時先調(diào)用派生類的析構函數(shù),其次才調(diào)用基類的析構函數(shù)。如果析構函數(shù)不是虛函數(shù),而程序執(zhí)行時又要通過基類的指針去銷毀派生類的動態(tài)對象(用new運算符創(chuàng)建的),那么用delete銷毀對象時,只調(diào)用了基類的析構函數(shù),未調(diào)用派生類的析構函數(shù)。這樣會造成銷毀對象不完全。

看例子:

#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> using namespace std; class Base { public: Base(){ cout << "Base()" << endl; }~Base() {cout << "~Base()" << endl; } private: int data; };class Derived: public Base { public: Derived(){cout << "Derived()" << endl;}~Derived(){cout << "~Derived()" << endl;} private: int data; }; int main() { Base *pb = new Derived;delete pb; return 0; }


可以看到,確實是只調(diào)用了基類的析構函數(shù),派生類的析構函數(shù)并沒有被調(diào)用。

當我把基類的析構函數(shù)改成這樣:

virtual ~Base() {cout << "~Base()" << endl; }

再運行時,結(jié)果如下:

可以看到,此時不管是基類還是派生類的的析構函數(shù)都被調(diào)用了,為什么呢?
解析如下:
假設Derive類public繼承自Base類,并且基類的析構函數(shù)沒有定義為虛函數(shù),有如下調(diào)用:
Base* pb = new Derive;
delete pb;
這時候,delete pb時,編譯器會把pb當成Base的一個對象看待,因為pb是Base類型的,所以直接去調(diào)用基類的析構函數(shù);
若將基類的析構函數(shù)寫成virtual函數(shù),那么基類和派生類的析構函數(shù)會分別存放在自己的虛表中,這時再執(zhí)行delete pb時,會調(diào)用析構函數(shù),但現(xiàn)在虛構函數(shù)是虛函數(shù),所以會到虛表中去查找,而此時pb指向的剛好是一個派生類對象,所以通過虛表查找就找到了派生類的虛函數(shù),從而調(diào)用派生類的析構函數(shù)。

4、純虛函數(shù)
①定義:
  純虛函數(shù)是在基類中聲明的虛函數(shù),它在基類中沒有定義,即沒有函數(shù)體,但要求任何派生類都要實現(xiàn)自己的函數(shù)體。在基類中實現(xiàn)純虛函數(shù)的方法是在函數(shù)原型后加“=0;“。
virtual void funtion()=0 ;

②引入原因 :
  為了方便使用多態(tài)特性,我們常常需要在基類中定義虛擬函數(shù)。 但在很多情況下,基類本身生成對象是不合情理的。例如,動物作為一個基類可以派生出老虎、孔雀等子類,但動物本身生成對象明顯不合常理。 為了解決上述問題,引入了純虛函數(shù)的概念,將函數(shù)定義為純虛函數(shù),則編譯器要求在派生類中必須予以重寫以實現(xiàn)多態(tài)性。這樣就很好地解決了上述兩個問題。

③作用:
  純虛函數(shù)的作用是在基類中為其派生類保留一個函數(shù)的名字,以便派生類根據(jù)需要對其進行實現(xiàn),從而實現(xiàn)多態(tài)性。

④抽象類
  不用來定義對象,只是作為一種基本類型用作繼承的類,稱為抽象類,由于抽象類通常作為基類,所以也稱為抽象基類。包含純虛函數(shù)的類就是抽象類,它不能生成對象。

注意:
①純虛函數(shù)沒有函數(shù)體;
②純虛函數(shù)聲明形式最后的“=0;”并不是返回值是0,它只是起一個形式上的作用,告訴編譯器這是虛函數(shù);
③虛函數(shù)的聲明形式是一個語句,最后要加上“;”。
④純虛函數(shù)只有聲明,沒有函數(shù)體,所以它不具有函數(shù)功能,因此不能被調(diào)用,可以稱其為“徒有其表”。
⑤如果一個類中聲明了虛函數(shù),且在其派生類中沒有被實現(xiàn),那么在派生類中它仍為純虛函數(shù)。

最后用一個實例來剖析:

class Animal { public:virtual void Sleep() = 0; //這里聲明Sleep為純虛函數(shù) public:char sex[5];int age; };class Person: public Animal { public:virtual void Sleep(){cout << "Person lying down to sleep!" << endl;} public:char notionality[20]; //國籍char name[10]; };class Horse: public Animal { public:virtual void Sleep(){cout << "Horse sleep standing up!" << endl;} public:char rase[20]; //種族 };int main() {Animal a;return 0; }


此時用含有純虛函數(shù)Sleep的抽象類Animal來創(chuàng)建對象時,會報錯。

我把主函數(shù)改為調(diào)用抽象類Animal中的Sleep函數(shù):

int main() {Animal::Sleep();return 0; }


調(diào)用一個純虛函數(shù),會報錯。

我把Person修改了,去掉它對Sleep函數(shù)的實現(xiàn):

class Person: public Animal { public:char notionality[20]; //國籍char name[10]; };


此時Person類中沒有對Sleep函數(shù)進行實現(xiàn),因此,Person類中的Sleep函數(shù)仍為虛函數(shù),會報錯。

總結(jié)

以上是生活随笔為你收集整理的初识C++之虚函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。