我再也不想在任何頭文件中看到"using namespace xxx;"了

來源: 圖靈社區  發布時間: 2013-01-20 17:03  閱讀: 10433 次  推薦: 20   原文鏈接   [收藏]  

  原文:I don’t want to see another “using namespace xxx;” in a header file ever again

  在這里,我毫不回避地說了這句話。

  作為一個開發者/團隊領導者,我經常會去招聘新的項目成員,有時候也幫助其他組的人來面試應聘者。作為應聘流程之一,我經常要求應聘者寫一些代碼,因此我檢查過相當多的代碼。在最近提交的 C++ 代碼中,我注意到一個趨勢,在任何頭文件中,我總是能看到以下代碼:

using namespace std;

  如果我用我們的代碼檢查系統(在實踐中我十分推薦這個系統)來檢驗代碼,以上那行代碼經常會跟著一句評論“Timo 不會這樣寫的”。他們說得很對,我確實不會這么寫。

  那么,為什么我說服了很多 C++ 教材(也許并不是什么好事),讓他們認為使用上面那段代碼是非常壞的方式?讓我們先來看看上面那段代碼做了什么。總的來說,它把命名空間“std”以內的所有內容(或者其他由作者用 using 調用命名空間)無一例外的引入了目前的命名空間中。請注意我說的“所有內容”,并不是一兩個你想用的類\類型\模板。在一段代碼的開頭引入命名空間的原因則是加強程序模塊化,和減少命名沖突。大體上,它允許你可以寫類似下面的那段代碼,并且保證編譯器可以選擇正確的實現:

std::vector<std::string> names;
my_cool_reimplementation::vector<our_internal_stuff::string> othernames;

  現在,假定我們正在嘗試減少代碼輸入,并且在以上代碼中使用 using 聲明(或者更糟糕的,兩個命名空間都聲明了),按照如下代碼來實現:

vector<string> names;
vector<our_internal_stuff::string> othernames;

  如果這段代碼的作者很幸運的話,編譯器會選擇 vector 的正確實現,或者至少在最初的階段會這么做。但是過了一段時間,你會碰到一些很奇怪的編譯器錯誤。幸運的話,你能找到這些錯誤的原因——我曾經遇到過類似問題,我花費了好幾天才能找到這類問題的原因。該死,它們會浪費你很多的時間,僅僅因為你為了想少打 5 個字符的代碼。

  并且,如果你把 using 聲明用在了頭文件中,你會讓這類問題更加惡化,因為命名沖突問題早晚都會在一個調用關系非常非常遠的模塊中神不知鬼不覺的出現,而你可能需要查三層調用才可以找到原因所在,一個頭文件包含了另一個直接使用 using 聲明的頭文件可以導致命名空間被立刻污染掉,任何一個使用命名空間的文件如果使用了 std 命名空間的內容,都會導致這類問題。

  那么,為什么你能在很多教科書中看到它們使用 using namespace std? 我的理論是,它確實會幫助改善整本書的排版,并且能減少一些視覺的混亂。在一本紙質書中,你只有很有限的空間來寫文字,因此你必須最大限度的利用它,加之書中的代碼例子通常都很簡單。但另一方面,不同的命名空間限定符會帶來了很多視覺混亂,這會讓讀者很難從上下文判斷作者的意圖。當你想在這個時代寫一些有效率的代碼的時候,以上兩點都不完全對,現在的編譯器大多數能每行處理 60-80 個單詞(你可以試試,這可以的)。因此,不要亂引入命名空間。

  如果你非常明確的想在一個頭文件中使用 using 聲明,應該怎么做?我們有其他途徑可以減少不得不用 using 聲明的情況——你可以用以下其中一種,或則多種方式的組合。

  首先,你只需使用 typedef。我會建議你使用這種方法,即使我并不經常遵循我自己的建議,我也認為無論如何這都是一個在實際應用中很好的方法。實際上,使用 typedef 有兩個好處——他讓一個類型名可讀性增加,如果你選擇了一個很好的名字,它可以讓作者的意圖更加顯而易見。比較一下如下的聲明方式:

std::map<std::string, long> clientLocations;
typedef std::map<std::string, long> ClientNameToZip;
ClientNameToZip clientLocations;

  第二個聲明——即使它被展開為兩行——也比第一個聲明更加直觀,同時,它也避免了命名空間模糊化。

  另外一個選擇則是用兩種方法來限制 using 聲明的作用域——僅僅是你想用的那個“using”符號,例如:

using std::string;

  但是,把這段聲明扔到頭文件中,幾乎和使用“using namespace”一樣糟糕,因此,你應該使用作用域來限制下它的可見性,來確保你的 using 聲明真的只在第一次做 using 聲明的地方有效。例如,你可以用如下方法限制類聲明作用域:

namespace bar
{
  struct zzz
  {
    …
  };
}
class foo
{
  using namespace bar;
  zzz m_snooze; // Pulls in bar::zzz  };

  或者,你可以直接把 using 的作用域限制到一個函數中,例如:

void temp ()
{
  using namespace std;
  string test = "fooBar";
}

  不管哪種方法,你都可以把 using 的作用域限制到需要使用它的代碼中,而不是把它放到代碼的公共空間中。你的工程越大,確保模塊化,和最小化不可預料的負面影響就顯得越發重要。

20
0
 
標簽:C++
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()