無論是身處學校還是步入社會,,大家都嘗試過寫作吧,,借助寫作也可以提高我們的語言組織能力,。大家想知道怎么樣才能寫一篇比較優(yōu)質(zhì)的范文嗎?這里我整理了一些優(yōu)秀的范文,,希望對大家有所幫助,,下面我們就來了解一下吧。
標準命名空間 命名空間的概念及作用篇一
對于命名空間,,官方文檔已經(jīng)說得很詳細[查看],,我在這里做了一下實踐和總結(jié)。
命名空間一個最明確的目的就是解決重名問題,,php中不允許兩個函數(shù)或者類出現(xiàn)相同的名字,,否則會產(chǎn)生一個致命的錯誤。這種情況下只要避免命名重復就可以解決,,最常見的一種做法是約定一個前綴,。例:項目中有兩個模塊:article和message board,它們各自有一個處理用戶留言的類comment,。之后我可能想要增加對所有用戶留言的一些信息統(tǒng)計功能,,比如說我想得到所有留言的數(shù)量。這時候調(diào)用它們comment提供的方法是很好的做法,,但是同時引入各自的comment類顯然是不行的,,代碼會出錯,在另一個地方重寫任何一個comment也會降低維護性,。那這時只能重構(gòu)類名,,我約定了一個命名規(guī)則,在類名前面加上模塊名,,像這樣:article_comment,、messageboard_comment 可以看到,名字變得很長,,那意味著以后使用comment的時候會寫上更多的代碼(至少字符多了),。并且,以后如果要對各個模塊增加更多的一些整合功能,,或者是互相調(diào)用,,發(fā)生重名的時候就需要重構(gòu)名字。當然在項目開始的時候就注意到這個問題,,并規(guī)定命名規(guī)則就能很好的避免這個問題,。另一個解決方法可以考慮使用命名空間。
注明:
本文提到的常量:php5.3開始const關(guān)鍵字可以用在類的外部,。const和define都是用來聲明常量的(它們的區(qū)別不詳述),,但是在命名空間里,define的作用是全局的,,而const則作用于當前空間,。我在文中提到的常量是指使用const聲明的常量,。
基礎
命名空間將代碼劃分出不同的空間(區(qū)域),每個空間的常量,、函數(shù),、類(為了偷懶,我下邊都將它們稱為元素)的名字互不影響,,這個有點類似我們常常提到的‘封裝'的概念,。創(chuàng)建一個命名空間需要使用namespace關(guān)鍵字,這樣: 復制代碼代碼如下:
//創(chuàng)建一個名為'article'的命名空間 namespace article;?>
要注意的是,,當前腳本文件的第一個命名空間前面不能有任何代碼,,下面的寫法都是錯誤的: 復制代碼代碼如下: //例一
//在腳本前面寫了一些邏輯代碼
//例二
//在腳本前面輸出了一些字符
為什么要說第一個命名空間呢?因為同一腳本文件中可以創(chuàng)建多個命名空間,。
下面我創(chuàng)建了兩個命名空間,,順便為這兩個空間各自添加了一個comment類元素: 復制代碼代碼如下:
//創(chuàng)建一個名為'article'的命名空間 namespace article;//此comment屬于article空間的元素 class comment { }
//創(chuàng)建一個名為'messageboard'的命名空間 namespace messageboard;//此comment屬于messageboard空間的元素 class comment { } ?>
在不同空間之間不可以直接調(diào)用其它元素,,需要使用命名空間的語法: 復制代碼代碼如下:
namespace article;class comment { }
namespace messageboard;class comment { } //調(diào)用當前空間(messageboard)的comment類 $comment = new comment();//調(diào)用article空間的comment類
$article_comment = new articlecomment();?>
可以看到,,在messageboard空間中調(diào)用article空間里的comment類時,使用了一種像文件路徑的語法: 空間名元素名
除了類之外,,對函數(shù)和常量的用法是一樣的,,下面我為兩個空間創(chuàng)建了新的元素,并在messageboard空間中輸出了它們的值,。復制代碼代碼如下:
namespace messageboard;const path = '/message_board';function getcommenttotal(){ return 300;} class comment { } //調(diào)用當前空間的常量,、函數(shù)和類 echo path;///message_board echo getcommenttotal();//300 $comment = new comment();//調(diào)用article空間的常量、函數(shù)和類 echo articlepath;///article echo articlegetcommenttotal();//100 $article_comment = new articlecomment();?>
然后我的確得到了article空間的元素數(shù)據(jù),。子空間
命名空間的調(diào)用語法像文件路徑一樣是有道理的,,它允許我們自定義子空間來描述各個空間之間的關(guān)系。抱歉我忘了說,,article和message board這兩個模塊其實都是處于同一個blog項目內(nèi),。如果用命名空間來表達它們的關(guān)系,是這樣: 復制代碼代碼如下:
//我用這樣的命名空間表示處于blog下的article模塊 namespace blogarticle;class comment { }
//我用這樣的命名空間表示處于blog下的message board模塊 namespace blogmessageboard;class comment { } //調(diào)用當前空間的類
$comment = new comment();//調(diào)用blogarticle空間的類
$article_comment = new blogarticlecomment();?>
而且,,子空間還可以定義很多層次,,比如說 blogarticlearchivesdate
公共空間
我有一個腳本文件,里面有一些好用的函數(shù)和類: 復制代碼代碼如下:
function getip(){ } class filterxss { } ?>
在一個命名空間里引入這個腳本,,腳本里的元素不會歸屬到這個命名空間,。如果這個腳本里沒有定義其它命名空間,它的元素就始終處于公共空間中: 復制代碼代碼如下:
namespace blogarticle;//引入腳本文件
include './';$filter_xss = new filterxss();//出現(xiàn)致命錯誤:找不到blogarticlefilterxss類 $filter_xss = new filterxss();//正確 ?>
調(diào)用公共空間的方式是直接在元素名稱前加 就可以了,,否則php解析器會認為我想調(diào)用當前空間下的元素,。除了自定義的元素,還包括php自帶的元素,,都屬于公共空間,。
要提一下,,其實公共空間的函數(shù)和常量不用加 也可以正常調(diào)用(不明白php為什么要這樣做),但是為了正確區(qū)分元素,,還是建議調(diào)用函數(shù)的時候加上
名稱術(shù)語
在說別名和導入之前,,需要知道關(guān)于空間三種名稱的術(shù)語,以及php是怎樣解析它們的,。官方文檔說得非常好,,我就直接拿來套了。
1.非限定名稱,,或不包含前綴的類名稱,,例如 $comment = new comment()。如果當前命名空間是blogarticle,,comment將被解析為blogarticlecomment,。如果使用comment的代碼不包含在任何命名空間中的代碼(全局空間中),則comment會被解析為comment,。
2.限定名稱,,或包含前綴的名稱,例如 $comment = new articlecomment(),。如果當前的命名空間是blog,,則comment會被解析為blogarticlecomment。如果使用comment的代碼不包含在任何命名空間中的代碼(全局空間中),,則comment會被解析為comment,。
3.完全限定名稱,或包含了全局前綴操作符的名稱,,例如 $comment = new articlecomment(),。在這種情況下,comment總是被解析為代碼中的文字名(literal name)articlecomment,。
其實可以把這三種名稱類比為文件名(例如 ),、相對路徑名(例如./article/)、絕對路徑名(例如 /blog/article/),,這樣可能會更容易理解,。我用了幾個示例來表示它們: 復制代碼代碼如下:
//創(chuàng)建空間blog namespace blog;class comment { } //非限定名稱,表示當前blog空間 //這個調(diào)用將被解析成 blogcomment();$blog_comment = new comment();//限定名稱,,表示相對于blog空間
//這個調(diào)用將被解析成 blogarticlecomment();$article_comment = new articlecomment();//類前面沒有反斜桿 //完全限定名稱,,表示絕對于blog空間 //這個調(diào)用將被解析成 blogcomment();$article_comment = new blogcomment();//類前面有反斜桿 //完全限定名稱,表示絕對于blog空間
//這個調(diào)用將被解析成 blogarticlecomment();$article_comment = new blogarticlecomment();//類前面有反斜桿
//創(chuàng)建blog的子空間article namespace blogarticle;class comment { } ?>
其實之前我就一直在使用非限定名稱和完全限定名稱,,現(xiàn)在它們終于可以叫出它們的名稱了,。
別名和導入
別名和導入可以看作是調(diào)用命名空間元素的一種快捷方式。php并不支持導入函數(shù)或常量,。它們都是通過使用use操作符來實現(xiàn): 復制代碼代碼如下:
namespace blogarticle;class comment { }
//創(chuàng)建一個bbs空間(我有打算開個論壇)namespace bbs;//導入一個命名空間 use blogarticle;//導入命名空間后可使用限定名稱調(diào)用元素 $article_comment = new articlecomment();//為命名空間使用別名 use blogarticle as arte;//使用別名代替空間名
$article_comment = new artecomment();//導入一個類
use blogarticlecomment;//導入類后可使用非限定名稱調(diào)用元素 $article_comment = new comment();//為類使用別名
use blogarticlecomment as comt;//使用別名代替空間名
$article_comment = new comt();?>
我注意到,,如果導入元素的時候,,當前空間有相同的名字元素將會怎樣?顯然結(jié)果會發(fā)生致命錯誤,。
例:
復制代碼代碼如下:
namespace blogarticle;class comment { }
namespace bbs;class comment { } class comt { }
//導入一個類
use blogarticlecomment;$article_comment = new comment();//與當前空間的comment發(fā)生沖突,,程序產(chǎn)生致命錯誤 //為類使用別名
use blogarticlecomment as comt;$article_comment = new comt();//與當前空間的comt發(fā)生沖突,程序產(chǎn)生致命錯誤 ?> 動態(tài)調(diào)用
php提供了namespace關(guān)鍵字和__namespace__魔法常量動態(tài)的訪問元素,,__namespace__可以通過組合字符串的形式來動態(tài)訪問: 復制代碼代碼如下:
namespace blogarticle;const path = '/blog/article';class comment { }
//namespace關(guān)鍵字表示當前空間 echo namespacepath;///blog/article $comment = new namespacecomment();//魔法常量__namespace__的值是當前空間名稱 echo __namespace__;//blogarticle //可以組合成字符串并調(diào)用
$comment_class_name = __namespace__.'comment';$comment = new $comment_class_name();?>
字符串形式調(diào)用問題
上面的動態(tài)調(diào)用的例子中,,我們看到了字符串形式的動態(tài)調(diào)用方式,如果要使用這種方式要注意兩個問題,。
1.使用雙引號的時候特殊字符可能被轉(zhuǎn)義 復制代碼代碼如下:
namespace blogarticle;class name { } //我是想調(diào)用blogarticlename $class_name = __namespace__.“name”;//但是n將被轉(zhuǎn)義為換行符 $name = new $class_name();//發(fā)生致命錯誤 ?>
2.不會認為是限定名稱
php在編譯腳本的時候就確定了元素所在的空間,,以及導入的情況。而在解析腳本時字符串形式調(diào)用只能認為是非限定名稱和完全限定名稱,,而永遠不可能是限定名稱,。復制代碼代碼如下:
namespace blog;//導入common類
use blogarticlecommon;//我想使用非限定名稱調(diào)用blogarticlecommon $common_class_name = 'common';//實際會被當作非限定名稱,也就表示當前空間的common類,,但我當前類沒有創(chuàng)建common類 $common = new $common_class_name();//發(fā)生致命錯誤:common類不存在 //我想使用限定名稱調(diào)用blogarticlecommon $common_class_name = 'articlecommon';//實際會被當作完全限定名稱,,也就表示article空間下的common類,但我下面只定義了blogarticle空間而不是article空間
$common = new $common_class_name();//發(fā)生致命錯誤:articlecommon類不存在
namespace blogarticle;class common { } ?> 總結(jié)
我對php的命名空間剛剛接觸,,也不能隨便給一些沒有實踐的建議,。我個人認為命名空間的作用和功能都很強大,,如果要寫插件或者通用庫的時候再也不用擔心重名問題,。不過如果項目進行到一定程度,要通過增加命名空間去解決重名問題,,我覺得工作量不會比重構(gòu)名字少,。也不得不承認它的語法會對項目增加一定的復雜度,因此從項目一開始的時候就應該很好的規(guī)劃它,,并制定一個命名規(guī)范,。
標準命名空間 命名空間的概念及作用篇二
進行對象的序列化,的引用,,ization命名空間,。這樣就可以在文件中使用序列化所需要的各種特性了。
imports ization
如果對xml serialization缺少了解,,請首先參考拙文:中實現(xiàn)對象序列化
2005-04-05
對象序列化
上面的例子包含了典型的xml中常見的各種元素:xml聲明,、xml根節(jié)點、xml節(jié)點,、xml屬性,、xml集合。除xml聲明外,,中都有對應的特性用于定義這些元素,。這些特性包括:xmlrootattribute,、xmltypeattribute、xmlelementattribute,、xmlattributeattribute,、xmlarrayattribute和xmlarrayitemattribute。另外,,還有兩個常用的特性,,xmlignoreattribute用于標記在對象序列化時需要被忽略的部分,xmlincludeattribute用于標記在生成xml schema時需要包括的類型,。如果沒有顯式地標記任何特性,,那么默認類的特性為xmltypeattribute、類成員的特性為xmlelementattribute,,且名稱為類或類成員的名稱,。例如:
public class order
public id as string
public orderdate as string
end class
如果不做任何特性標記,使用下面的代碼序列化時: dim o as new order
with o
.id = 123456
.orderdate = tdatestring
end with
dim writer as new xmltextwriter(“”, 8)
dim serializer as new xmlserializer(gettype(order))
ting = ed ize(writer, o)
序列化后的xml為:
123456
2005-4-11
可以看到,,對應order類,,而
和
分別對應order類中的字段id和orderdate。另外,,多了一個xml聲明和兩個xml命名空間,。
自動添加的,但是encoding是在xmltextwriter中指定的,,如果不指定encoding,,那么xml聲明只有。 1.1,,這個版本中只支持xml 1.0版本,。另外,如果不指定encoding,,那么默認的編碼可能也是utf8(沒找到相關(guān)的資料),。
.net默認為order類添加了xmlschema和xmlschema-instance兩個w3c的命名空間。該命名空間也可以自己指定,,方法是使用xmlserializer的另一個serialize方法,。
dim ns as new xmlserializernamespaces (“", ”“)ting = ed ize(writer, o, ns)
要將類序列化為xml節(jié)點:
_
public class order‘ any code class
要將類序列化為xml根節(jié)點:
_
public class order‘ any code class
當在類中同時使用xmlrootattribute、xmltypeattribute時,,序列化文檔中的類型以xmlrootattribute為準:
_
public class order‘ any code class
要將類成員序列化為xml節(jié)點:
_
public id as string要將類成員序列化為xml屬性:
_
public id as string要將類成員序列化為xml集合:
_
public class order_
public id as stringpublic orderdate as string
_
public items as new arraylistend class
_
public class orderitempublic name as string
end class
使用特性的一個好處是:可以在代碼和序列化的文檔中使用不同的編碼規(guī)范,。
標準命名空間 命名空間的概念及作用篇三
基本技能 12.5:命名空間
我們曾經(jīng)在第一章中對命名空間進行簡單的介紹。這里我們將對命名空間進行深入的討論,。使用命名空間的目的是對標識符的名稱進行本地化,,以避免命名沖突。在c++中,變量,、函數(shù)和類都是大量存在的,。如果沒有命名空間,這些變量,、函數(shù),、類的名稱將都存在于全局命名空間中,會導致很多沖突,。比如,,如果我們在自己的程序中定義了一個函數(shù)toupper(),這將重寫標準庫中的toupper()函數(shù),,這是因為這兩個函數(shù)都是位于全局命名空間中的,。命名沖突還會發(fā)生在一個程序中使用兩個或者更多的第三方庫的情況中。此時,,很有可能,,其中一個庫中的名稱和另外一個庫中的名稱是相同的,這樣就沖突了,。這種情況會經(jīng)常發(fā)生在類的名稱上,。比如,我們在自己的程序中定義了一個stack類,,而我們程序中使用的某個庫中也可能定義了一個同名的類,,此時名稱就沖突了。
namespace關(guān)鍵字的出現(xiàn)就是針對這種問題的,。由于這種機制對于聲明于其中的名稱都進行了本地化,,就使得相同的名稱可以在不同的上下文中使用,而不會引起名稱的沖突,?;蛟S命名空間最大的受益者就是c++中的標準庫了。在命名空間出現(xiàn)之前,,整個c++庫都是定義在全局命名空間中的(這當然也是唯一的命名空間)。引入命名空間后,,c++庫就被定義到自己的名稱空間中了,,稱之為std。這樣就減少了名稱沖突的可能性,。我們也可以在自己的程序中創(chuàng)建自己的命名空間,,這樣可以對我們認為可能導致沖突的名稱進行本地化。這點在我們創(chuàng)建類或者是函數(shù)庫的時候是特別重要的,。命名空間基礎
namespace關(guān)鍵字使得我們可以通過創(chuàng)建作用范圍來對全局命名空間進行分隔,。本質(zhì)上來講,一個命名空間就定義了一個范圍。定義命名空間的基本形式如下:
namespace 名稱{//聲明}
在命名空間中定義的任何東西都局限于該命名空間內(nèi),。
下面就是一個命名空間的例子,,其中對一個實現(xiàn)簡單遞減計數(shù)器的類進行了本地化。在該命名空間中定義了計數(shù)器類用來實現(xiàn)計數(shù),;其中的upperbound和lowerbound用來表示計數(shù)器的上界和下界,。//演示命名空間
namespace counternamespace {
int upperbound;
int lowerbound;
class counter {
int count;
public:
counter(int n){
if(n <= upperbound){
count = n;}
else {
count = upperbound;} }
void reset(int n){
if(n < upperbound){
count = n;} }
int run(){
if(count > lowerbound){
return count--;}
else
{
return lowerbound;
}
} };}
其中的upperbound,lowerbound和類counter都是有命名空間counternamespace定義范圍的組成部分,。
在命名空間中聲明的標識符是可以被直接引用的,,不需要任何的命名空間的修飾符。例如,,在counternamesapce命名空間中,,run()函數(shù)中就可以直接在語句中引用lowerbound:
if(count > lowerbound){
return count--;}
然而,既然命名空間定義了一個范圍,,那么我們在命名空間之外就需要使用范圍解析運算符來引用命名空間中的對象,。例如,在命名空間counternamespace定義的范圍之外給upperbound賦值為10,,就必須這樣寫:
counternamespace::upperbound = 10;或者在counternamespace定義的范圍之外想要聲明一個counter類的對象就必須這樣寫:
counternamespace::counter obj;
一般來講,,在命名空間之外想要訪問命名空間內(nèi)部的成員需要在成員前面加上命名空間和范圍解析運算符。
下面的程序演示了如何使用counternamespace這個命名空間: //演示命名空間
#include
using namespace std;
namespace counternamespace {int upperbound;
int lowerbound;
class counter {
int count;
public:
counter(int n){
if(n <= upperbound){
count = n;}
else {
count = upperbound;} }
void reset(int n){
if(n < upperbound){
count = n;} }
int run(){
if(count > lowerbound){
return count--;} else
return lowerbound;} };}
int main(){
counternamespace::upperbound = 100;
counternamespace::lowerbound = 0;
counternamespace::counter ob1(10);
int i;
do {
i = ();
cout << i << “ ”;
} while(i > counternamespace::lowerbound);
cout << endl;
counternamespace::counter ob2(20);
do {
i = ();
cout << i << “ ”;
} while(i > counternamespace::lowerbound);
cout << endl;
(100);
do {
i = ();
cout << i << “ ”;
} while(i > counternamespace::lowerbound);
cout << endl;
return 0;}
請注意:counter類以及upperbound和lowerbound的引用都是在前面加上了counternamespace修飾符,。但是,,一旦聲明了counter類型的對象,就沒有必須在對該對象的任何成員使用這種修飾符了,。()是可以被直接調(diào)用的,。其中的命名空間是可以被解析的。
相同的空間名稱是可以被多次聲明的,這種聲明向相互補充的,。這就使得命名空間可以被分割到幾個文件中甚至是同一個文件的不同地方中,。例如:
namespace ns {
int i;}
//...namespace ns {
int j;}
其中命名空間ns被分割成兩部分,但是兩部分的內(nèi)容卻是位于同一命名空間中的,。也就是ns,。最后一點:命名空間是可以嵌套的。也就是說可以在一個命名空間內(nèi)部聲明另外的命名空間,。using關(guān)鍵字
如果在程序中需要多次引用某個命名空間的成員,,那么按照之前的說法,我們每次都要使用范圍解析符來指定該命名空間,,這是一件很麻煩的事情,。為了解決這個問題,人們引入了using關(guān)鍵字,。using語句通常有兩種使用方式:
using namespace 命名空間名稱;using 命名空間名稱::成員,;
第一種形式中的命名空間名稱就是我們要訪問的命名空間。該命名空間中的所有成員都會被引入到當前范圍中。也就是說,,他們都變成當前命名空間的一部分了,,使用的時候不再需要使用范圍限定符了。第二種形式只是讓指定的命名空間中的指定成員在當前范圍中變?yōu)榭梢?。我們用前面的counternamespace來舉例,下面的using語句和賦值語句都是有效的:
using counternamespace::lowerbound;//只有l(wèi)owerbound當前是可見的 lowerbound = 10;//這樣寫是合法的,,因為lowerbound成員當前是可見的 using counternamespace;//所有counternamespace空間的成員當前都是可見的
upperbound = 100;//這樣寫是合法的,因為所有的counternamespace成員目前都是可見的
下面是我們對之前的程序進行修改的結(jié)果: //使用using
#include
using namespace std;namespace counternamespace { int upperbound;
int lowerbound;
class counter {
int count;
public:
counter(int n){
if(n < upperbound){
count = n;}
else {
count = upperbound;} }
void reset(int n){
if(n <= upperbound){
count = n;} }
int run(){
if(count > lowerbound){
return count--;}
else {
return lowerbound;} } };}
int main(){
//這里只是用counternamespace中的upperbound using counternamespace::upperbound;
//此時對upperbound的訪問就不需要使用范圍限定符了 upperbound = 100;
//但是使用lowerbound的時候,還是需要使用范圍限定符的 counternamespace::lowerbound = 0;counternamespace::counter ob1(10);
int i;
do {
i = ();cout << i << “ ”;
}while(i > counternamespace::lowerbound);cout << endl;
//下面我們將使用整個counternamespace的命名空間
using namespace counternamespace;counter ob2(20);
do {
i = ();cout << i << “ ”;
}while(i > counternamespace::lowerbound);cout << endl;
(100);lowerbound = 90;
do {
i = ();cout << i << “ ”;}while(i > lowerbound);
return 0;}
上面的程序還為我們演示了重要的一點:當我們用using引入一個命名空間的時候,,如果之前有引用過別的命名空間(或者同一個命名空間),則不會覆蓋掉對之前的引入,,而是對之前引入內(nèi)容的補充,。也就是說,,到最后,上述程序中的std和counternamespace這兩個命名空間都變成全局空間了,。沒有名稱的命名空間
有一種特殊的命名空間,,叫做未命名的命名空間,。這種沒有名稱的命名空間使得我們可以創(chuàng)建在一個文件范圍里可用的命名空間。其一般形式如下: namespace {
//聲明 }
我們可以使用這種沒有名稱的命名空間創(chuàng)建只有在聲明他的文件中才可見的標識符。也即是說,,只有在聲明這個命名空間的文件中,,它的成員才是可見的,,它的成員才是可以被直接使用的,,不需要命名空間名稱來修飾,。對于其他文件,,該命名空間是不可見的,。我們在前面曾經(jīng)提到過,,把全局名稱的作用域限制在聲明他的文件的一種方式就是把它聲明為靜態(tài)的。盡管c++是支持靜態(tài)全局聲明的,,但是更好的方式就是使用這里的未命名的命名空間,。std命名空間
標準c++把自己的整個庫定義在std命名空間中。這就是本書的大部分程序都有下面代碼的原因:
using namespace std;
這樣寫是為了把std命名空間的成員都引入到當前的命名空間中,,以便我們可以直接使用其中的函數(shù)和類,,而不用每次都寫上std::。
當然,,我們是可以顯示地在每次使用其中成員的時候都指定std::,,只要我們喜歡。例如,,我們可以顯示地采用如下語句指定cout:
std::cout << “顯示使用std::來指定cout”;
如果我們的程序中只是少量地使用了std命名空間中的成員,或者是引入std命名空間可能導致命名空間的沖突的話,我們就沒有必要使用using namespace std;了,。然而,,如果在程序中我們要多次使用std命名空間的成員,,則采用using namespace std;的方式把std命名空間的成員都引入到當前命名空間中會顯得方便很多,,而不用每次都單獨在使用的時候顯示指定,。
標準命名空間 命名空間的概念及作用篇四
c++命名空間namespace 雖然使用命名空間的方法,,有多種可供選擇。但是不能貪圖方便,,一味使用using 指令,,這樣就完全背離了設計命名空間的初衷,也失去了命名空間應該具有的防止名稱沖突的功能,。
一般情況下,對偶爾使用的命名空間成員,,應該使用命名空間的作用域解析運算符來直接給名稱定位,。而對一個大命名空間中的經(jīng)常要使用的少數(shù)幾個成員,提倡使用using聲明,,而不應該使用using編譯指令,。只有需要反復使用同一個命名空間的許多數(shù)成員時,使用using編譯指令,,才被認為是可取的,。
例如,如果一個程序()只使用一兩次cout,而且也不使用std命名空間中的其他成員,,則可以使用命名空間的作用域解析運算符來直接定位,。如: #include
……
std::cout << “hello, world!” << std::endl;std::cout << “outer::i = ” << outer::i << “, inner::i = ” << outer::inner::i << std::endl;又例如,如果一個程序要反復使用std命名空間中的cin,、cout和cerr(),,而不怎么使用其他std命名空間中的其他成員,則應該使用using 聲明而不是using指令,。如:
#include
……
using std::cout;cout << “hello, world!” << endl;cout << “outer::i = ” << outer::i << “, inner::i = ” << outer::inner::i << endl;4)命名空間的名稱l
命名空間別名 標準c++引入命名空間,,主要是為了避免成員的名稱沖突。若果用戶都給自己的命名空間取簡短的名稱,,那么這些(往往同是全局級的)命名空間本身,,也可能發(fā)生名稱沖突。如果為了避免沖突,,而為命名空間取很長的名稱,,則使用起來就會不方便。這是一個典型的兩難問題,。
標準c++為此提供了一種解決方案——命名空間別名,,格式為: namespace 別名 = 命名空間名;例如:(at&t美國電話電報公司)
namespace american_telephone_and_telegraph { // 命名空間名太長
class string {
string(const char*);
// ……
} }
american_telephone_and_telegraph::string s1 // 使用不方便
= new american_telephone_and_telegraph::string(“grieg”);
namespace att = american_telephone_and_telegraph;// 定義別名
att::string s2 = new att::string(“bush”);// 使用方便 att::string s3 = new att::string(“nielsen”);
l
無名命名空間 標準c++引入命名空間,除了可以避免成員的名稱發(fā)生沖突之外,,還可以使代碼保持局部性,,從而保護代碼不被他人非法使用。如果你的目的主要是后者,,而且又為替命名空間取一個好聽,、有意義、且與別人的命名空間不重名的名稱而煩惱的話,,標準c++還允許你定義一個無名命名空間,。你可以在當前編譯單元中(無名命名空間之外),直接使用無名命名空間中的成員名稱,,但是在當前編譯單元之外,,它又是不可見的。無名命名空間的定義格式為: namespace {
聲明序列可選 } 實際上,,上面的定義等價于:(標準c++中有一個隱含的使用指令)
namespace $$$ {
聲明序列可選 } using namespace $$$;例如: namespace {
int i;
void f(){/*……*/} } int main(){
i = 0;// 可直接使用無名命名空間中的成員i
f();// 可直接使用無名命名空間中的成員f()}
標準命名空間 命名空間的概念及作用篇五
本講基本要求
* 掌握:命名空間的作用及定義,;如何使用命名空間。
* 了解:使用早期的函數(shù)庫
重點,、難點
◆命名空間的作用及定義,;如何使用命名空間。
在學習本書前面各章時,,讀者已經(jīng)多次看到在程序中用了以下語句: using namespace std,;
這就是使用了命名空間std,。在本講中將對它作較詳細的介紹。
一,、為什么需要命名空間(問題提出)
命名空間是ansic++引入的可以由用戶命名的作用域,,用來處理程序中常見的同名沖突。
在c語言中定義了3個層次的作用域,,即文件(編譯單元),、函數(shù)和復合語句。c++又引入了類作用域,,類是出現(xiàn)在文件內(nèi)的,。在不同的作用域中可以定義相同名字的變量,互不于擾,,系統(tǒng)能夠區(qū)別它們,。
1、全局變量的作用域是整個程序,,在同一作用域中不應有兩個或多個同名的實體(enuty),,包括變量、函數(shù)和類等,。
例:如果在文件中定義了兩個類,,在這兩個類中可以有同名的函數(shù)。在引用時,,為了區(qū)別,,應該加上類名作為限定: class a //聲明a類
{ public:
void funl();//聲明a類中的funl函數(shù)
private:
int i,; };
void a::funl()//定義a類中的funl函數(shù)
{????}
class b //聲明b類
{ public:
void funl(),; //b類中也有funl函數(shù)
void fun2(),; };
void b::funl()//定義b類中的funl函數(shù)
{ ????} 這樣不會發(fā)生混淆,。
在文件中可以定義全局變量(global variable),,它的作用域是整個程序。如果在文件a中定義了一個變量a int a=3,;
在文件b中可以再定義一個變量a int a=5;在分別對文件a和文件b進行編譯時不會有問題,。但是,如果一個程序包括文件a和文件b,,那么在進行連接時,,會報告出錯,因為在同一個程序中有兩個同名的變量,,認為是對變量的重復定義,。
可以通過extern聲明同一程序中的兩個文件中的同名變量是同一個變量。如果在文件b中有以下聲明: extem int a;
表示文件b中的變量a是在其他文件中已定義的變量,。由于有此聲明,,在程序編譯和連接后,文件a的變量a的作用域擴展到了文件b,。如果在文件b中不再對a賦值,,則在文件b中用以下語句輸出的是文件a中變量a的值: cout<
2、程序中就會出現(xiàn)名字沖突,。
在簡單的程序設計中,,只要人們小心注意,可以爭取不發(fā)生錯誤,。但是,,一個大型的應用軟件,往往不是由一個人獨立完成的,,而是由若干人合作完成的,,不同的人分別完成不同的部分,最后組合成一個完整的程序,。假如不同的人分別定義了類,,放在不同的頭文件中,在主文件(包含主函數(shù)的文件)需要用這些類時,,就用#include命令行將這些頭文件包含進來,。由于各頭文件是由不同的人設計的,有可能在不同的頭文件中用了相同的名字來命名所定義的類或函數(shù),。例4 名字沖突
程序員甲在頭文件headerl.h中定義了類student和函數(shù)fun,。//例4中的頭文件header1(頭文件1,沒其文件名為cc8-4-h1.h)#include
#include
using namespace std;class student //聲明student類
{ public: student(int n,string nam,int a){ num=n;name=nam;age=a;} void get_data();private: int num;string name;int age;};void student::get_data()//成員函數(shù)定義 { cout<wang 18 2.82843 如果程序員乙寫了頭文件header2.h,,在其中除了定義其他類以外,,還定義了類student和函數(shù)fun,但其內(nèi)容與頭文件headerl.h中的student和函數(shù)fun有所不同,。//例4中的頭文件header2 #include
#include
using namespace std;class student //聲明student類 { public: student(int n,string nam,char s)//參數(shù)與headerl中的student不同
{ num=n;name=nam;sex=s;} void get_data();private: int num;string name;char sex;};//此項與headerl不同void student::get_data()//成員函數(shù)定義 { cout<
double fun(double a,double b)//定義全局函數(shù)
{ return sqrt(a-b);} //返回值與headerl中的fun函數(shù)不同 //頭文件2中可能還有其他內(nèi)容
假如主程序員在其程序中要用到headerl.h中的student和函數(shù)fun,,因而在程序中包含了頭文件headerl.h,同時要用到頭文件header2.h中的一些內(nèi)容(但對header2.h中包含與headerl.h中的student類和fun函數(shù)同名而內(nèi)容不同的類和函數(shù)并不知情,,因為在一個頭文件中往往包含許多不同的信息,,而使用者往往只關(guān)心自己所需要的部分,而不注意其他內(nèi)容),,因而在程序中又包含了頭文件header2.h,。如果主文件(包含主函數(shù)的文件)如下: #include
using namespace std;#include ”header1.h“//包含頭文件l #include ”header2.h“//包含頭文件2 int main(){ student stud1(101,”wang“,18);_data();cout<
3、全局命名空間污染(global namespace pollution),。
在程序中還往往需要引用一些庫(包括c++編譯系統(tǒng)提供的庫,、由軟件開發(fā)商提供的庫或者用戶自己開發(fā)的庫),,為此需要包含有關(guān)的頭文件。如果在這些庫中包含有與程序的全局實體同名的實體,,或者不同的庫中有相同的實體名,,則在編譯時就會出現(xiàn)名字沖突。
為了避免這類問題的出現(xiàn),,人們提出了許多方法,,例如:將實體的名字寫得長—些(包含十幾個或幾十個字母和字符);把名字起得特殊一些,,包括一些特殊的字符,;由編譯系統(tǒng)提供的內(nèi)部全局標識符都用下劃線作為前綴,如_complex(),,以避免與用戶命名的實體同名,;由軟件開發(fā)商提供的實體的名字用特定的字符作為前綴。但是這樣的效果并不理想,,而且增加了閱讀程序的難度,,可讀性降低了。c語言和早期的c++語言沒有提供有效的機制來解決這個問題,,沒有使庫的提供者能夠建立自己的命名空間的工具。人們希望ansi c++標準能夠解決這個問題,,提供—種機制,、一種工具,使由庫的設計者命名的全局標識符能夠和程序的全局實體名以及其他庫的全局標識符區(qū)別開來,。
二,、什么是命名空間(解決方案)
命名空間:實際上就是一個由程序設計者命名的內(nèi)存區(qū)域,程序設計者可以根據(jù)需要指定一些有名字的空間域,,把一些全局實體分別放在各個命名空間中,,從而與其他全局實體分隔開來。
如: namespace ns1 //指定命名中間nsl
{ int a,;
double b;} namespace是定義命名空間所必須寫的關(guān)鍵字,nsl是用戶自己指定的命名空間的名字(可以用任意的合法標識符,,這里用ns1是因為ns是namespace的縮寫,,含義請楚),在花括號內(nèi)是聲明塊,,在其中聲明的實體稱為命名空間成員(namespace member)?,F(xiàn)在命名空間成員包括變量a和b,注意a和b仍然是全局變量,,僅僅是把它們隱藏在指定的命名空間中而已,。如果在程序中要使用變量a和b,,必須加上命名空間名和作用域分辨符“::”,如nsl::a,,nsl::b,。這種用法稱為命名空間限定(qualified),這些名字(如nsl::a)稱為被限定名(qualified name),。c++中命名空間的作用類似于操作系統(tǒng)中的目錄和文件的關(guān)系,,由于文件很多,不便管理,,而且容易重名,,于是人們設立若干子目錄,把文件分別放到不同的子目錄中,,不同子目錄中的文件可以同名,。調(diào)用文件時應指出文件路徑。
命名空間的作用:是建立一些互相分隔的作用域,,把一些全局實體分隔開來,。以免產(chǎn)生老點名叫李相國時,3個人都站起來應答,,這就是名字沖突,,因為他們無法辨別老師想叫的是哪一個李相國,同名者無法互相區(qū)分,。為了避免同名混淆,,學校把3個同名的學生分在3個班。這樣,,在小班點名叫李相國時,,只會有一個人應答。也就是說,,在該班的范圍(即班作用域)內(nèi)名字是惟一的,。如果在全校集合時校長點名,需要在全校范圍內(nèi)找這個學生,,就需要考慮作用域問題,。如果校長叫李相國,全校學生中又會有3人一齊喊“到”,,因為在同一作用域中存在3個同名學生,。為了在全校范圍內(nèi)區(qū)分這3名學生,校長必須在名字前加上班號,,如高三甲班的李相國,,或高三乙班的李相國,即加上班名限定,。這樣就不致產(chǎn)生混淆,。
可以根據(jù)需要設置許多個命名空間,,每個命名空間名代表一個不同的命名空間域,不同的命名空間不能同名,。這樣,,可以把不同的庫中的實體放到不同的命名空間中,或者說,,用不同的命名空間把不同的實體隱蔽起來,。過去我們用的全局變量可以理解為全局命名空間,獨立于所有有名的命名空間之外,,它是不需要用namespace聲明的,,實際上是由系統(tǒng)隱式聲明的,存在于每個程序之中,。
在聲明一個命名空間時,,花括號內(nèi)不僅可以包括變量,而且還可以包括以下類型: ·變量(可以帶有初始化),; ·常量,;
·數(shù)(可以是定義或聲明); ·結(jié)構(gòu)體,; ·類,; ·模板;
·命名空間(在一個命名空間中又定義一個命名空間,,即嵌套的命名空間),。例如
namespace nsl { const int rate=0.08; //常量 doublepay,;
//變量
doubletax()
//函數(shù)
{return a*rate,;} namespacens2
//嵌套的命名空間
{int age;} }
如果想輸出命名空間nsl中成員的數(shù)據(jù),,可以采用下面的方法: cout<
cout<
可以看到命名空間的聲明方法和使用方法與類差不多,。但它們之間有一點差別:在聲明類時在右花括號的后面有一分號,而在定義命名空間時,,花括號的后面沒有分號,。
三、使用命名空間解決名字沖突(使用指南)有了以上的基礎后,,就可以利用命名空間來解決名字沖突問題?,F(xiàn)在,對例4程序進行修改,,使之能正確運行。
例5 利用命名空間來解決例4程序名字沖突問題,。
修改兩個頭文件,,把在頭文件中聲明的類分別放在兩個不同的命名空間中,。//例8.5中的頭文件1,文件名為header1.h using namespace std;#include
#include
namespace ns1 //聲明命名空間ns1 { class student //在命名空間nsl內(nèi)聲明student類
{ public: student(int n,string nam,int a){ num=n;name=nam;age=a;} void get_data();private: int num;string name;int age;};void student::get_data()//定義成員函數(shù){ cout<
double fun(double a,double b)//在命名空間n引內(nèi)定義fun函數(shù) { return sqrt(a+b);} } //例8.5中的頭文件2,,文件名為header2.h #include
#include
namespace ns2 //聲明命名空間ns2 { class student { public: student(int n,string nam,char s){ num=n;name=nam;sex=s;} void get_data();private: int num;string name;char sex;};
void student::get_data(){ cout<double fun(double a,double b){ return sqrt(a-b);} } //main file #include
#include ”header1.h“ //包含頭文件l #include ”header2.h“ //包含頭文件2 int main(){ ns1::student stud1(101,”wang“,18);//用命名空間nsl中聲明的student類定義studt _data();//不要寫成ns1::_data(),;
cout<ns2::student stud2(102,”li“,'f');//用命名空間ns2中聲明的student類定義stud2 _data();cout<
分析例4程序出錯的原因是:在兩個頭文件中有相同的類名student和相同的函數(shù)名fun,在把它們包含在主文件中時,,就產(chǎn)生名字沖突,,存在重復定義。編譯系統(tǒng)無法辨別用哪一個頭文件中的student來定義對象studl?,F(xiàn)在兩個student和fun分別放在不同的命名空間中,,各自有其作用域,互不相干,。由于作用域不相同,,不會產(chǎn):生名字沖突。正如同在兩個不同的類中可以有同名的變量和函數(shù)而不會產(chǎn)生沖突一樣,。
在定義對象時用ns1::student(命名空間nsl中的student)來定義studl,,用ns2::student(命名空間ns2中的student)來定義stud2。顯然,,nsl::student和ns2::student是兩個不同的類,,不會產(chǎn)生混淆。同樣,,在調(diào)用fun函數(shù)時也需要用命名空間名ns]或ns2加以限定,。ns1::fun()和ns2::fun()是兩個不同的函數(shù)。注意:對象studl是用nsl::student定義的,,但對象studl并不在命名空間nsl中,。studl的作用域為main函數(shù)范圍內(nèi)。在調(diào)用對象studl的成員函數(shù)get_data時,,應寫成studl.get_data(),,而不應寫成nsl::_data()。程序能順利通過編譯,,并得到以下運行結(jié)果: 101 wang l9(對象studl中的數(shù)據(jù))2.82843(/5+3的值)102 li f(對象studg中的數(shù)據(jù))1.41421(/5-2的值)
四,、使用命名空間成員的方法 從上面的介紹可以知道,在引用命名空間成員時,,要用命名空間名和作用域分辨符對命名空間成員進行限定,,以區(qū)別不同的命名空間中的同名標識符。即: 命名空間名::命名空間成員名
這種方法是有效的,,能保證所引用的實體有惟一的名字,。但是如果命名空間名字比較長,尤其在有命名空間嵌套的情況下,,為引用一個實體,,需要寫很長的名字,。在一個程序中可能要多次引用命名空間成員,就會感到很不方便,。1,、使用命名空間別名
可以為命名空間起一個別名(namespace alias),用來代替較長的命名空間名,。如 namespace television //聲明命名空間,,名為television {...} 可以用一個較短而易記的別名代替它。如:
namespace tv=television,; //別名tv與原名television等價
也可以說,,別名tv指向原名television,在原來出現(xiàn)television的位置都可以無條件地用tv來代替,。
2,、使用using命名空間成員名
using后面的命名空間成員名必須是由命名空間限定的名字。例如: using nsl::student,; 以上語句聲明:在本作用域(using語句所在的作用域)中會用到命名空間ns1中的成員student,,在本作用域中如果使用該命名空間成員時,不必再用命名空間限定,。例如在用上面的using聲明后,,在其后程序中出現(xiàn)的student就是隱含地指nsl::student。
using聲明的有效范圍是從using語句開始到using所在的作用域結(jié)束,。如果在以上的using語句之后有以下語句:
student studl(101,”wang“,18),; //此處的student相當于ns1::student 上面的語句相當于
nsl::student studl(101,”wang“,,18),; 又如
using nsl::fun; //聲明其后出現(xiàn)的fun是屬于命名空間nsl中的fun cout<
但是要注意:在同一作用域中用using聲明的不同命名空間的成員中不能有同名的成員,。例如:
usmgnsl::student,; //聲明其后出現(xiàn)的student是命名空間nsl中的student usmgns2::student,; //聲明其后出現(xiàn)的student是命名空間ns2小的student student stud1; //請問此處的student是哪個命名中間中的student? 產(chǎn)生了二義性,編譯出錯,。
3,、使用using namespace命名空間名
用上面介紹的using命名空間成員名,,一次只能聲明一個命名空間成員,,如果在一個命名空間中定義了10個實體,就需要使用10次using命名空間成員名,。能否在程序中用一個語句就能一次聲明一個命名空間中的全部成員呢? c++提供了using namespace語句來實現(xiàn)這一目的,。using namespace語句的一般格式為 using namespace 命名空間名; 例如
using nanlespace nsl;
聲明了在本作用域中要用到命名空間nsl中的成員,,在使用該命名空間的任何成員時都不必用命名空間限定,。如果在作了上面的聲明后有以下語句: student studl(101,”wang”,,18); //student隱含指命名中間nsl中的student cout<
cout<
student stud2(102,,“l(fā)i”,,'r'); _data(),;
coutt<
五,、無名的命名空間
以上介紹的是有名字的命名空間,c++還允許使用沒有名字的命名空間,,如在文件a中聲明了以下的無名命名空間:
namespace //命名空間沒有名字 { void fun()//定義命名空間成員 { cout<<“ok.”<
則執(zhí)行無名命名空間中的成員fun函數(shù),,輸出”ok.”。
在本程序中的其他文件中也無法使用該fun函數(shù),,也就是把fun函數(shù)的作用域限制在本文件范圍中,。可以聯(lián)想到:在c浯言中可以用static聲明一個函數(shù),,其作用也是使該函數(shù)的作用域限于本文件,。c++保留了用static聲明函數(shù)的用法,同時提供了用無名命名空間來實現(xiàn)這一功能,。隨著越來越多的c++編譯系統(tǒng)實現(xiàn)了ansi c++建議的命名空間的機制,,相信使用無名命名空間成員的方法將會取代以前習慣用的對全局變量的靜態(tài)聲明。
六,、標準命名空間std 為了解決c++標準庫中的標識符與程序中的全局標識符之間以及不同庫中的標識符之間的同名沖突,,應該將不同庫的標識符在不同的命名空間中定義(或聲明)。標準c++庫的所有的標識符都是在一個名為std的命名空間中定義的,,或者說標準頭文件(如iostream)中函數(shù),、類、對象和類模板是在命名空間std中定義的,。std是standard(標準)的縮寫,,表示這是存放標準庫的有關(guān)內(nèi)容的命名空間,含義請楚,,不必死記,。
這樣,在程序中用到c++標準庫時,,需要使用std作為限定,。如
std::cout<<“ok.”<
這樣,在std中定義和聲明的所有標識符在本文件中都可以作為全局量來使用。但是應當絕對保證在程序中不出現(xiàn)與命名空間std的成員同名的標識符,,例如在程序中不能再定義一個名為cout的對象,。由于在命名空間std中定義的實體實在太多,有時程序設計人員也弄不請哪些標識符已在命名空間std中定義過,,為減少出錯機會,,有的專業(yè)人員喜歡用若干個"using命名空間成員”聲明來代替“using namespace命名空間”聲明,如 using std::string,; using std::cout,; using std::cin;
等,。為了減少在每一個程序中都要重復書寫以亡的using聲明,,程序開發(fā)者往往把編寫應用程序時經(jīng)常會用到的命名空間std成員的usmg聲明組成一個頭文件,然后在程序中包含此頭文件即可,。
如果閱讀了多種介紹c++的書,,可能會發(fā)現(xiàn)有的書的程序中有using namespace語句,有的則沒有,。有的讀者會提出:究竟應該有還是應該沒有?應當說:用標準的c++編程,,是應該對命名空間std的成員進行聲明或限定的(可以采取前面介紹過的任一種方法)。但是目前所用的c++庫大多是幾年前開發(fā)的,,當時并沒有命名空間,,庫中的有關(guān)內(nèi)容也沒有放在std命名空間中,因而在程序中不必對std進行聲明,。
七,、使用早期的函數(shù)庫
c語言程序中各種功能基本上都是由函數(shù)來實現(xiàn)的,在c語言的發(fā)展過程中建立了功能豐富的函數(shù)庫,,c++從c語言繼承了這份寶貴的財富,。在c++程序中可以使用c語言的函數(shù)庫。如果要用函數(shù)庫中的函數(shù),,就必須在程序文件中包含有關(guān)的頭文件,,在不同的頭文件中,包含了不同的函數(shù)的聲明,。
在c++中使用這些頭文件有兩種方法,。
1、用c語言的傳統(tǒng)方法
頭文件名包括后綴.h,,如stdio.h,,math.h等。由于c語言沒有命名空間,,頭文件并不存放在命名空間中,,因此在c++程序文件中如果用到帶后綴.h的頭文件時,,不必用命名空間。只需在文件中包含所用的頭文件即可,。如 #include
2,、用c++的新方法c++標準要求系統(tǒng)提供的頭文件不包括后綴.h,例如iostream,、string,。為了表示與c語言的頭文件有聯(lián)系又有區(qū)別,c++所用的頭文件名是在c語言的相應的頭文件名(但不包括后綴.h)之前加一字母c,。例如,,c語言中有關(guān)輸入與輸出的頭文件名為stdio.h在c++中相應的頭文件名為cstdio。c語言中的頭文件math.h,,在c++中相應的頭文什名為cmath。c語言中的頭文件string.h在c++中相應的頭文件名為cstring,。注意在c++中,,頭文件cstnng和頭文件strmg不是同一個文件。前者提供c語言中對字符串處理的有關(guān)函數(shù)(如strcmp,,ctrcpy)的聲明,,后者提供c++中對字符串處理的新功能。此外,,由于這些函數(shù)都是在命名空間std中聲明的,,因此在程序中要對命名空間std作聲明。如:
#include
#include
using namespace std,;
目前所用的大多數(shù)c++編譯系統(tǒng)既保留了c的用法,,又提供丁c++的新方法。下面兩種用法等價,,可以任選,。c傳統(tǒng)方法 c++新方法#include
#include
#include
#include
#include
#include
using namespace std;