生活随笔
收集整理的這篇文章主要介紹了
第九天2017/04/18(4、非虚继承、虚继承的本质 / sizeof)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
case1:非虛繼承
上面是代碼的繼承圖
#include<iostream>
using namespace std;
class CAnimal
{
public:
virtual void EatFood(
string strSomething) =
0;
virtual void Drink(
string strSomething) =
0;
};
class CMammal :
public CAnimal
{
public:
virtual void EatFood(
string strSomething){
cout <<
"CMammal::EatFood()" << endl;}
virtual void Drink(
string strSomething){
cout <<
"CMammal::Drink()" << endl;}
};
class CAfricaAnimal :
public CAnimal
{
public:
virtual void EatFood(
string strSomething){
cout <<
"CAfricaAnimal::EatFood()" << endl;}
virtual void Drink(
string strSomething){
cout <<
"CAfricaAnimal::Drink()" << endl;}
};
class CLion :
public CMammal,
public CAfricaAnimal
{
public:
virtual void RunCrazily(){
cout <<
"CLion::RunCrazily()" << endl;}
};
int main()
{CLion sinba;
return 0;
}通過(guò)在
return 0;語(yǔ)句前添加一個(gè)斷點(diǎn),查看sinba的值可以得到如下結(jié)果:
【疑問(wèn)】但是,在Clion中定義了一個(gè)Clion自己的虛函數(shù)RunCrazily,可是從“局部變量”調(diào)試窗口中并沒(méi)有看到RunCrazily。那么虛函數(shù)RunCrazily到底存放在哪個(gè)虛函數(shù)表中呢?
【答案】通過(guò)網(wǎng)上查閱資料,得知:虛函數(shù)RunCrazily其實(shí)是存放在第一張?zhí)摫碇械?/strong>。
CLion聲明的sinba變量中的虛表指針的圖示,見(jiàn)下:
【已知:對(duì)象】
【得到:該對(duì)象中的所有的函數(shù)的入口地址】可以通過(guò):子類對(duì)象,如CLion sinba;
進(jìn)一步找到:子類對(duì)象中的所有的虛表指針;
進(jìn)而通過(guò)虛表指針,找到:每一個(gè)虛表中存放的虛函數(shù)的入口地址,(入口地址即函數(shù)名);
找到了虛函數(shù)入口地址后,可以:通過(guò)類型轉(zhuǎn)換,轉(zhuǎn)換成相對(duì)應(yīng)的函數(shù)指針,再進(jìn)行函數(shù)的調(diào)用。以下是測(cè)試代碼:
int main()
{CLion sinba;
typedef void (*FUN)(); FUN run_crazily; run_crazily = (FUN)(*((
int*)*(
int*)(&sinba)+
2)); (*run_crazily)();
return 0;
}【解析】關(guān)于上面復(fù)雜指針轉(zhuǎn)換的分析:(
int*)(&sinba)得到的是一個(gè)指向第一張?zhí)摫淼刂返闹羔?#xff0c;即虛表指針vptr_CMammal。對(duì)該地址取內(nèi)容:*(
int*)(&sinba) 可以得到第一張?zhí)摫淼氖椎刂?#xff0c;但應(yīng)該將該值轉(zhuǎn)換成
int*:(
int*)*(
int*)(&sinba),使得每次指針+
1操作都內(nèi)存向前移動(dòng)
4字節(jié)。因?yàn)樘摫碇性卣?span id="ze8trgl8bvbq" class="hljs-number">4字節(jié),現(xiàn)在把這個(gè)指針+
2: (
int*)*(
int*)(&sinba)+
2,得到指向表中第三項(xiàng)的指針,即指向CLion::RunCrazily()函數(shù)的指針。再用*對(duì)(
int*)*(
int*)(&sinba)+
2指針取內(nèi)容*((
int*)*(
int*)(&sinba)+
2),得到CLion::RunCrazily()函數(shù)的入口地址,函數(shù)的入口地址相當(dāng)于函數(shù)名RunCrazily。但是:得到函數(shù)的入口地址后還不能直接調(diào)用,應(yīng)該將該函數(shù)入口地址轉(zhuǎn)換成相應(yīng)的函數(shù)原型(FUN)(*((
int*)*(
int*)(&sinba)+
2))。現(xiàn)在可以調(diào)用了
===========================================================================
【擴(kuò)展】
如果要得到第二張?zhí)摫碇械哪稠?xiàng)函數(shù)可以按照上面分析的方法調(diào)用到:(int*)(&sinba):一個(gè)指向第一張?zhí)摫淼刂返闹羔?這里必須轉(zhuǎn)換成int*,方便在指針移動(dòng)的時(shí)候可以向前移動(dòng)
4字節(jié),也就是一個(gè)虛表指針的長(zhǎng)度)(int*)(&sinba)+
1:一個(gè)指向第二張?zhí)摫淼刂返闹羔?((int*)(&sinba)+
1):對(duì)上面的地址取內(nèi)容,得到第二張?zhí)摫淼氖椎刂?int*)*((int*)(&sinba)+
1)+
1:第二張?zhí)摫碇械诙?xiàng)的地址(這里也必須轉(zhuǎn)換成int*再+
1,原因同上面的)*((int*)*((int*)(&sinba)+
1)+
1):對(duì)上面的地址取內(nèi)容,得到第二張?zhí)摫碇械诙?xiàng)的入口地址(FUN)*((int*)(&sinba)+
1)+
1)):對(duì)上面的地址強(qiáng)制轉(zhuǎn)換成相應(yīng)原型的函數(shù)指針
上面幾句話總結(jié):要得到第m+
1張?zhí)摫碇械牡趎+
1個(gè)函數(shù)的入口地址為*((int*)*((int*)(&sinba)+m)+n),在使用之前,再進(jìn)行類型轉(zhuǎn)換(FUN)*((int*)(&sinba)+m)+n))轉(zhuǎn)換成相對(duì)應(yīng)的函數(shù)指針,用上面的函數(shù)指針進(jìn)行調(diào)用。
【綜上所述】假設(shè)A、B類中都有virtual函數(shù),C類多繼承了A類、B類(假設(shè)C類先繼承了A類),且C類中也有virtual函數(shù),那么:
C類定義的對(duì)象中,有A類、B類的虛函數(shù)指針,(C類中獨(dú)有的虛函數(shù)指針在A類的虛函數(shù)表中)。
========================================================================================
case2:虛繼承
總結(jié)
以上是生活随笔為你收集整理的第九天2017/04/18(4、非虚继承、虚继承的本质 / sizeof)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。