您好,登錄后才能下訂單哦!
本篇文章和大家了解一下C++中多態性和虛函數的使用原理。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。
按照Java的思維方式,在有了繼承和向上類型轉換(upcasting)之后,就可以實現多態性了。但是在C++中似乎并不能orz。考慮這種情況:
#include<iostream> using std::cout; using std::endl; class A{ public: void f() const{ cout<<"class A's function"<<endl; } }; class B : public A{ public: void f() const{ cout<<"class B's function"<<endl; } }; int main(){ B b; A *ptr_a = &b; A &ref_a = b; ptr_a->f();//print: class A's function ref_a.f();//print: Class A's function }
在使用基類指針或引用調用一個派生類對象的函數時,我們發現程序仍然在調用基類的函數,要想解決這種情況,就要引入virtual關鍵字,將上面代碼里的class A修改如下,main中的輸出就變成類B中f()的輸出了。
class A{ public: virtual void f() const{ cout<<"class A's function"<<endl; } };
那么為什么Java不需要呢?因為virtual關鍵字實現功能的同時,會增加該類一些操作的時間和空間占用,C++將這部分占用的優化決定權交給了程序員,以實現可能的效率提高;而Java內置了virtual的機制,沒有提高效率的選擇,但是簡化了編程。(關于virtual的具體機制,建議參考Thinking in C++)
有兩點需要注意的:
第一、當使用基類指針指向派生類時,無法通過基類指針直接調用派生類中增加的函數(基類中沒有同名虛函數),除非將基類指針強制類型轉換為派生類指針。
第二、只能通過基類指針或者引用來調用派生類對象,如果我們將一個派生類對象通過值傳遞的方式傳遞給基類對象,這個對象被真的切成一個基類對象,而不具有任何派生類的內容。
在類設計時,常常希望基類僅僅作為派生類的一個接口,被繼承實現,而不會去創建基類對象,這時,可以在基類中定義純虛函數,使其成為一個抽象類。定義純虛函數語法是在一個虛函數聲名的基礎上,加上=0。例如:virtual void f() = 0;
注意:當繼承一個抽象類時,必須實現其所有的純虛函數,否則繼承出的類也是一個抽象類。
一般情況下,在基類中我們不會對純虛函數進行實現,但是C++提供了實現純虛函數的機制,這種方法可以讓我們定義一段公共代碼,使派生類可以公用。
class A{ public: virtual void do() = 0; }; /* *純虛函數不能作為inline函數實現,要放在類外! */ void A::do(){ //一些公共代碼 } class B : public A{ public: void do() { A::do(); //其他代碼 } };
如上文所說,定義一個虛函數時,需要做一些額外的工作,完成這些工作的代碼其實被秘密插入到類構造函數的開頭部分。那么就有一個問題,如果我們在構造函數中調用虛函數會發生什么現象?答案是,會調用這個虛函數的本地版本,即虛函數機制在構造函數中不工作。
另外,構造函數也不能被定義為虛函數。
構造函數不能被定義為虛函數,而析構函數可以,并且經常被定義為虛函數。
#include<iostream> using namespace std; class Base1{ public: ~Base1(){cout<<"~Base1()"<<endl;} }; class Base2{ public: virtual ~Base2(){cout<<"~Base2()"<<endl;} }; class Derived1 : public Base1{ public: ~Derived1(){cout<<"~Derived1()"<<endl;} }; class Derived2 : public Base2{ public: ~Derived2(){cout<<"~Derived2()"<<endl;} }; int main(){ Base1* pd1 = new Derived1(); Base2* pd2 = new Derived2(); delete pd1; delete pd2; }
上面代碼的控制臺輸出:
~Base1()
~Derived2()
~Base2()
上面的代碼暴露出在使用多態性時,不把析構函數定義成虛函數所帶來的影響。這種錯誤不會立刻使程序崩潰,但是它不知不覺中使內存泄漏。
在一些時候,我們需要定義一個抽象類,但是剛好沒有其他純虛函數,這時候我們不妨將析構函數定義為純虛的,因為作為基類的析構函數本來就要求為虛函數,將其進一步定義為純虛函數并無太大不同。唯一需要注意的是,定義純虛析構函數時必須為其提供函數體,如下。
class A{ public: virtual ~A() = 0; }; A::~A(){ } class B:public A{ //不一定需要重定義析構函數,根據需要 }
還要注意一點,在析構函數中,虛機制也是不存在的,可通過下面的代碼體會。
#include<iostream> using namespace std; class Base{ public: virtual ~Base(){cout<<"~Base()"<<endl;f();} virtual void f(){cout<<"Base::f()"<<endl;} }; class Derived : public Base{ public: ~Derived(){cout<<"~Derived()"<<endl;} void f(){cout<<"Derived::f()"<<endl;} }; int main(){ Base * pb = new Derived(); delete pb; }
控制臺輸出為:
~Derived()
~Base()
Base::f()
以上就是C++中多態性和虛函數的使用原理的簡略介紹,當然詳細使用上面的不同還得要大家自己使用過才領會。如果想了解更多,歡迎關注億速云行業資訊頻道哦!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。