C++/作用域

維基教科書,自由的教學讀本
< C++
跳至導覽 跳至搜尋

作用域(scope)是名字(name)與實體(entity)的綁定(binding)保持有效的那部分電腦程式。顯然,這種名字綁定既可以是在編譯時的靜態綁定,也可使程序運行時的動態綁定,所產生的作用域分別稱為靜態作用域與動態作用域。C/C++是典型的靜態綁定的作用域;而Emacs Lisp是典型的動態綁定的作用域。C++語言標準規定:使得特定名字保持有效的那些可能並不連續的程序文本就是該名字的作用域[參 1]名字的作用域通常從其聲明之處開始,但要排除內部嵌套的聲明區域中其他同名的名字的作用域。例如:

int j;         //全局变量j
int main() {
 int j;        //局部变量j。注意,从这个局部变量j的声明之处开始,直至该函数结束之处,被排除出全局变量j的作用域
 j = 42;
}

C++語言有下述名字作用域:

塊作用域[編輯]

塊作用域(block scope),也即局部作用域(local scope)。[參 2]聲明區域典型為一對花括號{ }括起來的程序塊。其內部聲明的名字的作用域從首次聲明之處至該塊的結束之處。如:局部變量、局部類型等的名字。

for、while、if、switch等語句也構成了塊作用域。

函數作用域[編輯]

函數作用域(function scope),只適用標籤(label)的名字。標籤名字可以在它所出現的函數中的任何位置被goto語句使用。也就是說,在函數內部,可以先通過goto語句使用一個標籤名字,之後在該函數內部才有該標籤名字的定義。

函數原型作用域[編輯]

函數原型作用域(function prototype scope)是指,函數原型中聲明的名字只在該原型結束前可見,在原型結束處即為作用域結束之處。[參 3]例如:

 void foo(int a, float b, vector<int> c);

全局作用域[編輯]

全局命名空間作用域(global namespace scope),也稱作全局作用域(global scope)是指編譯單元的最外部的聲明區域中聲明的名字的作用域。[參 4]這些名字在程序的各個編譯單元之間均可見。全局名字是連結器需要識別的名字。一般說來,全局名字對應的實體在程序中應僅有一份,而不同編譯單元都可以通過同一名字綁定到該實體上。

典型的具有全局作用域的名字,包括:全局變量、全局函數等。

C++規定,可以通過::來限定一個名字為全局作用域中的名字。這也是「全局命名空間」稱謂的來源。例如:

int i; //全局变量
namespace ns{int i;}
using namespace ns;
::i=102;  //给全局变量i赋值,而不是ns::i赋值。这行如果不用“::”,则编译错误: reference to 'i' is ambiguous|

文件作用域[編輯]

文件作用域(file scope)是從名字聲明之處直至該編譯單元結束之處。靜態全局變量、內聯全局函數、const限定的全局變量等的名字的作用域均為典型的文件作用域。

各種類型,不論是通過typedef、class、struct、union、enum等方式聲明或定義,如果不是內部類型,則名字都具有文件作用域。也就是說,同一個類型的聲明與定義可以出現在不同的編譯單元,在連結時不產生名字衝突。實際上,編譯時並不把類型的名字放到連結器的輸入文件中,連結器根本看不到類型的名字,所以也無可能產生名字衝突。甚至在不同的編譯單元分別適用了同一類型名字的完全不同的定義,也不會有編譯連結錯誤。例如:

 //main.cpp
 struct T{int i};
 int main(){
   T v;
   v.i=101;
   return 0;
 }
 // foo.cpp
 struct T{float i};
 void foo(){
   T v;
   v.i=3.14;
 }

C++引入了無名命名空間(unnamed namespace),其作用域即為當前編譯單元。例如:

namespace {int i;}

也可以通過前面加上::限定訪問文件作用域中的名字。

命名空間作用域[編輯]

命名空間的成員名字具有命名空間作用域(namespace scope)。[參 5]該名字的作用域從在該命名空間內的聲明點直至當前編譯單元內該命名空間的結尾處。使用using namespace語句可以提升該命名空間此時已經聲明的名字到該using語句的作用域。

使用::限定符,可以在命名空間名字的作用域內訪問該命名空間的成員名字。

類作用域[編輯]

類(class、struct、union)內定義的名字的作用域稱為類作用域(class scope)。[參 6]這些名字在當前類的定義內部,以及類定義詞法範圍外的類成員定義中是可見的。因此,在類內部,成員名字可以先使用後定義,不必前向聲明(forward declaration)。

類靜態數據成員具有外部連結屬性

類的成員名字在其所在的類作用域內、或者派生類作用域內可見,或者通過 .運算符、->運算符、::限定符訪問。

匿名類的作用域[編輯]

這裏的匿名類是指匿名struct、匿名class、匿名union,且沒有直接用這種類型定義了變量。如果緊隨這些無名類型的定義之後,定義了該類型的變量,則類型的定義及使用與普通情況完全一樣;嚴格說,這種情形可以不算是匿名類。

匿名類的作用域有特別含義:

  • 如果匿名類作為嵌套類,即匿名類在一個外部類的內部定義:則編譯器就在此處定義一個該匿名類的無名變量,並把該匿名類的成員的名字提升到該類型定義所在的外部類的作用域內。由於匿名類不能使用點運算符訪問其成員,所以匿名類只能有數據成員,不允許有成員函數,也不能包含私有或受保護的數據成員。如果匿名類的定義是連續嵌套,則最內部的匿名類的成員名字被提升至最外部的非匿名類或可用變量訪問的成員類之處。
  • 如果匿名類不作為嵌套類定義,即匿名類定義在一個函數內部或者全局函數外部。在這種情形下C/C++語言標準只允許定義匿名union;如果定義匿名struct、匿名class,則編譯報錯。對於此種情形的匿名union,編譯器同樣在此處定義一個該匿名聯合的無名變量,並把該匿名聯合的成員的名字提升到該匿名聯合所在的作用域內,匿名聯合只能有數據成員,不允許有成員函數,也不能包含私有或受保護的數據成員。在函數外的匿名union只能在當前編譯單元內可見,因此必須使用static關鍵字,或者必須放在匿名命名空間中(對於Visual C++在匿名命名空間中也必須有static關鍵字)。

例如:

int main(){
 union{
         int test;
         char c;
      };         
  test=5;  //匿名union的成员的名字提升到定义了该匿名union的作用域内。
  struct { int i;} v; //匿名struct,但是紧随其后声明了一个变量v
  v.i=101;  // 编译通过
  i=102;   //编译报错: 'i' was not declared in this scope| 
  return 0;
}

枚舉作用域[編輯]

枚舉作用域(enumeration scope)是指枚舉類型的成員(enumerator)的名字的作用域,起自其聲明之處,終至枚舉定義結束之處。[參 7]

C語言規定,枚舉類型的成員(enumerator)的可見範圍被提升至該枚舉類型所在的作用域內。這被認為有可能污染了外部的作用域。為此,C++11引入了枚舉類(enum class)解決此問題。

模板參數作用域[編輯]

模板參數作用域(template parameter scope)是指,模板的形參從其聲明之處直至模板定義結束之處可見。[參 8]

模板的模板參數自身也有模板參數(template parameter of a template template-parameter),其名字作用域是包含該名字的最小的模板形參列表。[1]

名字隱藏[編輯]

一個名字可以被隱藏(name hiding),這是通過在它的作用域內的嵌套的聲明區域中或者派生類中聲明一個同樣的名字。

類名或枚舉名可以被作用域中聲明的變量、數據成員、函數、枚舉成員的名字隱藏掉。而不論這兩大類名字聲明的先後順序。例如:

  int i=101;
  struct i{double  k;};
  std::cout<<sizeof(i); //输出为4

使用using namespace語句提升作用域的名字,會被該using namespace所在的聲明區域中的名字隱藏掉。例如:

#include <iostream>
namespace ns{int i=102;}
int main(){
   int i=101;
   using namespace ns;
   std::cout<<i;  //输出: 101
   return 0;
}

參考文獻[編輯]

頁面Template:ReflistH/styles.css沒有內容。

  1. C++11語言標準,3.3.1.1:Declarative regions and scopes: In general, each particular name is valid only within some possibly discontiguous portion of program text called its scope.
  2. C++11語言標準,§3.3.3:Block scope
  3. C++11語言標準,§3.3.4 Function prototype scope
  4. C++11語言標準,§3.3.6.3: The outermost declarative region of a translation unit is also a namespace, called the global namespace. A name declared in the global namespace has global namespace scope (also called global scope).
  5. C++11語言標準,§3.3.6 Namespace scope
  6. C++11語言標準,§3.3.7 Class scope
  7. C++11語言標準,§3.3.8 Enumeration scope
  8. C++11語言標準,§3.3.9 Template parameter scope

Category:C++

  1. C++11 3.3.9.1:The declarative region of the name of a template parameter of a template template-parameter is the smallest template-parameter-list in which the name was introduced.