詳解C++11的std::addressof源碼解析
1、源碼準(zhǔn)備
本文是基于gcc-4.9.0的源代碼進(jìn)行分析,std::addressof是C++11才加入標(biāo)準(zhǔn)的,所以低版本的gcc源碼是沒(méi)有這個(gè)的,建議選擇4.9.0或更新的版本去學(xué)習(xí),不同版本的gcc源碼差異應(yīng)該不小,但是原理和設(shè)計(jì)思想的一樣的,下面給出源碼下載地址
http://ftp.gnu.org/gnu/gcc
2、std::addressof簡(jiǎn)介
std::addressof的作用是獲取一個(gè)對(duì)象的實(shí)際地址,即使這個(gè)對(duì)象的&操作符已被重載。它接受一個(gè)參數(shù),該參數(shù)為要獲得地址的那個(gè)對(duì)象的引用。下面通過(guò)一個(gè)極其簡(jiǎn)單的例子了解一下std::addressof的使用方法
#include <iostream> #include <string> #include <functional> class Test { public: int* operator&() { return &b; } int* a_addr() { return &a; } int* b_addr() { return &b; } private: int a; int b; }; int main(int argc, char* argv[]) { Test t; std::cout << "&t.a:" << t.a_addr() << std::endl; std::cout << "&t.b:" << t.b_addr() << std::endl; std::cout << "&t:" << &t << std::endl; std::cout << "addressof(t):" << std::addressof(t) << std::endl; }
上面的代碼輸出結(jié)果為:
&t.a:0x7ffefcb48eb0
&t.b:0x7ffefcb48eb4
&t:0x7ffefcb48eb4
addressof(t):0x7ffefcb48eb0
在這里正常來(lái)說(shuō)使用&t應(yīng)該取到的是t.a的地址值才對(duì),但是由于我們重載了&運(yùn)算符,所以這里取到了t.b的地址值,但是如果使用了std::addressof,就可以取到正確的值了。
3、std::addressof源碼解析
std::addressof位于libstdc++-v3\include\bits\move.h中
template<typename _Tp> inline _Tp* __addressof(_Tp& __r) _GLIBCXX_NOEXCEPT { return reinterpret_cast<_Tp*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(__r))); } template<typename _Tp> inline _Tp* addressof(_Tp& __r) noexcept { return std::__addressof(__r); }
從代碼中可以看出std::addressof里面調(diào)用了std::__addressof,所以真正起作用的是std::__addressof。__addressof的處理過(guò)程可以分為以下四步:
- 將__r由類(lèi)型_Tp&強(qiáng)制轉(zhuǎn)換為const volatile char&,這樣做有兩個(gè)作用:一是防止后面使用&操作符獲取地址時(shí)觸發(fā)原類(lèi)型(即_Tp)的重載操作(operator&),就像上面那個(gè)例子那樣;二是reinterpret_cast操作符總是可以合法的在原類(lèi)型的基礎(chǔ)上加const或volatile, 但是如果_Tp原來(lái)就帶有const或volatile的話(huà), 通過(guò)reinterpret_cast去掉是不允許的, 因此需要加上const volatile來(lái)避免編譯器報(bào)錯(cuò), 也就是此時(shí)不用再管_Tp是否本來(lái)就帶有const或volatile屬性了。
- 將前面轉(zhuǎn)換得到的結(jié)果強(qiáng)制轉(zhuǎn)換為char&類(lèi)型,此時(shí)如果轉(zhuǎn)換成其它類(lèi)型有可能會(huì)觸發(fā)強(qiáng)制地址對(duì)齊的操作,這樣的話(huà)真實(shí)地址就有可能會(huì)被改變了,最終造成程序錯(cuò)誤。需要注意的是這個(gè)轉(zhuǎn)換過(guò)程使用的是const_cast,可以順便將前面留下的const和volatile屬性給去掉了。
- 使用&符號(hào)將前面的結(jié)果的地址給取出來(lái)(此時(shí)已經(jīng)不會(huì)觸發(fā)重載了)
- 最后一步使用reinterpret_cast將前面獲取到的地址轉(zhuǎn)換回_Tp*類(lèi)型,并且此時(shí)也會(huì)保留_Tp的const或volatile屬性(如果有的話(huà))
4、總結(jié)
本文通過(guò)一個(gè)簡(jiǎn)單的例子和源碼介紹了C++11新引入的模板函數(shù)std::addressof,內(nèi)容雖然比較簡(jiǎn)單,但是考慮到std::addressof在標(biāo)準(zhǔn)庫(kù)中使用頻率是比較高的,所以我們還是應(yīng)該掌握其原理和用法。
最后需要注意的一點(diǎn)就是std::addressof并不保證轉(zhuǎn)換的正確性和合理性,這個(gè)是需要程序員自己把控的,雖然標(biāo)準(zhǔn)庫(kù)使用std::addressof的頻率比較高,但是我們平時(shí)在編程中還是得謹(jǐn)慎一點(diǎn)使用它。
到此這篇關(guān)于詳解C++11的std::addressof源碼解析的文章就介紹到這了,更多相關(guān)C++11 std::addressof內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言基礎(chǔ)知識(shí)變量的作用域和存儲(chǔ)方式詳細(xì)介紹
這篇文章主要介紹了C語(yǔ)言基礎(chǔ)知識(shí)變量的作用域和存儲(chǔ)方式詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-01-01C語(yǔ)言學(xué)習(xí)基礎(chǔ)知識(shí)分享
這篇文章主要介紹了C語(yǔ)言學(xué)習(xí)基礎(chǔ)知識(shí)分享的相關(guān)資料,需要的朋友可以參考下2023-01-01C語(yǔ)言之實(shí)現(xiàn)棧的基礎(chǔ)創(chuàng)建
這篇文章主要介紹了C語(yǔ)言之實(shí)現(xiàn)棧的基礎(chǔ)創(chuàng)建,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C++ OpenCV讀寫(xiě)XML或YAML文件的方法詳解
XML是一種元標(biāo)記語(yǔ)言。所謂元標(biāo)記,就是開(kāi)發(fā)者可以根據(jù)自身需要定義自己的標(biāo)記。YAML是一個(gè)可讀性高,用來(lái)表達(dá)資料序列的格式。本文將通過(guò)C++和OpenCV實(shí)現(xiàn)這兩種文件的讀寫(xiě),需要的可以參考一下2022-05-05C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)系列之樹(shù)的概念結(jié)構(gòu)和常見(jiàn)表示方法
本章將正式開(kāi)啟數(shù)據(jù)結(jié)構(gòu)中?“樹(shù)”?部分的講解,本章將介紹樹(shù)的概念和結(jié)構(gòu),以及樹(shù)的表示方法,感興趣的朋友進(jìn)來(lái)看看吧2022-02-02