欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++ typeid 和虛函數(shù)詳解

 更新時(shí)間:2021年09月08日 15:38:53   作者:chls  
這篇文章主要介紹了c++ typeid 和虛函數(shù)的使用,幫助大家更好的理解和使用c++,感興趣的朋友可以了解下,希望能夠給你帶來(lái)幫助

typeid 和虛函數(shù)

前面咱們講到 typeid 的操作返回值是 type_info 對(duì)象的引用,然后輸出返回值的地址是相同的,測(cè)試代碼如下:

#include <iostream>
#include <functional>
using namespace std;
class Base{
public:
    virtual 
	void test(){
		cout << "Base::test" << endl;
	}
};
class Derived : public Base{
public:
    void test(){
		cout << "Derived::test" << endl;
	}
	virtual 
	~Derived(){
		cout << "Derived::~Derived" << endl;
	}
};
int main()
{
	Base* pBase = new Base();
	Base* pBase2 = new Derived();
	Derived* pDerive = new Derived();
	//typeid(pBase2) 和 typeid(pDerive) 返回地址相同
    cout << "typeid(pBase2) = " << &typeid(*pBase2) <<  " typeid(pDerive) = "<< &typeid(*pDerive) << endl;
    return 0;
}

output信息:

typeid(pBase2) = 0x55dd724c6d48 typeid(pDerive) = 0x55dd724c6d48

也就是說(shuō),0x55dd724c6d48 就是 Derived 類編譯之后的類標(biāo)識(shí)(type_info)數(shù)據(jù)信息!是否真的如此,咱們可以添加一下代碼測(cè)試:

int main()
{
	Base* pBase = new Base();
	Base* pBase2 = new Derived();
	Derived* pDerive = new Derived();
	//typeid(pBase2) 和 typeid(pDerive) 返回地址相同
    cout << "typeid(pBase2) = " << &typeid(*pBase2) <<  " typeid(pDerive) = "<< &typeid(*pDerive) << endl;
	//class Base type_info 地址
	cout << "typeid(Base) = " << &typeid(Base)  << endl;
	//class Derive type_info 地址
	cout << "typeid(Derived) = " << &typeid(Derived)  << endl;
    //指針類型推導(dǎo)
    cout << "point ---- typeid(pBase2) = " << &typeid(pBase2) <<  " typeid(pDerive) = "<< &typeid(pDerive) << endl;
    return 0;
}

ouput信息:

typeid(pBase2) = 0x562309345d48 typeid(pDerive) = 0x562309345d48
typeid(Base) = 0x562309345d60
typeid(Derived) = 0x562309345d48
point ---- typeid(pBase2) = 0x562309345d28 typeid(pDerive) = 0x562309345d08

可以看到,Derived 類的 type_info 信息的地址就是 0x558a4dec7d48 !要注意的一點(diǎn):直接對(duì)指針類型進(jìn)行操作,并不能返回正確的原始類型。

好了嘛,那 typeid 到底是咋從虛函數(shù)表找到這個(gè)地址的呢?如果大家看過(guò)我之前的 深入理解new[]和delete[]_master-計(jì)算機(jī)科學(xué)專欄-CSDN博客 一文,應(yīng)該就能夠想到是不是C++編譯器對(duì)虛函數(shù)表進(jìn)行構(gòu)造的過(guò)程中是不是也一樣,做了地址偏移呢?

咱們看看上面代碼的匯編信息:

通過(guò)查看匯編信息,我們得到以下結(jié)論:

虛函數(shù)表中確實(shí)存有typeinfo信息(第一個(gè)虛函數(shù)的地址偏移 -1 即是)typeinfo信息是區(qū)分指針類型是的(指針類型有前綴P,例如 P4Base、P7Derived)

然后,我們仔細(xì)觀察四個(gè) typeinfo 類(Derived*、 Base*、Derived、Base),每個(gè)typeinfo 類都有一個(gè)虛函數(shù)表,繼承自 vtable for __cxxabiv1::******* ,后面的信息會(huì)不一樣。這里對(duì)該信息做一下簡(jiǎn)單說(shuō)明:

對(duì)于啟用了 RTTI 的類來(lái)說(shuō)會(huì)繼承 __cxxabiv1 里的某個(gè)類所有的基礎(chǔ)類(沒(méi)有父類的類)都繼承于_class_type_info所有的基礎(chǔ)類指針都繼承自 __pointer_type_info所有的單一繼承類都繼承自 __si_class_type_info所有的多繼承類都繼承自 __vmi_class_type_info

以typeinfo for Derived為例:

然后是指向存儲(chǔ)類型名字的指針,
如果有繼承關(guān)系,則最后是指向父類的 typeinfo 的記錄。

所以,如果是正常調(diào) typeinfo 基類(_class_type_info、__pointer_type_info、__si_class_type_info、__vmi_class_type_info)的方法,應(yīng)該會(huì)動(dòng)態(tài)調(diào)到 type_info 的繼承類 (typeinfo for Derived*、typeinfo for Base*、typeinfo for Derived、typeinfo for Base)的方法。

但是,typeid 操作指針類型時(shí)并不是這樣,說(shuō)明C++編譯器底層有特殊處理!

調(diào)試以下代碼:

    cout << typeid(*pBase2).name();
    cout << typeid(*pDerive).name();
    cout << typeid(pBase2).name();
    cout << typeid(pDerive).name();

通過(guò)匯編信息,可以看到這里并沒(méi)有做任何動(dòng)態(tài)調(diào)用的邏輯,而是直接返回該指針類型的typeinfo信息,這也就解釋了為什么 typeid 操作指針和操作對(duì)象的結(jié)果不一樣!

那么我們?cè)谑褂胻ypeid時(shí),如果要獲取到真實(shí)對(duì)象類型,應(yīng)該要將指針去掉!

為了驗(yàn)證我們前面的結(jié)論: 虛函數(shù)表中確實(shí)存有typeinfo信息(第一個(gè)虛函數(shù)的地址偏移 -1 即是),咱們可以直接通過(guò)指針的方式操作虛函數(shù)表!

測(cè)試代碼如下:

#include <iostream>
#include <functional>
using namespace std;
class Base{
public:
    virtual 
	void test(){
		cout << "Base::test" << endl;
	}
};
class Derived : public Base{
public:
    void test(){
		cout << "Derived::test" << endl;
	}
	virtual 
	~Derived(){
		cout << "Derived::~Derived" << endl;
	}
};
typedef void (*FUNPTR)();
type_info* getTypeInfo(unsigned long ** vtbl){
	type_info* typeinfo = (type_info*)((unsigned long)vtbl[-1]);
	return typeinfo;
}
void visitVtbl(unsigned long ** vtbl, int count)
{
    cout << vtbl << endl;
    cout << "\t[-1]: " << (unsigned long)vtbl[-1] << endl;
    typedef void (*FUNPTR)();
    for (int i = 0; vtbl[i] && i < count; ++i)
    {
        cout << "\t[" << i << "]: " << vtbl[i] << " -> ";
        FUNPTR func = (FUNPTR)vtbl[i];
        func();
    }
}
int main()
{
	Base* pBase = new Base();
	Base* pBase2 = new Derived();
	Derived* pDerive = new Derived();
	//這里去遍歷虛函數(shù)表
	visitVtbl((unsigned long **)*(unsigned long **)pBase2, 2);
	//獲取虛函數(shù)表-1位置的typeinfo地址
	cout << "pDerive = " << getTypeInfo((unsigned long **)*(unsigned long **)pDerive) << " "
	<< getTypeInfo((unsigned long **)*(unsigned long **)pDerive)->name() << endl;
	//獲取虛函數(shù)表-1位置的typeinfo地址
	cout << "pBase2 = " << getTypeInfo((unsigned long **)*(unsigned long **)pBase2) << " "
	<< getTypeInfo((unsigned long **)*(unsigned long **)pBase2)->name() << endl;
    return 0;
}

這里要注意的一點(diǎn)是,遍歷虛函數(shù)表 visitVtbl 方法的第2個(gè)參數(shù),自己控制不要越界,此外,還要注意調(diào)用的順序,如果先調(diào)用了虛析構(gòu)函數(shù),會(huì)導(dǎo)致內(nèi)存錯(cuò)誤!

output信息:

0x5620022edd10
[-1]: 94695475567936
[0]: 0x5620022eb5fa -> Derived::test
[1]: 0x5620022eb636 -> Derived::~Derived
pDerive = 0x5620022edd40 7Derived
pBase2 = 0x5620022edd40 7Derived

通過(guò)直接訪問(wèn)虛函數(shù)表-1位置,我們可以看到輸出的日志信息與我們前面的結(jié)論是一致的!也即是C++編譯器給我們做了偏移操作(在-1的位置存儲(chǔ)了type_info信息,實(shí)例化對(duì)象中的虛函數(shù)表地址是偏移之后的地址)。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • C/C++程序編譯流程詳解

    C/C++程序編譯流程詳解

    C/C++程序編譯過(guò)程包括下面4個(gè)階段:1.預(yù)處理,2.編譯,3.匯編,4.鏈接。下面我們就來(lái)詳細(xì)分析下這幾個(gè)階段。
    2016-04-04
  • 利用C語(yǔ)言解決八皇后問(wèn)題以及解析

    利用C語(yǔ)言解決八皇后問(wèn)題以及解析

    這篇文章主要給大家介紹了關(guān)于利用C語(yǔ)言解決八皇后問(wèn)題以及解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • C語(yǔ)言中send()函數(shù)和sendto()函數(shù)的使用方法

    C語(yǔ)言中send()函數(shù)和sendto()函數(shù)的使用方法

    這篇文章主要介紹了C語(yǔ)言中send()函數(shù)和sendto()函數(shù)的使用方法,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-09-09
  • C++引用的使用與const修飾符

    C++引用的使用與const修飾符

    這篇文章介紹了C++引用使用與const修飾符,引用是給已經(jīng)定義的變量一個(gè)別名,可以簡(jiǎn)單理解成同一個(gè)變量的昵稱,既然是昵稱或者是別名,顯然它和原本的變量名有著同樣的效力,所以我們對(duì)別名進(jìn)行修改,原本的變量值也一樣會(huì)發(fā)生變化,下面來(lái)看看詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-11-11
  • C++生成隨機(jī)數(shù)的實(shí)現(xiàn)代碼

    C++生成隨機(jī)數(shù)的實(shí)現(xiàn)代碼

    這篇文章主要介紹了C++生成隨機(jī)數(shù)的實(shí)現(xiàn)代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • 簡(jiǎn)單掌握C++編程中的while與do-while循環(huán)語(yǔ)句使用

    簡(jiǎn)單掌握C++編程中的while與do-while循環(huán)語(yǔ)句使用

    這篇文章主要介紹了C++編程中的while與do-while循環(huán)語(yǔ)句使用,區(qū)別就是while是先判斷再執(zhí)行,而do-while是先執(zhí)行再判斷,需要的朋友可以參考下
    2016-01-01
  • C語(yǔ)言修煉之路函數(shù)篇真題訓(xùn)練下

    C語(yǔ)言修煉之路函數(shù)篇真題訓(xùn)練下

    函數(shù)是一組一起執(zhí)行一個(gè)任務(wù)的語(yǔ)句。每個(gè) C 程序都至少有一個(gè)函數(shù),即主函數(shù) main() ,所有簡(jiǎn)單的程序都可以定義其他額外的函數(shù)
    2022-03-03
  • C語(yǔ)言實(shí)現(xiàn)abs和fabs絕對(duì)值

    C語(yǔ)言實(shí)現(xiàn)abs和fabs絕對(duì)值

    這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)abs和fabs絕對(duì)值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • opencv實(shí)現(xiàn)圖片與視頻中人臉檢測(cè)功能

    opencv實(shí)現(xiàn)圖片與視頻中人臉檢測(cè)功能

    這篇文章主要為大家詳細(xì)介紹了opencv實(shí)現(xiàn)圖片與視頻中人臉檢測(cè)功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Pthread?并發(fā)編程線程自底向上深入解析

    Pthread?并發(fā)編程線程自底向上深入解析

    這篇文章主要為大家介紹了Pthread?并發(fā)編程線程自底向上深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論