生活随笔
收集整理的這篇文章主要介紹了
C++虚函数
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
C++三大特性
1.繼承 一個(gè)對(duì)象直接使用另一個(gè)對(duì)象的屬性和方法,減少重復(fù)的代碼,增加了類的耦合性。但父類通常定義了子類的部分行為,父類的改變可能影響子類的行為。 2. 多態(tài) C++中有靜多態(tài)(編譯時(shí)多態(tài))和動(dòng)多態(tài)(運(yùn)行時(shí)多態(tài))兩種。靜多態(tài)主要通過(guò)模板來(lái)實(shí)現(xiàn),宏也是實(shí)現(xiàn)靜多態(tài)的一種途徑,所有調(diào)用在編譯期就可確定,因此它是靜態(tài)的;動(dòng)多態(tài)在C++中是通過(guò)虛函數(shù)實(shí)現(xiàn)的,即在基類中存在一些接口(一般為純虛函數(shù)),子類必須重載這些接口,通過(guò)使用基類的指針或者引用指向子類的對(duì)象,通過(guò)基類指針實(shí)現(xiàn)調(diào)用子類對(duì)應(yīng)的函數(shù)的功能,函數(shù)調(diào)用在執(zhí)行期才能進(jìn)行確定,所以它是動(dòng)態(tài)的。
# 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 ;
}
動(dòng)多態(tài)代碼運(yùn)行結(jié)果: 3. 封裝 隱藏對(duì)象的屬性和實(shí)現(xiàn)細(xì)節(jié),僅僅對(duì)外提供接口和方法。 重載:寫(xiě)一個(gè)與已有函數(shù)同名但是參數(shù)表不同的函數(shù) 覆蓋:派生類中改寫(xiě)基類中的虛函數(shù)
虛函數(shù)與純虛函數(shù)
虛函數(shù)使用的其核心目的是通過(guò)基類訪問(wèn)派生類定義的函數(shù)(virtual),實(shí)現(xiàn)了多態(tài)的機(jī)制。在使用虛函數(shù)的過(guò)程中存在兩個(gè)常見(jiàn)錯(cuò)誤:無(wú)意的重寫(xiě)(具有相同的簽名的成員函數(shù))、虛函數(shù)簽名不匹配(函數(shù)名、參數(shù)列表 或 const 屬性不一樣)。針對(duì)上述情況,C++ 11 增加了兩個(gè)繼承控制關(guān)鍵字:override 和 final。override:防止虛函數(shù)的覆蓋,保證基類的虛函數(shù)在派生類重載;final:防止基類的虛函數(shù)在派生類重載,保證虛函數(shù)的覆蓋。
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 ;
} ;
當(dāng)想要在基類中定義虛函數(shù),以便在派生類中重新定義該函數(shù)更好地適用于對(duì)象,但是您在基類中又不能對(duì)虛函數(shù)給出有意義的實(shí)現(xiàn),這個(gè)時(shí)候就會(huì)用到純虛函數(shù)(virtual 返回類型 函數(shù)名() = 0;)。
虛繼承
在上圖菱形繼承中,類 A 中的成員變量和成員函數(shù)繼承到類 D 中變成了兩份,在一個(gè)派生類中保留間接基類的多份同名成員,雖然可以在不同的成員變量中分別存放不同的數(shù)據(jù),但大多數(shù)情況下這是多余的:因?yàn)楸A舳喾莩蓡T變量不僅占用較多的存儲(chǔ)空間,還容易產(chǎn)生命名沖突。為了解決多繼承時(shí)的命名沖突和冗余數(shù)據(jù)問(wèn)題,C++ 提出了虛繼承(繼承方式前面加上 virtual 關(guān)鍵字),使得在派生類中只保留一份間接基類的成員。虛繼承機(jī)制下,不論虛基類在繼承體系中出現(xiàn)了多少次,在派生類中都只包含一份虛基類的成員。
虛指針與虛表
C++實(shí)現(xiàn)虛函數(shù)的方法是:為每個(gè)類對(duì)象添加一個(gè)隱藏成員,隱藏成員保存了一個(gè)指針,這個(gè)指針叫虛表指針(vptr),它指向一個(gè)虛函數(shù)表(vtbl),位于該類的首地址(系統(tǒng)為32位時(shí)地址為前4個(gè)字節(jié),系統(tǒng)為64位時(shí)地址為前8個(gè)字節(jié))。 基類對(duì)象包含一個(gè)虛表指針,指向基類的虛函數(shù)表,派生類對(duì)象也將包含一個(gè)虛表指針,指向派生類虛函數(shù)表,如果派生類重寫(xiě)了基類的虛方法,該派生類虛函數(shù)表將保存重寫(xiě)的虛函數(shù)的地址,而不是基類的虛函數(shù)地址,如果基類中的虛方法沒(méi)有在派生類中重寫(xiě),那么派生類將繼承基類中的虛方法,而且派生類中虛函數(shù)表將保存基類中未被重寫(xiě)的虛函數(shù)的地址,但如果派生類中定義了新的虛方法,則該虛函數(shù)的地址也將被添加到派生類虛函數(shù)表中。 例子:
# 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實(shí)例對(duì)象的數(shù)據(jù)地址:" << & ( aObj
. m_data
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的地址:" << ( long long int * ) * ( long long int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的第一個(gè)函數(shù)地址:" << ( long long int * ) * ( long long int * ) * ( long long int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的第二個(gè)函數(shù)地址:" << ( long long int * ) * ( ( long long int * ) * ( long long int * ) ( & aObj
) + 1 ) << std
:: endl
; std
:: cout
<< std
:: endl
; std
:: cout
<< "bObj實(shí)例對(duì)象的數(shù)據(jù)地址:" << & ( bObj
. m_data
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的地址:" << ( long long int * ) * ( long long int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第一個(gè)函數(shù)地址:" << ( long long int * ) * ( long long int * ) * ( long long int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第二個(gè)函數(shù)地址:" << ( long long int * ) * ( ( long long int * ) * ( long long int * ) ( & bObj
) + 1 ) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第三個(gè)函數(shù)地址:" << ( 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實(shí)例對(duì)象的數(shù)據(jù)地址:" << & ( aObj
. m_data
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的地址:" << ( int * ) * ( int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的第一個(gè)函數(shù)地址:" << ( int * ) * ( int * ) * ( int * ) ( & aObj
) << std
:: endl
; std
:: cout
<< "A虛函數(shù)表的第二個(gè)函數(shù)地址:" << ( int * ) * ( ( int * ) * ( int * ) ( & aObj
) + 1 ) << std
:: endl
; std
:: cout
<< std
:: endl
; std
:: cout
<< "bObj實(shí)例對(duì)象的數(shù)據(jù)地址:" << & ( bObj
. m_data
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的地址:" << ( int * ) * ( int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第一個(gè)函數(shù)地址:" << ( int * ) * ( int * ) * ( int * ) ( & bObj
) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第二個(gè)函數(shù)地址:" << ( int * ) * ( ( int * ) * ( int * ) ( & bObj
) + 1 ) << std
:: endl
; std
:: cout
<< "B虛函數(shù)表的第三個(gè)函數(shù)地址:" << ( 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 ;
}
運(yùn)行結(jié)果: 上例虛函數(shù)存儲(chǔ)方式如圖所示:
總結(jié)
以上是生活随笔 為你收集整理的C++虚函数 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。