C++ RTTI的typeid()操作符

首先為什么typeid()我們把它叫做操作符示括,而不是一個(gè)函數(shù)呢秘通,答案是因?yàn)樗筒皇且粋€(gè)函數(shù)悠就;熟悉C/C++的童鞋立馬就會(huì)猜,它應(yīng)該是一個(gè)宏充易,宏展開后可能是一段代碼梗脾,或者調(diào)用另一個(gè)函數(shù);我們翻遍C++的代碼庫(kù)也找不到typeid的定義盹靴,也就是說它既不是一個(gè)宏也不是一個(gè)函數(shù)炸茧。

它是C++內(nèi)部定義的一個(gè)運(yùn)算符號(hào),屬于C++語言本身的特性稿静,不是庫(kù)梭冠,所以我們叫它操作符號(hào);這和C/C++的另一個(gè)操作符sizeof()是一樣的行為改备。他們都是編譯器負(fù)責(zé)解釋翻譯控漠,運(yùn)行時(shí)刻看不到他們的影子,在編譯后的匯編代碼里就找不到他們的影子了悬钳。


以代碼為例:

#include <stdio.h>
#include <string.h>
#include <typeinfo>

void foo() {
    long a = sizeof(int);
    const std::type_info & b = typeid(int);
    printf("size=%d, name=[%s]\n", a, b.name());
}

int main(int argc, char * argv[]) {
    foo();
    return 0;
}

運(yùn)行結(jié)果為:
size=4, name=[i]

在看聲稱的foo()匯編代碼:

_Z3foov:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    $4, -16(%rbp)
    movq    $_ZTIi, -8(%rbp)
    movq    -8(%rbp), %rax
    movq    %rax, %rdi
    call    _ZNKSt9type_info4nameEv
    movq    %rax, %rdx
    movq    -16(%rbp), %rax
    movq    %rax, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    leave
    ret

這是完整的foo()匯編代碼盐捷,我們只關(guān)注其中的兩條指令:

movq    $4, -16(%rbp)
movq    $_ZTIi, -8(%rbp)

這兩條指令對(duì)應(yīng)的源代碼分別是:

long a = sizeof(int);
const std::type_info & b = typeid(int);

很神奇吧,生成的匯編指令比原高級(jí)語言C++還要簡(jiǎn)單默勾,第一條指令把int的大小4直接算出來賦給變量a碉渡,第二條指令也是把一個(gè)常量直接賦給變量b。因?yàn)榫幾g器在翻譯過程中當(dāng)碰到操作符sizeof和typeid的時(shí)候母剥,它就直接完成了這部分的計(jì)算功能滞诺,把結(jié)果賦得輸出。

另外我們?cè)谏傻膮R編代碼文件找不到常量$_ZTIi的定義环疼,這個(gè)常量定義在C++運(yùn)行庫(kù)里面习霹,所以也就是C++知道有這些定義才能直接用。也就是說C++編譯器生成的目標(biāo)文件依賴于C++編譯器提供的部分功能函數(shù)炫隶。

說到這兒淋叶,有些童鞋估計(jì)可能會(huì)想起來以前做項(xiàng)目的時(shí)候遇到過的問題,當(dāng)一個(gè)大的工程由多團(tuán)隊(duì)多人開發(fā)時(shí)等限,各自模塊驗(yàn)證都沒有問題爸吮,集成的時(shí)候總是莫名其妙的錯(cuò)誤芬膝,crash等等,原因當(dāng)然很多了形娇,而如果不同的團(tuán)隊(duì)使用不同的編譯器生成目標(biāo)代碼锰霜,運(yùn)行庫(kù)時(shí),當(dāng)這些模塊集成到一起的時(shí)候可能就會(huì)由于不同的編譯器對(duì)各自運(yùn)行庫(kù)的需求不一致桐早,導(dǎo)致莫名錯(cuò)誤癣缅。


接著討論typeid()如何工作的?typeid的功能是用來得到一個(gè)對(duì)象的類型定義哄酝,據(jù)此判斷兩個(gè)類型是否一致友存。

std::type_info 是在/usr/include/c++/4.4.4/typeinfo里面定義的一個(gè)類,這個(gè)類只有一個(gè)成員const char *__name陶衅,加上一個(gè)虛函數(shù)表指針std::type_info的實(shí)際內(nèi)存大小為16屡立。

看其中定義的兩個(gè)函數(shù):
const char* name() const;
返回類型的名字,即成員變量__name的值搀军。

bool operator==(const type_info& __arg) const;
比較兩個(gè)類型是否相同膨俐,它不是比較字符串__name的內(nèi)容值,而是比較兩個(gè)類型的__name是否指向同一個(gè)內(nèi)存罩句,當(dāng)然指向同一塊內(nèi)存必然是內(nèi)存值相同的焚刺。

if (typeid(TYPE1) == typeid(TYPE2)) {
  ...
}
else {
  ...
}

注意既然typeid是在編譯時(shí)刻確定的類型,那么在多態(tài)環(huán)境下门烂,并不能根據(jù)變量指針的實(shí)際類型返回乳愉,而是返回變量的申明類型:

include <stdio.h>
#include <typeinfo>

class A1 {};
class A2 : public A1 {};

void foo(A1 * a1) {
   printf("a1=%s\n", typeid(a1).name());
}

int main(int argc, char * argv[])
{
   A1 * a1 = new A1();
   A2 * a2 = new A2();
   foo(a1);
   foo(a2);
   return 0;
}

運(yùn)行結(jié)果為

a1=P2A1
a1=P2A1

可見foo()打印出來的結(jié)果都是A1而不管實(shí)際參數(shù)a1是一個(gè)A1類型對(duì)象,還是一個(gè)A2類型對(duì)象屯远。


最后我們看一個(gè)class A1和A2的type_info是怎么定義的蔓姚。
函數(shù)foo()如下:

_Z3fooP2A1:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    movl    $_ZTIP2A1, %eax
    movq    %rax, %rdi
    call    _ZNKSt9type_info4nameEv
    movq    %rax, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    leave
    ret

其中$_ZTIP2A1就是類A1的type_info定義信息。

    .weak   _ZTSP2A1
_ZTSP2A1:
    .string "P2A1"
    .weak   _ZTIP2A1
_ZTIP2A1:
    .quad   _ZTVN10__cxxabiv119__pointer_type_infoE+16
    .quad   _ZTSP2A1

_ZTIP2A1的內(nèi)容有兩個(gè)字段氓润,前面我們說過type_info有兩個(gè)成員赂乐,第一個(gè)是指向類type_info虛函數(shù)表的指針薯鳍,第二個(gè)是指向類型字符串名字的指針咖气。

前面我們提到類型int的type_info定義在生成的目標(biāo)文件里找不到,因?yàn)閕nt是系統(tǒng)類型挖滤,C++會(huì)把所有的內(nèi)置類型的type_info組織好定義在庫(kù)里面崩溪,而對(duì)于用戶自定義的類型的type_info,則在定義數(shù)據(jù)類型的時(shí)候生成斩松。

_ZNKSt9type_info4nameEv是type_info::name()函數(shù)的代碼伶唯,因?yàn)楹瘮?shù)name()聲明成為inline類型,所以匯編代碼在此處也可見惧盹。


結(jié)尾
其實(shí)typeid()在做項(xiàng)目過程中很少被直接使用到乳幸,我猜更多的應(yīng)用場(chǎng)景是C++內(nèi)部使用瞪讼,比如dynamic_cast函數(shù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粹断,一起剝皮案震驚了整個(gè)濱河市符欠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瓶埋,老刑警劉巖希柿,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異养筒,居然都是意外死亡曾撤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門晕粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挤悉,“玉大人,你說我怎么就攤上這事巫湘〖夥龋” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵剩膘,是天一觀的道長(zhǎng)衅斩。 經(jīng)常有香客問我,道長(zhǎng)怠褐,這世上最難降的妖魔是什么畏梆? 我笑而不...
    開封第一講書人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮奈懒,結(jié)果婚禮上奠涌,老公的妹妹穿的比我還像新娘。我一直安慰自己磷杏,他們只是感情好溜畅,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著极祸,像睡著了一般慈格。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遥金,一...
    開封第一講書人閱讀 52,682評(píng)論 1 312
  • 那天浴捆,我揣著相機(jī)與錄音,去河邊找鬼稿械。 笑死选泻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播页眯,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼梯捕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了窝撵?” 一聲冷哼從身側(cè)響起科阎,我...
    開封第一講書人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忿族,沒想到半個(gè)月后锣笨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡道批,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年错英,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隆豹。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡椭岩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出璃赡,到底是詐尸還是另有隱情判哥,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布碉考,位于F島的核電站塌计,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏侯谁。R本人自食惡果不足惜锌仅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望墙贱。 院中可真熱鬧热芹,春花似錦、人聲如沸惨撇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽魁衙。三九已至报腔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纺棺,已是汗流浹背榄笙。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祷蝌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓帆卓,卻偏偏與公主長(zhǎng)得像巨朦,于是被迫代替她去往敵國(guó)和親米丘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容