詳解C++11的std::addressof源碼解析
1、源碼準備
本文是基於gcc-4.9.0的源代碼進行分析,std::addressof是C++11才加入標準的,所以低版本的gcc源碼是沒有這個的,建議選擇4.9.0或更新的版本去學習,不同版本的gcc源碼差異應該不小,但是原理和設計思想的一樣的,下面給出源碼下載地址
http://ftp.gnu.org/gnu/gcc
2、std::addressof簡介
std::addressof的作用是獲取一個對象的實際地址,即使這個對象的&操作符已被重載。它接受一個參數,該參數為要獲得地址的那個對象的引用。下面通過一個極其簡單的例子瞭解一下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; }
上面的代碼輸出結果為:
&t.a:0x7ffefcb48eb0
&t.b:0x7ffefcb48eb4
&t:0x7ffefcb48eb4
addressof(t):0x7ffefcb48eb0
在這裡正常來說使用&t應該取到的是t.a的地址值才對,但是由於我們重載瞭&運算符,所以這裡取到瞭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裡面調用瞭std::__addressof,所以真正起作用的是std::__addressof。__addressof的處理過程可以分為以下四步:
- 將__r由類型_Tp&強制轉換為const volatile char&,這樣做有兩個作用:一是防止後面使用&操作符獲取地址時觸發原類型(即_Tp)的重載操作(operator&),就像上面那個例子那樣;二是reinterpret_cast操作符總是可以合法的在原類型的基礎上加const或volatile, 但是如果_Tp原來就帶有const或volatile的話, 通過reinterpret_cast去掉是不允許的, 因此需要加上const volatile來避免編譯器報錯, 也就是此時不用再管_Tp是否本來就帶有const或volatile屬性瞭。
- 將前面轉換得到的結果強制轉換為char&類型,此時如果轉換成其它類型有可能會觸發強制地址對齊的操作,這樣的話真實地址就有可能會被改變瞭,最終造成程序錯誤。需要註意的是這個轉換過程使用的是const_cast,可以順便將前面留下的const和volatile屬性給去掉瞭。
- 使用&符號將前面的結果的地址給取出來(此時已經不會觸發重載瞭)
- 最後一步使用reinterpret_cast將前面獲取到的地址轉換回_Tp*類型,並且此時也會保留_Tp的const或volatile屬性(如果有的話)
4、總結
本文通過一個簡單的例子和源碼介紹瞭C++11新引入的模板函數std::addressof,內容雖然比較簡單,但是考慮到std::addressof在標準庫中使用頻率是比較高的,所以我們還是應該掌握其原理和用法。
最後需要註意的一點就是std::addressof並不保證轉換的正確性和合理性,這個是需要程序員自己把控的,雖然標準庫使用std::addressof的頻率比較高,但是我們平時在編程中還是得謹慎一點使用它。
到此這篇關於詳解C++11的std::addressof源碼解析的文章就介紹到這瞭,更多相關C++11 std::addressof內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 解析C++11的std::ref、std::cref源碼
- c++中的const_cast用法大全
- C++強制類型轉換(static_cast、dynamic_cast、const_cast、reinterpret_cast)
- C++ 強制類型轉換詳解
- C++右值引用與移動構造函數基礎與應用詳解