天天看點

C++命名空間<轉>

轉自:http://www.cnblogs.com/autocrat/archive/2010/05/05/1727630.html

c++ using namespace std 詳解

 所謂namespace,是名額識符的各種可見範圍。C++标準程式庫中的所有辨別符都被定義于一個名為std的namespace中。

一 :

<iostream>和<iostream.h>是不一樣,前者沒有字尾,實際上,在你的編譯器include檔案夾裡面可以看到,二者是兩個檔案,打開檔案就會發現,裡面的代碼是不一樣的。

字尾為.h的頭檔案c++标準已經明确提出不支援了,早些的實作将标準庫功能定義在全局空間裡,聲明在帶.h字尾的頭檔案裡,c++标準為了和C差別開,也為了正确使用命名空間,規定頭檔案不使用字尾.h。

是以,當使用<iostream.h>時,相當于在c中調用庫函數,使用的是全局命名空間,也就是早期的c++實作;當使用<iostream>的時候,該頭檔案沒有定義全局命名空間,必須使用namespacestd;這樣才能正确使用cout。

二:

所謂namespace,是名額識符的各種可見範圍。

C++标準程式庫中的所有辨別符都被定義于一個名為std的namespace中。

由于namespace的概念,使用C++标準程式庫的任何辨別符時,可以有三種選擇:

1、直接指定辨別符。例如std::ostream而不是ostream。完整語句如下:

std::cout << std::hex<< 3.4<< std::endl;

2、使用using關鍵字。

using std::cout;

using std::endl;

以上程式可以寫成

cout << std::hex<< 3.4<< endl;

3、最友善的就是使用using namespace std;

例如:

#include <iostream>

#include <sstream>

#include <string>

using namespace std;

這樣命名空間std内定義的所有辨別符都有效(曝光)。就好像它們被聲明為全局變量一樣。那麼以上語句可以如下寫:

cout << hex<< 3.4<< endl;

因為标準庫非常的龐大,所程式員在選擇的類的名稱或函數名時就很有可能和标準庫中的某個名字相同。是以為了避免這種情況所造成的名字沖突,就把标準庫中的一切都被放在名字空間std中。但這又會帶來了一個新問題。無數原有的C++代碼都依賴于使用了多年的僞标準庫中的功能,他們都是在全局空間下的。

        是以就有了<iostream.h>和<iostream>等等這樣的頭檔案,一個是為了相容以前的C++代碼,一個是為了支援新的标準。

命名空間std封裝的是标準程式庫的名稱,标準程式庫為了和以前的頭檔案差別,一般不加".h"

using namespace std 的用法

摘自

using namespacestd;用的并不少!   

---------------------------------------------------------------

實際上就是告訴編譯器,你類型是什麼,在哪能找到。

常用的是using namespace std,就是說用C++的标準名字空間。

你也可以引用你自己的名字空間。比如說:

import "C:\\MyTest\\test.tlb"

using namespace CMyTest

就可以引用CMyTest内的各個類型名

看C++ prime

聲明該檔案使用C++标準庫吧!

比如

void main()

{

   cout<< "hello!"<< endl;

}

如果不用using namespace std;這句,那麼

std::cout << "hello!"<<endl;

這是名字空間的問題!具體參看有關書籍吧,新版的C++ 書應該都有介紹的!

using 訓示符!

這是個名字空間問題,是标準C++引入的新概念!

具體在《C++Primer》第8.6節有詳細說明!

      是以就有了<iostream.h>和<iostream>等等這樣的頭檔案,一個是為了相容以前的C++代碼,一個是為了支援新的标準。

名字空間,實質上也是為了友善程式在不同平台上正确的運作。

namespace是為了解決C++中的名字沖突而引入的。

什麼是名字沖突呢?比如,在檔案x.h中有個類MyClass,

在檔案y.h中也有個類MyClass,而在檔案z.cpp中要同時

引用x.h和y.h檔案。顯然,按通常的方法是行不能的,

那怎麼辦呢?引入namespace即可。例如:

      在x.h中的内容為

// x.h

namespace MyNamespace1

   class MyClass

   {

   public:

      void f();

   private:

      int m;

   }

};

      在y.h中的内容為

// y.h

namespace MyNamespace2

   然後在z.cpp中引入x.h和y.h

// z.cpp

#include"x.h"   

#include"y.h"   

void z::f()

   //聲明一個檔案x.h中類MyClass的執行個體x

   MyNamespace1::MyClass x;

    //聲明一個檔案x.h中類MyClass的執行個體x

   MyNamespace2::MyClass y;

   //調用檔案x.h中的函數f

   x.f();

   //調用檔案y.h中的函數f

   y.f();

      名字空間實質上是一個作用域。

      通過上面的一個執行個體應該知道名字空間的作用了吧

盡量不要使用using namespace std;VC++2005使用有感

Posted on 2007-11-06 20:28 Samson小天 閱讀(1163) 評論(6) 編輯 收藏 網摘 所屬分類:C++/C++.net

今天用了VISUAL C++寫了個小程式(VS2005),很簡單很簡單的,但是就是編譯不通過

出現一個奇怪的問題:錯誤 1 error C2668: “max”: 對重載函數的調用不明确

最初代碼如下

template <typename T>

T max (T a,T b)

return ((a>b)?a:b);

double x,y;

cin>>x>>y;

cout<<"Max number is"<<(max(x,y))<<endl;

cin>>x;

   我将這段代碼放到VC++ 6.0下竟然通過了,程式運作也正常。這讓我百思不得其解。後來終于弄明白了!

   其實在std命名空間下還有一個MAX函數,而且實作的功能也是一樣的……我昏。利用轉到定義功能可以看到微軟是怎麼寫MAX函數的。這裡為了不被鄙視就不貼微軟的代碼了。

   明白了為什麼出現這個錯誤我們就改寫代碼如下:

using std::cin;

int main()

   這是我比較推薦的做法,因為C++ PRIMER, EFFECTIVE C++上都是用這種方式的,但是譚浩強的書上都是一句usingnamespace std;就搞定,我覺得蠻簡潔的就一直用了,沒想到帶來那麼多的問題,以前在友元函數上還碰到莫名的錯誤呢。

   其實還有兩個簡單的解決方案,那就是把自己定義的函數改成其他的名字,或者直接用微軟提供的函數。相信微軟提供的效率絕對不會比我們寫的低~

   好了,就寫到這了。希望大家養成良好的程式設計習慣,^-^

很多C++程式員還在使用而不是用更新的标準的庫。

這兩者都有什麼不同呢?首先,5年前我們就開始反對把.h符号繼續用在标準的頭

檔案中。繼續使用過時的規則可不是個好的方法。從功能性的角度來講,

<iostream>包含了一系列模闆化的I/O類,相反地<iostream.h>隻僅僅是支援字元

流。另外,輸入輸出流的C++标準規範接口在一些微妙的細節上都已改進,是以,

<iostream>和<iostream.h>在接口和執行上都是不同的。最後,<iostream>的各組

成都是以STL的形式聲明的,然而<iostream.h>的各組成都是聲明成全局型的。

因為這些實質上的不同,你不能在一個程式中混淆使用這兩個庫。做為一種習

慣,在新的代碼中一般使用<iostream>,但如果你處理的是過去編寫的代碼,為了

繼承可以用繼續用<iostream.h>舊保持代碼的一緻性。

///////////////////

<iostream>表示你使用的是标注命名空間,也就是在程式開始應該有這麼一句話

using namespace std ;

這是遵循c++标準的

<iostream.h>

則沒有遵循c++标準

////////////////

<string.h>是舊的C頭檔案,對應的是基于char*的字元串處理函數;

<string>是包裝了std的C++頭檔案,對應的是新的strng類;

<cstring>是對應舊的C頭檔案的std版本。

在C++語言編寫的程式中,變量和函數等的作用範圍是有一定限制的。比如,在函數體中定義的一個臨時變量就不可以在函數體外使用。為了解決變量和函數等的作用範圍,在C++語言中引入了名空間的概念,并增加了關鍵字namespace和using  

         在一個名空間中可以定義一組變量和函數,這些變量和函數的作用範圍一緻,可以将這些變量和函數稱為這個名空間的成員。  

         通過名空間,可以在同一個檔案中使用相同的變量名或函數名,隻要它們屬于不同的名空間。另外,名空間可以使得代碼操作具有相同名字但屬于不同庫的變量。而且,名空間也可以提高C語言與C++語言的相容性。  

下面通過例程說明關鍵字namespace的用法。  

#include  <conio.h>  

#include  <iostream.h>  

namespace  car  //  名空間的定義  

{  

     int  model;  

     int  length;  

     int  width;  

}  

namespace  plane  

     namespace  size  //  名空間的嵌套  

     {  

         int  length;  

         int  width;  

     }  

namespace  car  //  添加名空間的成員  

     char  *  name;  

namespace  c=car;  //  定義名空間的别名  

int  Time;  //  外部變量屬于全局名空間  

void  main()  

     car::length=3;  

     //  下面一句錯誤,故屏蔽掉  

     //  width=2;  //  對于非全局變量和目前有效臨時變量應該指定名空間  

     plane::size::length=70;  

     cout<<"the  length  of  plane  is  "<<plane::size::length<<"m."<<endl;  

     cout<<"the  length  of  car  is  "<<car::length<<"m."<<endl;  

     //  使用名空間的别名  

     cout<<"the  length  of  c  is  "<<c::length<<"m."<<endl;  

     int  Time=1996;  //  臨時變量,應差別于全局變量  

     ::Time=1997;  

     cout<<"Temp  Time  is  "<<Time<<endl;  

     cout<<"Outer  Time  is  "<<::Time<<endl;  

     //  使用關鍵字using  

     using  namespace  plane;  

     model=202;  

     size::length=93;  

     cout<<model<<endl;  

     cout<<size::length<<endl;  

     getch();  

運作結果:  

the  length  of  plane  is  70m.  

the  length  of  car  is  3m.  

the  length  of  c  is  3m.  

Temp  Time  is  1996  

Outer  Time  is  1997  

說明:  

•  從上面可以看出,名空間定義了一組變量和函數,它們具有相同的作用範圍。對于不同的  

     名空間,可以定義相同的變量名或函數名,在使用的時候,隻要在變量名或函數名前區分  

     開不同的名空間就可以了。  

•  名空間可以被嵌套定義,使用時要逐級對成員用名空間限定符:  :來引用。  

•  系統預設有一個全局名空間,它包含了所有的外部變量。這個名空間沒有名字,引用這個  

     名空間裡的變量時要使用名空間限定符:  :,前面沒有名字。在不使用名空間的情況下,我  

     們知道,不可以在不同檔案中定義相同名字的外部變量,這是因為它們屬于同一個全局名  

     空間,名字不可以重複。  

•  可以給名空間取一個别名。一般别名是一個比較短的名字,來簡化程式設計。  

•  在原有定義好的名空間的基礎上,随時可以往裡增加成員。  

<<using>>  

在前面的例程中可以看到,為了使用時的友善,又引入了關鍵字using。利用using聲明可以在引用名空間成員時不必使用名空間限定符::。此外,關鍵字namespace和using的使用,對函數重載有一定的影響。  

下面通過例程進行具體說明。  

     void  ShowLength(double  len)  //  參數類型為d  o  u  b  l  e  

         cout<<"in  car  namespace:  "<<len<<endl;  

namespace  plane  //  名空間的定義  

     void  ShowLength(int  len)  //  參數類型為i  n  t  

     {    

         cout<<"in  plane  namespace:  "<<len<<endl;  

     using  namespace  car;  

     ShowLength(3);  

     ShowLength(3.8);  

     ShowLength(93);  

     ShowLength(93.75);  

in  car  namespace:  3  

in  car  namespace:  3.8  

in  plane  namespace:  93  

in  car  namespace:  93.75  

         如果沒有名空間的幹擾,函數重載時選擇規則将是非常簡單。隻要實參是double類型,則調用的是前面的函數;如果實參是int類型,則調用後面的函數。但是由于名空間的參與,就出現了上面的運作結果。是以在程式設計的時候一定要注意名空間對函數重載的影響。  

         應注意:調用函數時,如果實參和形參的資料類型實在沒有辦法完全比對,可能會對實參進行适當的資料類型轉換。比如,将char類型轉換為int類型,或進一步将int類型轉換為double類型。這種是将資料類型從簡單往複雜轉換,一般不會丢失資訊。另外一種轉換是反過來,将double類型轉換為int類型,或進一步将int類型轉換為char類型。這種是将資料類型從複雜往簡單轉換,可能會丢失部分資訊。在調用函數的時候,不同的情況下,C++對上述兩種轉換的優先級是不同的。當引入了名空間後,則參與了上述優先級順序的配置設定。

全局空間最大的問題在于它本身僅有一個。在大的軟體項目中,經常會有不少人把他們定義的名字都放在這個單一的空間中,進而不可避免地導緻名字沖突。例如,假設library1.h定義了一些常量,其中包括:

const double lib_version = 1.204;

類似的,library2.h也定義了:

const int lib_version = 3;

 如果某個程式想同時包含library1.h和library2.h就會有問題。作為程式員,盡力使自己的程式庫不給别人帶來這些問題。例如,可預先想一些不大可能造成沖突的某種字首,加在每個全局符号前。當然得承認,這樣組合起來的辨別符看起來不是那麼令人舒服。

  另一個比較好的方法是使用c++namespace。namespace本質上和使用字首的方法一樣,隻不過避免了别人總是看到字首而已。是以,不要這麼做:

const double sdmbook_version =2.0;     // 在這個程式庫中, 每個符号以"sdm"開頭

                                        // class sdmhandle { ... };

sdmhandle&sdmgethandle();            // 為什麼函數要這樣聲明? 

而要這麼做:

namespace sdm {

  const double book_version = 2.0;

  class handle { ... };

  handle& gethandle();

 使用者于是可以通過三種方法來通路這一名字空間裡的符号:将名字空間中的所有符号全部引入到某一使用者空間;将部分符号引入到某一使用者空間;或通過修飾符顯式地一次性使用某個符号:

void f1()

  using namespacesdm;          // 使得sdm中的所有符号不用加修飾符就可以使用

  cout <<book_version;         // 解釋為sdm::book_version

  ...

  handle h =gethandle();       // handle解釋為sdm::handle,

                                // gethandle解釋為sdm::gethandle

 ...                           

void f2()

  usingsdm::book_version;       // 使得僅book_version不用加修飾符就可以使用

  cout <<book_version;          // 解釋為sdm::book_version

  handle h =gethandle();        // 錯誤! handle和gethandle

                                 // 都沒有引入到本空間

 ...                            

void f3()

  cout <<sdm::book_version;     // 使得book_version在本語句有效

  double d =book_version;       // 錯誤! book_version不在本空間

  handle h =gethandle();        // 錯誤! handle和gethandle都沒有引入到本空間

 有些名字空間沒有名字。這種沒命名的名字空間一般用于限制名字空間内部元素的可見性。

  名字空間帶來的最大的好處之一在于:潛在的二義不會造成錯誤。是以,從多個不同的名字空間引入同一個符号名不會造成沖突(假如确實真的從不使用這個符号的話)。例如,除了名字空間sdm外,假如還要用到下面這個名字空間:

namespace acmewindowsystem {

  typedef int handle;

 隻要不引用符号handle,使用sdm和acmewindowsystem時就不會有沖突。假如真的要引用,可以明确地指明是哪個名字空間的handle:

void f()

  using namespacesdm;                // 引入sdm裡的所有符号

  using namespaceacmewindowsystem;   // 引入acme裡的所有符号

 ...                                 // 自由地引用sdm和acme裡除handle之外的其它符号

  handleh;                           // 錯誤! 哪個handle?

  sdm::handleh1;                     // 正确, 沒有二義

  acmewindowsystem::handleh2;        // 也沒有二義

 假如用正常的基于頭檔案的方法來做,隻是簡單地包含sdm.h和acme.h,這樣的話,由于handle有多個定義,編譯将不能通過。

 名字空間的概念加入到c++标準的時間相對較晚,是以有些人會認為它不太重要,可有可無。但這種想法是錯誤的,因為c++标準庫裡幾乎所有的東西都存在于名字空間std之中。它有一種直接的影響方式:c++提供了那些沒有擴充名的頭檔案,如<iostream>,<string>等。

 由于名字空間的概念引入的時間相對較晚,有些編譯器可能不支援。就算是這樣,那也沒理由污染全局名字空間,因為可以用struct來近似實作namespace。可以這樣做:先建立一個結構用以儲存全局符号名,然後将這些全局符号名作為靜态成員放入結構中:

// 用于模拟名字空間的一個結構的定義

struct sdm {

  static const double book_version;

  static handle&gethandle();

const double sdm::book_version =2.0;     // 靜态成員的定義

現在,如果有人想通路這些全局符号名,隻用簡單地在它們前面加上結構名作為字首:

  cout <<sdm::book_version;

  sdm::handle h = sdm::gethandle();

 如果全局範圍内實際上沒有名字沖突,使用者就會覺得加修飾符麻煩而多餘。幸運的是,還是有辦法來讓使用者選擇使用它們或忽略它們。

 對于類型名,可以用類型定義(typedef)來顯式地去掉空間引用。例如,假設結構s(模拟的名字空間)内有個類型名t,可以這樣用typedef來使得t成為s::t的同義詞:

typedef sdm::handle handle;

 對于結構中的每個(靜态)對象x,可以提供一個(全局)引用x,并初始化為s::x:

const double& book_version = sdm::book_version;

 處理函數的方法和處理對象一樣,但要注意,即使定義函數的引用是合法的,但代碼的維護者會更喜歡使用函數指針:

sdm::handle& (* const gethandle)()=     // gethandle是指向sdm::gethandle

 sdm::gethandle;                        // 的const 指針

 注意gethandle是一個常指針。因為當然不想讓使用者将它指向别的什麼東西,而不是sdm::gethandle。

如果真想知道怎麼定義一個函數的引用,看看下面:

sdm::handle& (&gethandle)()=     // gethandle是指向

 sdm::gethandle;                 // sdm::gethandle的引用

 除了初始化的方式外,函數的引用和函數的常指針在行為上完全相同,隻是函數指針更易于了解。

 有了上面的類型定義和引用,那些不會遭遇全局名字沖突的使用者就會使用沒有修飾符的類型和對象名;相反,那些有全局名字沖突的使用者就會忽略類型和引用的定義,代之以帶修飾符的符号名。還要注意的是,不是所有使用者都想使用這種簡寫名,是以要把類型定義和引用放在一個單獨的頭檔案中,不要把它和(模拟namespace的)結構的定義混在一起。

 struct是namespace的很好的近似,但實際上還是相差很遠。它在很多方面很欠缺,其中很明顯的一點是對運算符的處理。如果運算符被定義為結構的靜态成員,它就隻能通過函數調用來使用,而不能象正常的運算符所設計的那樣,可以通過自然的文法來使用:

// 定義一個模拟名字空間的結構,結構内部包含widgets的類型

// 和函數。widgets對象支援operator+進行加法運算

struct widgets {

  class widget { ... };

   static const widgetoperator+(const widget& lhs,constwidget& rhs);

// 為上面所述的widge和operator+ 建立全局(無修飾符的)名稱

typedef widgets::widget widget;

const widget (* const operator+)(constwidget&,       // 錯誤!

                                constwidget&);      // operator+不能是指針名

widget w1, w2, sum;

sum = w1 +w2;                          // 錯誤! 本空間沒有聲明

                                        // 參數為widgets 的operator+

sum = widgets::operator+(w1,w2);       // 合法, 但不是"自然"的文法

正因為這些限制,是以一旦編譯器支援,就要盡早使用真正的名字空間。

繼續閱讀