靈活而奇特的C++語言特性——作用域解析(一)

       學習了博主的《漫談繼承技術》系列博文之後,相信大家都有所收穫吧!這次博主將和大家一起探討 《靈活而奇特的C++語言特性》 ,主要包括引用、常量(const)、常量表達式(constexpr)、靜態(static)、外部(expert)、類型定義(typedef)、類型別名(aliases)、類型轉換、作用域解析、統一初始化、顯示轉換運算符、特性(attribute)、用戶自定義文本、頭文件、可變長度參數列表和預處理器宏。儘管這個知識清單顯得有點凌亂,但是這些話題都是博主經過精心挑選,是容易混淆的語言特性。本篇我們來學習一下作用域解析的觀念以及使用場景,增進大家對《靈活而奇特的C++語言特性》的理解。

       C++程序猿必須熟悉作用域(scope)的概念。程序中的所有名稱,包括變量、函數和類名,都具有某種作用域。可以使用名稱空間、函數定義、花括號界定的塊和類定義創建作用域。當試圖訪問某個變量、函數或者類時,首先在最近作用域中查找這個名稱,然後是相鄰的作用域,依次類推,知道全局作用域。任何不在有名名稱空間、函數、花括號界定的塊和類中的名稱都被認爲在全局作用域中。如果在全局作用域中也找不到這個名稱,編譯器會給出一個未定義符號錯誤。那什麼是作用域運算符呢?作用域運算符就是“::”。下面就讓我們一起先結合作用域和可見性來探討一下作用域運算符的使用。

作用域

       作用域是一個標識符在程序正文中有效的區域。C++中標識符的作用域有函數原型作用域局部作用域(塊作用域)類作用域命名空間作用域

函數原型作用域

在函數原型聲明時形式參數的作用範圍就是函數原型作用域。

如:double area(double radius);//函數聲明

       標識符radius的作用範圍在函數area形參列表的左右括號之間,在程序的其他地方不能引用這個標識符。注意,由於在函數原型的形參列表中起作用的只是形參類型,形參標識符並不起作用,因此形參標示符是允許省去的。但是考慮到程序的可讀性,通常還是要在函數原型聲明時給出形參標識符。


局部作用域(塊作用域)

       在函數內部一對大括號中聲明的變量的作用域爲局部作用域。在函數定義時,形參列表中的參數屬於該函數的局部變量,作用域爲局部作用域。


類作用域

       類的成員函數可以訪問類的所有數據成員和成員函數。在類外,類的對象可以訪問類的公有成員。公有成員,在類外也不能直接使用,必須通過類的對象來訪問。對於類的靜態公有成員,可以通過類名加作用域解析運算符來訪問或者通過對象來訪問。


命名空間作用域

命名空間定義:

namespace 命名空間名

{

         命名空間內的各種聲明(函數聲明、類聲明、變量聲明……)

}

       一個命名空間確定了一個命名空間作用域,凡是在該命名空間之內聲明的、不屬於前面所述各種作用域的標識符,都屬於該命名空間作用域。在該命名空間內部可以直接引用當前命名空間中聲明的標識符(前提是標識符的聲明在引用之前)。

如果需要引用其他命名空間的標識符(變量、函數、類……),需要使用下面的語法:

①命名空間名::標識符名

如:

std::cout << “hello” << std::endl;//每次都要在使用的標識符前加上命名空間名,使用不方便。

 

②using 命名空間名::標識符名

如:

using std::cout;//使用std命名空間裏的cout標識符

cout << “hello” << std::endl;//要使用的所有標識符都得使用一次using語句(using std::cout;),否則就只能採用第一種方式使用了(如std::endl)。

③using namespace 命名空間名;

如:

using namespace std; //使用std命名空間裏的所有標識符

cout << “hello” << endl;//可以直接使用std命名空間裏的所有標識符

        當兩個命名空間中有相同的標示符,並且都使用的using namespace命令,那麼在引用標示符時,必須採用“命名空間名::標識符名”引用來加以區分。否則編譯時報二義性錯誤。舉個栗子吧。

#include <iostream>

using namespacestd;

 

//開發者TF的命名空間

namespace TF

{

    intnValue = 10;

};

 

//開發者Jock的命名空間

namespace Jock

{

    intnValue = 20;

};

 

int main(intargc,char**argv)

{

 

    //cout<< "nValue: " << nValue << endl;

    cout<< "TF::nValue: "<< TF::nValue << endl;

    cout<< "Jock::nValue: "<< Jock::nValue << endl;


    return0;

}

 

程序運行結果:

如果將上例註釋去掉,編譯器將會報以下錯誤:

       在一個文件中,全局命名空間和匿名命名空間中可以定義同名的標示符。但是如果直接使用同名的標示符,編譯時會報二義性錯誤。舉個栗子。

#include <iostream>

using namespacestd;

 

//匿名命名空間

namespace

{

    intnValue = 10;

};

 

//全局命名空間

int nValue = 50;

 

int main(intargc,char**argv)

{

 

    //cout<< "nValue: " << nValue << endl;

 

    //通過作用域解析運算符將使用全局命名空間中變量名稱

    //匿名命名空間中的同名變量被屏蔽

    //只要有全局nValue的存在,在匿名命名空間外將永遠無法訪問匿名命名空間中的nValue

    cout<< "TF::nValue: "<< ::nValue << endl;

 

    return0;

}

 

程序運行結果:

如果將上例註釋去掉,編譯器將會報以下錯誤:


 

       命名空間可以嵌套。命名空間分爲全局命名空間和匿名命名空間。全局命名空間是默認的命名空間,在顯示聲明的命名空間之外聲明的標識符都在一個全局命名空間中(如:在顯示聲明的命名空間之外聲明的全局變量、全局函數、全局類……)。匿名命名空間是一個需要顯示聲明的沒有名字的命名空間。在一個源文件中沒有辦法訪問其他源文件的匿名命名空間,但可以直接引用本源文件中的所有匿名空間中的所有標示符(前提是命名空間的定義在引用之前)。具有命名空間作用域的變量也稱爲全局變量

 

可見性

程序運行到某一點,能夠引用到的標識符,就是該處可見的標識符。

作用域之間的大小關係:命名空間作用域 > 類作用域 > 局部作用域(塊作用域)> 函數原型作用域

作用域可見性的一般規則:

①標識符要聲明在前,引用在後

②在同一個作用域中,不能聲明同名的標識符

③在沒有互相包含關係的不同作用域中聲明的同名標識符,互不影響

④如果在兩個或多個具有包含關係的作用域中聲明瞭同名標識符,則外層標識符在內層不可見。

       關於可見性的問題,前面的栗子中已經有涉及到,這裏只是做個總結,就不再贅述啦。在本篇博文中,我們一起結合作用域和可見性來探討一下作用域運算符的使用,相信大家都有所收穫。如果想了解更多關於作用域解析的知識,請關注博主的《靈活而奇特的C++語言特性——作用域解析(二)》一篇博文。

       如果想了解更多關於C++語言特性相關的知識,請關注博主《靈活而奇特的C++語言特性》系列博文,相信你能夠在那裏尋找到更多有助你快速成長和加深你對C++語言特性相關的知識和一些特性的理解和掌握。當然,如果你想了解關於繼承方面的技術,請關注博主《漫談繼承技術》系列博文。




發佈了81 篇原創文章 · 獲贊 18 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章