青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

陳碩的Blog

C++ 工程實踐(1):慎用匿名 namespace

匿名 namespace (anonymous namespace 或稱 unnamed namespace) 是 C++ 的一項非常有用的功能,其主要目的是讓該 namespace 中的成員(變量或函數(shù))具有獨(dú)一無二的全局名稱,避免名字碰撞 (name collisions)。一般在編寫 .cpp 文件時,如果需要寫一些小的 helper 函數(shù),我們常常會放到匿名 namespace 里。muduo 0.1.7 中的 muduo/base/Date.ccmuduo/base/Thread.cc 等處就用到了匿名 namespace。

我最近在工作中遇到并重新思考了這一問題,發(fā)現(xiàn)匿名 namespace 并不是多多益善。

C 語言的 static 關(guān)鍵字的兩種用法

C 語言的 static 關(guān)鍵字有兩種用途:

1. 用于函數(shù)內(nèi)部修飾變量,即函數(shù)內(nèi)的靜態(tài)變量。這種變量的生存期長于該函數(shù),使得函數(shù)具有一定的“狀態(tài)”。使用靜態(tài)變量的函數(shù)一般是不可重入的,也不是線程安全的。

2. 用在文件級別(函數(shù)體之外),修飾變量或函數(shù),表示該變量或函數(shù)只在本文件可見,其他文件看不到也訪問不到該變量或函數(shù)。專業(yè)的說法叫“具有 internal linkage”(簡言之:不暴露給別的 translation unit)。

C 語言的這兩種用法很明確,一般也不容易混淆。

C++ 語言的 static 關(guān)鍵字的四種用法

由于 C++ 引入了 class,在保持與 C 語言兼容的同時,static 關(guān)鍵字又有了兩種新用法:

3. 用于修飾 class 的數(shù)據(jù)成員,即所謂“靜態(tài)成員”。這種數(shù)據(jù)成員的生存期大于 class 的對象(實體 instance)。靜態(tài)數(shù)據(jù)成員是每個 class 有一份,普通數(shù)據(jù)成員是每個 instance 有一份,因此也分別叫做 class variable 和 instance variable。

4. 用于修飾 class 的成員函數(shù),即所謂“靜態(tài)成員函數(shù)”。這種成員函數(shù)只能訪問 class variable 和其他靜態(tài)程序函數(shù),不能訪問 instance variable 或 instance method。

當(dāng)然,這幾種用法可以相互組合,比如 C++ 的成員函數(shù)(無論 static 還是 instance)都可以有其局部的靜態(tài)變量(上面的用法 1)。對于 class template 和 function template,其中的 static 對象的真正個數(shù)跟 template instantiation (模板具現(xiàn)化)有關(guān),相信學(xué)過 C++ 模板的人不會陌生。

可見在 C++ 里 static 被 overload 了多次。匿名 namespace 的引入是為了減輕 static 的負(fù)擔(dān),它替換了 static 的第 2 種用途。也就是說,在 C++ 里不必使用文件級的 static 關(guān)鍵字,我們可以用匿名 namespace 達(dá)到相同的效果。(其實嚴(yán)格地說,linkage 或許稍有不同,這里不展開討論了。)

匿名 namespace 的不利之處

在工程實踐中,匿名 namespace 有兩大不利之處:

  1. 其中的函數(shù)難以設(shè)斷點(diǎn),如果你像我一樣使用的是 gdb 這樣的文本模式 debugger。
  2. 使用某些版本的 g++ 時,同一個文件每次編譯出來的二進(jìn)制文件會變化,這讓某些 build tool 失靈。

考慮下面這段簡短的代碼 (anon.cc):

   1: namespace
   2: {
   3:   void foo()
   4:   {
   5:   }
   6: }
   7:  
   8: int main()
   9: {
  10:   foo();
  11: }

對于問題 1:

gdb 的<tab>鍵自動補(bǔ)全功能能幫我們設(shè)定斷點(diǎn),不是什么大問題。前提是你知道那個"(anonymous namespace)::foo()"正是你想要的函數(shù)。

$ gdb ./a.out
GNU gdb (GDB) 7.0.1-debian

(gdb) b '<tab>
(anonymous namespace)         __data_start                  _end
(anonymous namespace)::foo()  __do_global_ctors_aux         _fini
_DYNAMIC                      __do_global_dtors_aux         _init
_GLOBAL_OFFSET_TABLE_         __dso_handle                  _start
_IO_stdin_used                __gxx_personality_v0          anon.cc
__CTOR_END__                  __gxx_personality_v0@plt      call_gmon_start
__CTOR_LIST__                 __init_array_end              completed.6341
__DTOR_END__                  __init_array_start            data_start
__DTOR_LIST__                 __libc_csu_fini               dtor_idx.6343
__FRAME_END__                 __libc_csu_init               foo
__JCR_END__                   __libc_start_main             frame_dummy
__JCR_LIST__                  __libc_start_main@plt         int
__bss_start                   _edata                        main

(gdb) b '(<tab>
anonymous namespace)         anonymous namespace)::foo()

(gdb) b '(anonymous namespace)::foo()'
Breakpoint 1 at 0x400588: file anon.cc, line 4.

麻煩的是,如果兩個文件 anon.cc 和 anonlib.cc 都定義了匿名空間中的 foo() 函數(shù)(這不會沖突),那么 gdb 無法區(qū)分這兩個函數(shù),你只能給其中一個設(shè)斷點(diǎn)?;蛘吣闶褂?文件名:行號 的方式來分別設(shè)斷點(diǎn)。(從技術(shù)上,匿名 namespace 中的函數(shù)是 weak text,鏈接的時候如果發(fā)生符號重名,linker 不會報錯。)

從根本上解決的辦法是使用普通具名 namespace,如果怕重名,可以把源文件名(必要時加上路徑)作為 namespace 名字的一部分。

對于問題 2:

把它編譯兩次,分別生成 a.out 和 b.out:

$ g++ -g -o a.out anon.cc

$ g++ -g -o b.out anon.cc

$ md5sum a.out b.out
0f7a9cc15af7ab1e57af17ba16afcd70  a.out
8f22fc2bbfc27beb922aefa97d174e3b  b.out

$ g++ --version
g++ (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu4)

$ diff -u <(nm a.out) <(nm b.out)
--- /dev/fd/63  2011-02-15 22:27:58.960754999 +0800
+++ /dev/fd/62  2011-02-15 22:27:58.960754999 +0800
@@ -2,7 +2,7 @@
0000000000600940 d _GLOBAL_OFFSET_TABLE_
0000000000400634 R _IO_stdin_used
                  w _Jv_RegisterClasses
-0000000000400538 t _ZN36_GLOBAL__N_anon.cc_00000000_E2CEEB513fooEv
+0000000000400538 t _ZN36_GLOBAL__N_anon.cc_00000000_CB51498D3fooEv
0000000000600748 d __CTOR_END__
0000000000600740 d __CTOR_LIST__
0000000000600758 d __DTOR_END__

由上可見,g++ 4.2.4 會隨機(jī)地給匿名 namespace 生成一個惟一的名字(foo() 函數(shù)的 mangled name 中的 E2CEEB51 和 CB51498D 是隨機(jī)的),以保證名字不沖突。也就是說,同樣的源文件,兩次編譯得到的二進(jìn)制文件內(nèi)容不相同,這有時候會造成問題。比如說拿到一個會發(fā)生 core dump 的二進(jìn)制可執(zhí)行文件,無法確定它是由哪個 revision 的代碼編譯出來的。畢竟編譯結(jié)果不可復(fù)現(xiàn),具有一定的隨機(jī)性。

這可以用 gcc 的 -frandom-seed 參數(shù)解決,具體見文檔。

這個現(xiàn)象在 gcc 4.2.4 中存在(之前的版本估計類似),在 gcc 4.4.5 中不存在。

替代辦法

如果前面的“不利之處”給你帶來困擾,解決辦法也很簡單,就是使用普通具名 namespace。當(dāng)然,要起一個好的名字,比如 boost 里就常常用 boost::detail 來放那些“不應(yīng)該暴露給客戶,但又不得不放到頭文件里”的函數(shù)或 class。

 

總而言之,匿名 namespace 沒什么大問題,使用它也不是什么過錯。萬一它礙事了,可以用普通具名 namespace 替代之。

posted on 2011-02-15 22:55 陳碩 閱讀(6819) 評論(3)  編輯 收藏 引用

評論

# re: C++ 工程實踐(1):慎用匿名 namespace 2011-02-16 09:31 zuhd

喜歡樓主這種寫作風(fēng)格,深入淺出,感覺像是以前高中時做證明題一樣,很清晰  回復(fù)  更多評論   

# re: C++ 工程實踐(1):慎用匿名 namespace 2011-02-17 13:20 淘寶皇冠店

很好12659965  回復(fù)  更多評論   

# re: C++ 工程實踐(1):慎用匿名 namespace 2011-02-18 10:48 zwp

首先,匿名空間最大的好處是不用擔(dān)心名字沖突。如果,你能保證現(xiàn)有的代碼或者以后的代碼都不會使用你的具名空間的話,當(dāng)然可以使用具名空間。而這也正是使用匿名空間的理由。

具名空間是一種封閉、限制和警戒,意味著可能的沖突。
匿名空間則像一個開放的獨(dú)立的組織,一定程度上代表著可擴(kuò)展。  回復(fù)  更多評論   

<2011年2月>
303112345
6789101112
13141516171819
20212223242526
272812345
6789101112

導(dǎo)航

統(tǒng)計

常用鏈接

隨筆分類

隨筆檔案

相冊

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品视频精品视频| 一本久道久久综合狠狠爱| 久久久之久亚州精品露出| 午夜精品一区二区三区在线视| 亚洲卡通欧美制服中文| 日韩亚洲欧美中文三级| 在线视频精品| 久久精品国产久精国产爱| 久久久噜久噜久久综合| 乱中年女人伦av一区二区| 欧美国产一区二区在线观看| 欧美三级电影一区| 国产无一区二区| **欧美日韩vr在线| 亚洲深夜福利| 久久全国免费视频| 亚洲电影成人| 中文网丁香综合网| 久久精品一区四区| 欧美日韩一区二区三| 国产精品美腿一区在线看| 激情婷婷久久| 亚洲一区二区少妇| 久久蜜桃精品| 99热免费精品| 久久在线免费视频| 国产精品国产三级国产a| 国产在线不卡精品| 在线亚洲一区| 欧美成年人视频网站| 亚洲一区二区三区乱码aⅴ蜜桃女| 久久国产精品亚洲va麻豆| 欧美日韩一区二区免费在线观看 | 欧美va天堂在线| 国产精品欧美日韩一区二区| 在线色欧美三级视频| 欧美一区二区三区日韩视频| 亚洲激情国产精品| 久久久久国色av免费看影院| 国产精品国产三级国产aⅴ浪潮| 伊人久久大香线蕉av超碰演员| 亚洲午夜三级在线| 亚洲激情精品| 你懂的亚洲视频| 伊人蜜桃色噜噜激情综合| 亚洲欧美日韩中文视频| 99re66热这里只有精品3直播| 另类专区欧美制服同性| 国产在线播精品第三| 欧美一区永久视频免费观看| 日韩亚洲欧美精品| 欧美激情在线有限公司| 亚洲国产欧美日韩| 老巨人导航500精品| 久久爱www.| 国产亚洲一本大道中文在线| 国产精品国产三级国产aⅴ无密码| 亚洲电影免费在线观看| 另类激情亚洲| 久久午夜电影| 亚洲国产另类久久精品| 欧美成人免费全部| 免费观看30秒视频久久| 亚洲激情六月丁香| 亚洲二区免费| 欧美日韩1区2区3区| 一区二区av在线| 日韩亚洲欧美精品| 国产精品久久77777| 欧美一区二区三区啪啪| 亚洲欧美国产制服动漫| 国产一区二区在线观看免费| 久久久青草青青国产亚洲免观| 欧美一区二区免费| 亚洲大片免费看| 亚洲国产一区二区a毛片| 欧美精品日韩三级| 午夜精品久久久久久久99热浪潮 | 欧美电影免费观看| 日韩午夜电影av| 一区二区三区四区国产精品| 国产精品久久久久久久午夜| 欧美一区二区三区婷婷月色| 久久精品欧洲| 亚洲精品久久久一区二区三区| 亚洲人成在线播放| 国产精品麻豆欧美日韩ww| 久久精品国产91精品亚洲| 久久久久免费视频| 一本色道久久综合亚洲二区三区| 一区二区三区精品| 国产一区日韩二区欧美三区| 亚洲二区视频在线| 国产精品乱看| 你懂的国产精品| 欧美体内谢she精2性欧美| 欧美在线视频一区二区| 麻豆成人综合网| 欧美亚洲日本网站| 欧美大片91| 久久九九全国免费精品观看| 久久亚洲一区二区| 香蕉久久夜色精品国产使用方法| 久久久青草婷婷精品综合日韩 | 亚洲视频在线免费观看| 欧美在线免费看| 亚洲深夜影院| 久久最新视频| 校园激情久久| 欧美日韩三区四区| 欧美国产日韩一二三区| 国产日韩欧美一区二区三区四区| 亚洲国产精品电影| 激情综合网址| 午夜久久久久| 亚洲欧美日韩国产综合在线| 免费观看久久久4p| 久久午夜精品| 亚洲综合不卡| 亚洲视频在线观看网站| 美女脱光内衣内裤视频久久影院| 欧美自拍偷拍午夜视频| 欧美视频在线观看| 欧美激情精品久久久久久蜜臀| 国产精品中文在线| 亚洲视频日本| 亚洲免费影院| 欧美午夜不卡视频| 亚洲麻豆国产自偷在线| 亚洲精品久久久久中文字幕欢迎你| 久久激情视频免费观看| 久久久999精品免费| 国产一区二区高清| 欧美中日韩免费视频| 久久精品一区二区三区四区 | 日韩一级大片在线| 欧美r片在线| 蜜臀av国产精品久久久久| 狠狠色狠色综合曰曰| 欧美在线视频全部完| 久久综合久色欧美综合狠狠| 国产亚洲一区二区三区在线观看| 亚洲在线播放电影| 久久精品一本久久99精品| 国产一区久久久| 久久躁狠狠躁夜夜爽| 欧美成人久久| 99视频国产精品免费观看| 欧美欧美天天天天操| 夜夜嗨av一区二区三区四区 | 中文久久精品| 国产精品一区二区a| 亚洲欧美久久久| 久久免费观看视频| 亚洲国产综合91精品麻豆| 欧美黑人国产人伦爽爽爽| 99re6热只有精品免费观看| 午夜精品久久久久久| 国语自产精品视频在线看一大j8 | 欧美三级免费| 亚洲一区二区三区高清| 欧美一区二区三区喷汁尤物| 国内精品伊人久久久久av影院| 久久久水蜜桃| 亚洲精品欧美| 性欧美video另类hd性玩具| 国产亚洲制服色| 欧美成熟视频| 亚洲一区二区在线免费观看| 久久精品亚洲一区二区三区浴池 | 欧美在线影院在线视频| 一区二区三区在线观看欧美| 欧美日韩国产黄| 性欧美1819性猛交| 亚洲高清中文字幕| 午夜精品久久久久久99热| 一区二区三区自拍| 欧美日韩国产在线播放网站| 亚洲欧美色婷婷| 亚洲国产日韩欧美在线图片| 欧美一区二区三区精品电影| 亚洲欧美日韩国产| 一本久久知道综合久久| 国产精品日韩一区二区| 浪潮色综合久久天堂| 亚洲天堂黄色| 亚洲高清电影| 久久夜色精品国产欧美乱| 一区二区三区精品国产| 在线观看一区| 国产精品永久免费视频| 欧美精品一区二区久久婷婷| 先锋影音网一区二区| 一区二区电影免费观看| 欧美gay视频| 久久精品亚洲一区| 午夜精品免费视频| 一区二区福利| 亚洲日本理论电影| 亚洲国产毛片完整版|