最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

typeid 和虛函數(shù)

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

#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

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

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):直接對指針類型進(jìn)行操作,并不能返回正確的原始類型。

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

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

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

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

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

對于啟用了 RTTI 的類來說會繼承 __cxxabiv1 里的某個類所有的基礎(chǔ)類(沒有父類的類)都繼承于_class_type_info所有的基礎(chǔ)類指針都繼承自 __pointer_type_info所有的單一繼承類都繼承自 __si_class_type_info所有的多繼承類都繼承自 __vmi_class_type_info

以typeinfo for Derived為例:

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

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

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

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

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

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

那么我們在使用typeid時,如果要獲取到真實(shí)對象類型,應(yīng)該要將指針去掉!

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

測試代碼如下:

#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個參數(shù),自己控制不要越界,此外,還要注意調(diào)用的順序,如果先調(diào)用了虛析構(gòu)函數(shù),會導(dǎo)致內(nèi)存錯誤!

output信息:

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

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

總結(jié)

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

相關(guān)文章

  • STl中的排序算法詳細(xì)解析

    STl中的排序算法詳細(xì)解析

    全排序即把所給定范圍所有的元素按照大小關(guān)系順序排列。sort采用的是成熟的"快速排序算法"(目前大部分STL版本已經(jīng)不是采用簡單的快速排序,而是結(jié)合內(nèi)插排序算法)
    2013-09-09
  • 詳解C++11 變參模板

    詳解C++11 變參模板

    這篇文章主要介紹了C++11 變參模板的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++11,感興趣的朋友可以了解下
    2020-08-08
  • C語言 選擇排序算法詳解及實(shí)現(xiàn)代碼

    C語言 選擇排序算法詳解及實(shí)現(xiàn)代碼

    本文主要介紹C語言 選擇排序算法,這里對排序算法做了詳細(xì)說明,并附代碼示例,有需要的小伙伴可以參考下
    2016-08-08
  • vscode工程中c_cpp_properties.json文件作用詳細(xì)說明

    vscode工程中c_cpp_properties.json文件作用詳細(xì)說明

    c_cpp_properties.json是Visual Studio Code的一個配置文件,用于定義C/C++編譯器的路徑、默認(rèn)包含路徑和預(yù)處理器定義,這篇文章主要給大家介紹了關(guān)于vscode工程中c_cpp_properties.json文件作用詳細(xì)說明的相關(guān)資料,需要的朋友可以參考下
    2024-08-08
  • C++ Cartographer加載配置文件過程介紹

    C++ Cartographer加載配置文件過程介紹

    這篇文章主要介紹了Cartographer加載配置文件過程,谷歌優(yōu)秀的激光SLAM開源框架Cartographer算法簡單,但是程序部分太多需要學(xué)習(xí)的地方了,不論是整體框架的結(jié)構(gòu),還是數(shù)據(jù)的使用,都是非常優(yōu)美的
    2023-03-03
  • VS2022配置編譯使用boost庫的實(shí)現(xiàn)

    VS2022配置編譯使用boost庫的實(shí)現(xiàn)

    本文介紹了如何在VS2022中配置和編譯使用Boost庫的步驟,包括下載Boost、解壓、配置環(huán)境變量和編譯等過程,具有一定的參考價值,感興趣的可以了解一下
    2024-12-12
  • C++類和對象補(bǔ)充

    C++類和對象補(bǔ)充

    類是創(chuàng)建對象的模板,一個類可以創(chuàng)建多個對象,每個對象都是類類型的一個變量;創(chuàng)建對象的過程也叫類的實(shí)例化。每個對象都是類的一個具體實(shí)例(Instance),擁有類的成員變量和成員函數(shù)
    2021-10-10
  • 10個步驟Opencv輕松檢測出圖片中條形碼

    10個步驟Opencv輕松檢測出圖片中條形碼

    這篇文章主要為大家詳細(xì)介紹了Opencv輕松檢測出圖片中條形碼的10個步驟,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 利用C語言實(shí)現(xiàn)HashTable

    利用C語言實(shí)現(xiàn)HashTable

    根據(jù)KEY從hashtable中獲取接點(diǎn),步驟是先根據(jù)KEY計算hash值,然后從hashtable中找到指定的接點(diǎn)或者接點(diǎn)鏈表
    2013-09-09
  • 二進(jìn)制、八進(jìn)制?、十進(jìn)制、十六進(jìn)制之間轉(zhuǎn)換的原理詳解

    二進(jìn)制、八進(jìn)制?、十進(jìn)制、十六進(jìn)制之間轉(zhuǎn)換的原理詳解

    本文介紹了進(jìn)制的概念及其在C語言編程中的應(yīng)用,進(jìn)制是進(jìn)位制的簡稱,描述了數(shù)值在不同進(jìn)制下的表示方法,常見的進(jìn)制包括二進(jìn)制、八進(jìn)制和十六進(jìn)制,二進(jìn)制使用0和1表示,八進(jìn)制使用0-7數(shù)字表示,十六進(jìn)制使用0-9和A-F表示,文章還介紹了如何在不同進(jìn)制之間進(jìn)行轉(zhuǎn)換
    2024-11-11

最新評論

宜黄县| 阜阳市| 崇信县| 汉阴县| 长乐市| 南和县| 手游| 两当县| 奈曼旗| 凯里市| 讷河市| 会同县| 德昌县| 莱芜市| 当阳市| 南投县| 酒泉市| 宽城| 韩城市| 绥滨县| 廊坊市| 肇源县| 五原县| 高邮市| 常山县| 长泰县| 双鸭山市| 教育| 马边| 香港 | 江达县| 依安县| 收藏| 上蔡县| 汉寿县| 湘潭市| 永善县| 扶沟县| 师宗县| 长岭县| 南投县|