• <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>
            隨筆 - 74, 文章 - 0, 評論 - 26, 引用 - 0
            數據加載中……

            劃分全局名字空間

            ?
            ?

            條款28: 劃分全局名字空間

            全局空間最大的問題在于它本身僅有一個。在大的軟件項目中,經常會有不少人把他們定義的名字都放在這個單一的空間中,從而不可避免地導致名字沖突。例如,假設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();???????????? // 為什么函數要這樣聲明?
            ?????????????????????????????????????? // 參見條款47

            而要這么做:

            namespace sdm {
            ? const double book_version = 2.0;
            ? class handle { ... };
            ? handle& gethandle();
            }

            用戶于是可以通過三種方法來訪問這一名字空間里的符號:將名字空間中的所有符號全部引入到某一用戶空間;將部分符號引入到某一用戶空間;或通過修飾符顯式地一次性使用某個符號:

            void f1()
            {
            ? using namespace sdm;?????????? // 使得sdm中的所有符號不用加
            ???????????????????????????????? // 修飾符就可以使用

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

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

            }

            void f2()
            {
            ? using sdm::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
            ????????????????????????????????? // 都沒有引入到本空間
            ? ...???????????????????????????

            }

            (有些名字空間沒有名字。這種沒命名的名字空間一般用于限制名字空間內部元素的可見性。詳見條款m31。)

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

            namespace acmewindowsystem {

            ? ...

            ? typedef int handle;

            ? ...

            }

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

            void f()
            {
            ? using namespace sdm;???????????????? // 引入sdm里的所有符號
            ? using namespace acmewindowsystem;??? // 引入acme里的所有符號

            ? ...????????????????????????????????? // 自由地引用sdm
            ?????????????????????????????????????? // 和acme里除handle之外
            ?????????????????????????????????????? // 的其它符號

            ? handle h;??????????????????????????? // 錯誤! 哪個handle?

            ? sdm::handle h1;????????????????????? // 正確, 沒有二義

            ? acmewindowsystem::handle h2;???????? // 也沒有二義

            ? ...

            }

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

            名字空間的概念加入到c++標準的時間相對較晚,所以有些人會認為它不太重要,可有可無。但這種想法是錯誤的,因為c++標準庫(參見條款49)里幾乎所有的東西都存在于名字空間std之中。這可能令你不以為然,但它卻以一種直接的方式影響到你:這就是為什么c++提供了那些看起來很有趣的、沒有擴展名的頭文件,如<iostream>, <string>等。詳細介紹參見條款49。

            由于名字空間的概念引入的時間相對較晚,有些編譯器可能不支持。就算是這樣,那也沒理由污染全局名字空間,因為可以用struct來近似實現namespace??梢赃@樣做:先創建一個結構用以保存全局符號名,然后將這些全局符號名作為靜態成員放入結構中:

            // 用于模擬名字空間的一個結構的定義
            struct sdm {
            ? static const double book_version;
            ? class handle { ... };
            ? static handle& gethandle();
            };

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

            現在,如果有人想訪問這些全局符號名,只用簡單地在它們前面加上結構名作為前綴:

            void f()
            {
            ? 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;

            老實說,如果讀了條款47,你就會不喜歡定義一個象book_version這樣的非局部靜態對象。(你就會用條款47中所介紹的函數來取代這樣的對象)

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

            sdm::handle& (* const gethandle)() =????? // gethandle是指向sdm::gethandle
            ? sdm::gethandle;???????????????????????? // 的const 指針 (見條款21)

            注意gethandle是一個常指針。因為你當然不想讓你的用戶將它指向別的什么東西,而不是sdm::gethandle,對不對?

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

            sdm::handle& (&gethandle)() =????? // gethandle是指向
            ? sdm::gethandle;????????????????? // sdm::gethandle的引用

            我個人認為這樣的做法也很好,但你可能以前從沒見到過。除了初始化的方式外,函數的引用和函數的常指針在行為上完全相同,只是函數指針更易于理解。)

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

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

            // 定義一個模擬名字空間的結構,結構內部包含widgets的類型
            // 和函數。widgets對象支持operator+進行加法運算
            struct widgets {
            ? class widget { ... };


            ? // 參見條款21:為什么返回const
            ? static const widget operator+(const widget& lhs,
            ??????????????????????????????? const widget& rhs);

            ? ...

            };

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

            typedef widgets::widget widget;


            const widget (* const operator+)(const widget&,??????? // 錯誤!
            ???????????????????????????????? const widget&);?????? // operator+不能是指針名
            ?

            widget w1, w2, sum;

            sum = w1 + w2;?????????????????????????? // 錯誤! 本空間沒有聲明
            ???????????????????????????????????????? // 參數為widgets 的operator+

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

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

            posted on 2006-06-29 15:09 井泉 閱讀(233) 評論(0)  編輯 收藏 引用 所屬分類: C++

            99久久伊人精品综合观看| 久久亚洲私人国产精品vA| 91亚洲国产成人久久精品| 伊人色综合久久| 日韩欧美亚洲综合久久| 日产精品久久久久久久| 香蕉久久夜色精品国产小说| 精品久久久久久无码免费| 国产精品久久久久久久app| 久久精品无码一区二区无码| 久久久久无码精品| www性久久久com| 久久只这里是精品66| 粉嫩小泬无遮挡久久久久久| 国内精品伊人久久久影院| 国产精品久久久久影院嫩草| 无码精品久久一区二区三区| 99久久国语露脸精品国产| 一级a性色生活片久久无| 国产成人AV综合久久| 久久久久亚洲AV成人片| 亚洲国产精品综合久久网络| 久久九九亚洲精品| 人妻无码αv中文字幕久久琪琪布| 久久久久99精品成人片| av国内精品久久久久影院| 国产69精品久久久久APP下载| 激情久久久久久久久久| 久久99精品国产自在现线小黄鸭 | 精品亚洲综合久久中文字幕| 欧美激情精品久久久久久久九九九| 国产99精品久久| 一本一道久久综合狠狠老| 久久久噜噜噜久久中文字幕色伊伊| 久久精品成人| 久久中文字幕视频、最近更新| 777久久精品一区二区三区无码| 久久免费国产精品一区二区| 国产精品久久久久国产A级| 久久婷婷激情综合色综合俺也去| 人妻无码αv中文字幕久久琪琪布 人妻无码久久一区二区三区免费 人妻无码中文久久久久专区 |