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

歡迎訪問 生活随笔!

生活随笔

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

c/c++

C++之虚函数

發布時間:2023/12/15 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++之虚函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

都說面向對象的三大特性是封裝、繼承、多態。C++作為一門面向對象編程語言,肯定也是具備了面向對象的三大特性,那么在C++中是如何實現多態的呢?

在C++中是通過虛函數動態綁定的方式實現多態的。

虛函數與純虛函數

首先我們來回顧一下虛函數,在C++中是使用virtual關鍵字修飾的函數就是虛函數,下面是一個簡單的虛函數例子:

class Base{ public:// 虛函數,必須實現,否則編譯報錯virtual void f1() const{std::cout << "我是Base的f1函數" << std::endl;} };class A:public Base{void f1() const override{std::cout << "我是A的f1函數" << std::endl;} };

虛函數必須在基類中實現,如果不實現的話就會編譯報錯。

如果我們不想實現虛函數的話可以將其聲明為純虛函數,純虛函數的聲明方式如下:

virtual 返回值類型 函數名(函數參數) = 0;

聲明了純虛函數的類稱為抽象類,繼承抽象類的最終子類必須父類的純虛函數,否則不能生產對應的類對象。以下是一個純虛函數的例子:

class Base{ public:// 虛函數,必須實現,否則編譯報錯virtual void f1() const{std::cout << "我是Base的f1函數" << std::endl;}// 純虛函數,不需要實現virtual void f2() const = 0; };class A:public Base{ public:void f1() const override{std::cout << "我是A的f1函數" << std::endl;}void f2() const override{std::cout << "我是A的純虛函數f2" << std::endl;} };

有了虛函數我們就能通過基類的的指針進行動態綁定,在運行時訪問到子類的函數,但是動態綁定只能發生在指針或引用上。例如在以下的例子中,函數test3是不會訪問到子類的函數的,
它訪問的函數依然是基類的虛函數,也就是說它沒有發生動態綁定,因為它既不是指針也不是引用。

class Base{ public:// 虛函數,必須實現,否則編譯報錯virtual void f1() const{std::cout << "我是Base的f1函數" << std::endl;} };class A:public Base{ public:void f1() const override{std::cout << "我是A的f1函數" << std::endl;} };// 引用傳遞參數 void test1(const Base &base){base.f1(); }// 指針傳遞參數 void test2(Base *base){base->f1(); }// 非引用、非指針傳遞參數 void test3(Base base){base.f1(); }int main(int arg,char** argv) {Base *base = new A();test1(*base);test2(base);test3(*base);return 0; }

運行打印結果:

我是A的f1函數 我是A的f1函數 我是Base的f1函數

如果一個類可能會被繼承,這個類的析構函數應該被聲明為一個虛函數,否則會引發內存泄漏。例如以下例子:

class Base{ public:// 虛函數,必須實現,否則編譯報錯virtual void f1() const{std::cout << "我是Base的f1函數" << std::endl;}~Base(){std::cout << "Base的析構函數" << std::endl;} };class A:public Base{ public:void f1() const override{std::cout << "我是A的f1函數" << std::endl;}~A(){std::cout << "A的析構函數" << std::endl;} };int main(int arg,char** argv) {Base *base = new A();delete base;return 0; }

運行輸出如下:

Base的析構函數

從運行結果可以看出A沒有被正確析構,這是因為它的基類Base的析構函數沒有被聲明為虛函數的原因,此時只要我們把Base類的析構函數聲明為虛函數即可修復這個內存泄漏的問題,也就是:

class Base{ public:// 虛函數,必須實現,否則編譯報錯virtual void f1() const{std::cout << "我是Base的f1函數" << std::endl;}// 可能被繼承的類的析構函數應該是一個虛函數virtual ~Base(){std::cout << "Base的析構函數" << std::endl;} };

虛函數總結:

1、當我們在派生類中覆蓋了某個虛函數時,可以再一次使用virtual關鍵字指出該函數的性質。然而這么做并非必須,因為一旦某個函數被聲明成虛函數,則在所有派生類中它都是虛函數。
2、虛函數只有在引用或者指針調用時才會發生動態綁定;
3、基類的析構函數需要聲明為虛函數;
4、虛函數必須要在基類實現,不實現,編譯會報錯;
5、如果子類沒有實現父類的純虛函數,則該子類不能被構造成一個對象。

多態實現原理-虛函數表

通過上面的例子我們知道了在C++中通過引用或指針的形式進行虛函數的動態綁定而實現多態,那么動態綁定在C++中是如何實現呢?答案是虛函數表。

所謂的虛函數表就是:

當編譯器在編譯過程中遇到virtual關鍵字時,它不會對函數調用進行綁定,而是為包含虛函數的類建立一張虛函數表Vtable。在虛函數表中,編譯器按照虛函數的聲明順序依次保存虛函數地址。同時,編譯器會在類中添加一個隱藏的虛函數指針VPTR,指向虛函數表。在創建對象時,將虛函數指針VPTR放置在對象的起始位置,為其分配空間,并調用構造函數將其初始化為虛函數表地址。需要注意的是,虛函數表不占用對象空間。

虛函數表總結:
1、單繼承下的虛函數表

虛函數表中的指針順序,按照虛函數聲明的順序排序;基類的虛函數指針在派生類的前面。

2、多繼承下的虛函數表

多繼承關系下會有多個虛函數表,也會有多個指向不同虛函數表的指針;

推薦閱讀

C++之指針掃盲
C++之智能指針
C++之指針與引用
C++之右值引用
C++之多線程一
C++之多線程二
C++之異常處理

關注我,一起進步,人生不止coding!!!

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。