生活随笔
收集整理的這篇文章主要介紹了
C++虚函数
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
C++三大特性
1.繼承 一個對象直接使用另一個對象的屬性和方法,減少重復的代碼,增加了類的耦合性。但父類通常定義了子類的部分行為,父類的改變可能影響子類的行為。 2. 多態 C++中有靜多態(編譯時多態)和動多態(運行時多態)兩種。靜多態主要通過模板來實現,宏也是實現靜多態的一種途徑,所有調用在編譯期就可確定,因此它是靜態的;動多態在C++中是通過虛函數實現的,即在基類中存在一些接口(一般為純虛函數),子類必須重載這些接口,通過使用基類的指針或者引用指向子類的對象,通過基類指針實現調用子類對應的函數的功能,函數調用在執行期才能進行確定,所以它是動態的。
# include <iostream>
using namespace std
; class line
{
public : void draw ( ) { cout
<< "line is drawing!" << endl
; }
} ; class circle
{
public : void draw ( ) { cout
<< "circle is drawing!" << endl
; }
} ; template < typename T >
void drawShape ( T
& shape
)
{ shape
. draw ( ) ;
} int main ( )
{ line lining
; circle circling
; drawShape ( lining
) ; drawShape ( circling
) ; return 0 ;
}
# include <iostream>
using namespace std
; class shape
{
public : virtual void draw ( ) = 0 ;
} ; class line : public shape
{
public : void draw ( ) { cout
<< "line is drawing!" << endl
; }
} ; class circle : public shape
{
public : void draw ( ) { cout
<< "circle is drawing!" << endl
; }
} ; int main ( )
{ shape
* pLine
= new line
; shape
* pCircle
= new circle
; pLine
-> draw ( ) ; pCircle
-> draw ( ) ; if ( pCircle
) delete pCircle
; if ( pLine
) delete pLine
; return 0 ;
}
動多態代碼運行結果: 3. 封裝 隱藏對象的屬性和實現細節,僅僅對外提供接口和方法。 重載:寫一個與已有函數同名但是參數表不同的函數 覆蓋:派生類中改寫基類中的虛函數
虛函數與純虛函數
虛函數使用的其核心目的是通過基類訪問派生類定義的函數(virtual),實現了多態的機制。在使用虛函數的過程中存在兩個常見錯誤:無意的重寫(具有相同的簽名的成員函數)、虛函數簽名不匹配(函數名、參數列表 或 const 屬性不一樣)。針對上述情況,C++ 11 增加了兩個繼承控制關鍵字:override 和 final。override:防止虛函數的覆蓋,保證基類的虛函數在派生類重載;final:防止基類的虛函數在派生類重載,保證虛函數的覆蓋。
class Base {
public : virtual void display ( int x
) ;
} ; class Derived : public Base {
public : virtual void display ( int x
) const override ;
}
class Base {
public : virtual void display ( int x
) final ;
} ; class Derived : public Base {
public : virtual void display ( int x
) override ;
} ;
當想要在基類中定義虛函數,以便在派生類中重新定義該函數更好地適用于對象,但是您在基類中又不能對虛函數給出有意義的實現,這個時候就會用到純虛函數(virtual 返回類型 函數名() = 0;)。
虛繼承
在上圖菱形繼承中,類 A 中的成員變量和成員函數繼承到類 D 中變成了兩份,在一個派生類中保留間接基類的多份同名成員,雖然可以在不同的成員變量中分別存放不同的數據,但大多數情況下這是多余的:因為保留多份成員變量不僅占用較多的存儲空間,還容易產生命名沖突。為了解決多繼承時的命名沖突和冗余數據問題,C++ 提出了虛繼承(繼承方式前面加上 virtual 關鍵字),使得在派生類中只保留一份間接基類的成員。虛繼承機制下,不論虛基類在繼承體系中出現了多少次,在派生類中都只包含一份虛基類的成員。
虛指針與虛表
C++實現虛函數的方法是:為每個類對象添加一個隱藏成員,隱藏成員保存了一個指針,這個指針叫虛表指針(vptr),它指向一個虛函數表(vtbl),位于該類的首地址(系統為32位時地址為前4個字節,系統為64位時地址為前8個字節)。 基類對象包含一個虛表指針,指向基類的虛函數表,派生類對象也將包含一個虛表指針,指向派生類虛函數表,如果派生類重寫了基類的虛方法,該派生類虛函數表將保存重寫的虛函數的地址,而不是基類的虛函數地址,如果基類中的虛方法沒有在派生類中重寫,那么派生類將繼承基類中的虛方法,而且派生類中虛函數表將保存基類中未被重寫的虛函數的地址,但如果派生類中定義了新的虛方法,則該虛函數的地址也將被添加到派生類虛函數表中。 例子:
# include <iostream>
int main ( )
{ class A { public : virtual void vfunc ( ) { std
:: cout
<< "A::vfunc" << std
:: endl
; } virtual void vfuncA ( ) { std
:: cout
<< "A::vfuncA" << std
:: endl
; } public : double m_data
= 1.57 ; } ; class B : public A { public : virtual void vfunc ( ) { std
:: cout
<< "B::vfunc" << std
:: endl
; } virtual void vfuncB ( ) { std
:: cout
<< "B::vfuncB" << std
:: endl
; } public : double m_data
= 3.14 ; } ; typedef void ( * Fun
) ( void ) ; A aObj
; B bObj
; A
* abObj
= new B
; Fun pFun
= NULL ;
# if _WIN64 std
:: cout
<< "aObj實例對象的數據地址:" << & ( aObj
. m_data
) << std
:: endl
; std
:: cout
<< "A虛函數表的地址:" << ( long long int * ) * ( long long int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數表的第一個函數地址:" << ( long long int * ) * ( long long int * ) * ( long long int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數表的第二個函數地址:" << ( long long int * ) * ( ( long long int * ) * ( long long int * ) ( & aObj
) + 1 ) << std
:: endl
; std
:: cout
<< std
:: endl
; std
:: cout
<< "bObj實例對象的數據地址:" << & ( bObj
. m_data
) << std
:: endl
; std
:: cout
<< "B虛函數表的地址:" << ( long long int * ) * ( long long int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數表的第一個函數地址:" << ( long long int * ) * ( long long int * ) * ( long long int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數表的第二個函數地址:" << ( long long int * ) * ( ( long long int * ) * ( long long int * ) ( & bObj
) + 1 ) << std
:: endl
; std
:: cout
<< "B虛函數表的第三個函數地址:" << ( long long int * ) * ( ( long long int * ) * ( long long int * ) ( & bObj
) + 2 ) << std
:: endl
; std
:: cout
<< std
:: endl
; pFun
= ( Fun
) * ( long long int * ) * ( long long int * ) ( & aObj
) ; pFun ( ) ; pFun
= ( Fun
) * ( ( long long int * ) * ( long long int * ) ( & aObj
) + 1 ) ; pFun ( ) ; std
:: cout
<< std
:: endl
; pFun
= ( Fun
) * ( long long int * ) * ( long long int * ) ( & bObj
) ; pFun ( ) ; pFun
= ( Fun
) * ( ( long long int * ) * ( long long int * ) ( & bObj
) + 1 ) ; pFun ( ) ; pFun
= ( Fun
) * ( ( long long int * ) * ( long long int * ) ( & bObj
) + 2 ) ; pFun ( ) ; std
:: cout
<< std
:: endl
; aObj
. vfunc ( ) ; abObj
-> vfunc ( ) ; # else std
:: cout
<< "aObj實例對象的數據地址:" << & ( aObj
. m_data
) << std
:: endl
; std
:: cout
<< "A虛函數表的地址:" << ( int * ) * ( int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數表的第一個函數地址:" << ( int * ) * ( int * ) * ( int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數表的第二個函數地址:" << ( int * ) * ( ( int * ) * ( int * ) ( & aObj
) + 1 ) << std
:: endl
; std
:: cout
<< std
:: endl
; std
:: cout
<< "bObj實例對象的數據地址:" << & ( bObj
. m_data
) << std
:: endl
; std
:: cout
<< "B虛函數表的地址:" << ( int * ) * ( int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數表的第一個函數地址:" << ( int * ) * ( int * ) * ( int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數表的第二個函數地址:" << ( int * ) * ( ( int * ) * ( int * ) ( & bObj
) + 1 ) << std
:: endl
; std
:: cout
<< "B虛函數表的第三個函數地址:" << ( int * ) * ( ( int * ) * ( int * ) ( & bObj
) + 2 ) << std
:: endl
; std
:: cout
<< std
:: endl
; pFun
= ( Fun
) * ( int * ) * ( int * ) ( & aObj
) ; pFun ( ) ; pFun
= ( Fun
) * ( ( int * ) * ( int * ) ( & aObj
) + 1 ) ; pFun ( ) ; std
:: cout
<< std
:: endl
; pFun
= ( Fun
) * ( int * ) * ( int * ) ( & bObj
) ; pFun ( ) ; pFun
= ( Fun
) * ( ( int * ) * ( int * ) ( & bObj
) + 1 ) ; pFun ( ) ; pFun
= ( Fun
) * ( ( int * ) * ( int * ) ( & bObj
) + 2 ) ; pFun ( ) ; std
:: cout
<< std
:: endl
; aObj
. vfunc ( ) ; abObj
-> vfunc ( ) ;
# endif delete abObj
; return 0 ;
}
運行結果: 上例虛函數存儲方式如圖所示:
總結
以上是生活随笔 為你收集整理的C++虚函数 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。