文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>C++标准(n3126)——作用域(3.3 basic.scope)

C++标准(n3126)——作用域(3.3 basic.scope)

时间:2010-10-14  来源:剧终

3.3.1 声明区域和作用域 [basic.scope.declarative]

1. 声明区域是指程序中名称(变量、类等等的名称)的最大有效区域,就是说,在使用某一名称时,在该区域应该能够唯一标识某一实体。一个名称如果可以在不连续的代码片断中生效,则这些片断的集合就是它的作用域。为了便于理解作用域,我们引用了潜在作用域的概念:当潜在作用域不包含另一个同名的声明时,作用域与潜在作用域是相同的。当一个潜在作用域包含两个同名的声明时,总是有某个名称的作用域会从这个潜在作用域中剔除出去。比如如下代码:

int j = 24;
int main() 
{
    int i = j, j;
    j = 42;
}

 标识符j在程序中被声明了两次。第一个j的声明区域贯穿程序始末,它的潜在作用域紧跟于它出现的位置(从第一个“=”开始)到程序最后面,但是,它的(实际)作用域不包含第一个“,”和最后一个“}”之间的区域。第二个j的声明区域从它声明之后(“;”开始)到“}”,但是它的潜在作用域不包含i的声明部分。因此,第二个j的作用域与它的潜在作用域是相同的。

2. 一个名称是从它的声明开始在作用域中生效的,特例是当使用friend关键字、域操作符“::”以及使用using关键字的情况。

3. 在声明区域中定义一个有效的名称,需遵照以下规则:

    —— 同一个名称需引用同一个实体(同一个函数,函数模板等等)

    —— 类和枚举在该区域中应该只有唯一声明,并且该名字不能是已经使用typedef定义的类型名,并且其它声明应该全部都引用前面的声明,否则类或枚举的定义将被隐藏。如

   

class A;

int A=1;

上面class A将被后面的变量A隐藏。

[注意:这些限定是指名称在被使用的时候,而不是它被声明时。特别是使用限定符“::”和friend关键字引入一个名称到另一个命名空间时,这些规则就会被应用到目标命名空间中,还有就是本地通过extern声明一个变量也会把一个名称引入到一个声明区域中。

4. 名称查找规则将在3.4节中介绍。

 

3.3.2 声明位置 [basic.scope.pdecl]

1. 一个名称的声明位置介于声明符与它的初始化语句(如果有的话)之间,以下这种情况除外:

int x = 12;
{ int x = x; }

这里第二个x是由它自己(不确定值)来初始化的。[译注:编译通过,但是在有些编译器中运行报错“x的值未被初始化”]

2. 外部作用域的名字会保持到隐藏它的另一个名字的声明位置结束处,如:

const int i = 2;
{ int i[i]; }

上面的代码声明了一个包含两个元素的数组。

3. 类或模板类的声明位置是在这个类的标识符(类名)后面。

4. 枚举项的声明点是在它结束处开始的,因此如下代码中的x是由值为12的x来初始化的(跟前面的int x=x不一样):

const int x = 12;
{ enum { x = x }; }

 5. 在一个成员变量被声明后,这个成员的名称可以在类内部使用。[注意:这个类不完整的情况除外] [译注:C++如何出现一个不完整的类?]

struct X {
enum E { z = 16 };
int b[X::z]; // OK
};

6. 类的声明位置

    —— 对于这种格式 "class-key attribute-specifieropt identifier;", 标识符(identifier)将被声明成一个类名,否则

    —— 对于这种格式 "class-key identifier",如果是以函数参数形式出现,则它跟上面一样,声明成一个类名;否则它不是被声明成一个类名,也不是函数。[注意:这个规则适用于模板][注意:其它形式的声明不产生一个新名称,而是引用一个现成的类型名]

7. 嵌入类的声明位置紧跟这个类的定义后面。

8. 函数的声明位置位于函数体之前。

9. 模板参数的声明位置位于这个参数的声明之后,例如:

typedef unsigned char T;
template<class T
= T // 使用的是typedef的T
, T // 使用的是模板参数
N = 0> struct A { };

10. 友元声明会引用它所在命名空间的函数或类,但是它不会引入新的名称。

11. 模板展开部分,参考14.6.4.1

 

3.3.3 作用域 [basic.scope.local]



1. 在程序块中声明的名称被称为该块的局部名称。它的潜在作用域起始于它的声明位置,结束于程序块结束。程序块中声明的变量称为局部变量。

2. 函数参数的潜在作用域(包含lambda表达式中的参数)起始于它的声明位置。如果函数有一个function-try-block,参数和函数的局部变量的潜在作用域结束于函数最后一个handler,否则它结束于函数内最外层的程序块。参数名不应该在程序内最外层的程序块中,也不能在一个有function-try-block的任意handler中重复定义。

3. exception-declaration中定义的名称是该handler的局部名称,不应该让它在这个handler内的最外层程序块中重复定义。

4. for语句的初始化块(for(int i,b;),范围块(for(;i<5)和if,while,for,switch中的条件语句对于前面这些语句是局部的,并且不应该在它的其它条件中,也不能在它内部最外层的程序块中重复定义。

 

3.3.4 函数原型的作用域 [basic.scope.proto]

 

3.3.5 函数的作用域 [basic.funscope]

只有标号才拥有整个函数范围的作用域。

 

3.3.6 命名空间作用域 [basic.scope.namespace]

1. 命名空间的声明区域就是它的主体部分。命名空间中声明的实体称为命名空间的成员,命名空间中的成员名称在整个命名空间都是可见的。它们的潜在作用域包含它的声明位置和使用using语句将该命名空间引入到的其它空间。例如:

namespace N {
int i;
int g(int a) { return a; }
int j();
void q();
}
namespace { int l=1; }
// "l"的潜在作用域超始于声明位置
// 到该语句块结束处。
namespace N {
int g(char a) { // 重载 N::g(int)
return l+a; // 使用未命名的命名空间中的"l"
}
int i; // 错误:重复定义
int j(); // 正确:重复的函数声明
int j() { // 正确:定义N:j()
return g(i); // 调用 N::g(int)
}
int q(); // 错误:返回类型不一致
}

2. 命名空间中的成员同样可以通过“::”来引用,这跟使用using 引入命名空间,然后直接使用它内部 的成员是一样的。

3. 一个翻译单元的最外层声明区域同样是一个命名空间,被称之为全局命名空间。在全局命名空间范围内声明的名称拥有全局作用域。它的潜在作用域起始于它的声明位置,终止于它的声明区域(该翻译单元的结束位置)。这些名称被称之为全局名称。

 

3.3.7 类作用域 [basic.scope.class]

1. 下面是对类内部名称声明的几条规则的描述:

1)类内名称的潜在作用域不局限于它所在的声明区域,而是包含成员函数体、数据成员的初始位置、该类的默认参数值(包含嵌套类)。

2)类S内的一个N名称的作用域应该参考它生效处的其它相同名称。比如

A(int val)
{
    val=val;  // 应该使用this->val=val;
}

3)程序内部声明的名称会将该类中的相同名称隐藏(类似于(2))。

4)潜在作用域可能会超出类的范围,例如:

typedef int c;
enum { i = 1 };
class X {
char v[i]; // 错误: i 使用的是::i
              // 但其实要使用的是 X::i
int f() { return sizeof(c); } // 正确: X::c
char c;
enum { i = 2 };
};
typedef char* T;
struct Y {
T a; // 错误: T 引用了 ::T
       // 但其实打算使用的是 Y::T
typedef long T;
T b;
};
typedef int I;
class D {
typedef I I; // 错误
};

2. 类内成员名称应该以下面几种方式使用:

——在类内部或派生类中使用。

——类或派生类的对象使用“. ”来使用。

——类或派生类的对象的指针使用“->”来使用。

——类或派生类通过“::”来使用。

 

3.3.8 枚举作用域 [basic.scope.enum]

 

3.3.9 模板参数作用域 [basic.scope.temp]

1. 模板参数的声明区域是它所在的模板参数列表。

2. 下面代码中的T、U和V是模板声明。但是A、f、g和C都同属于命名空间N。

namespace N {
template<class T> struct A { }; // #1
template<class U> void f(U) { } // #2
struct B {
template<class V> friend int g(struct C*); // #3
};
}

3. 模板参数名的潜在作用域起始于声明位置,终止于它所在的声明区域结束位置。[注意:这暗示着模板参数可以用于后续的参数列表中,也可以作为参数列表的值,但是不能为它们指定默认值],例如:

template<class T, T* p, class U = T> class X { /* ... */ }; 
template<class T> void f(T* p = new T);

同样,也可以将模板参数应用于基类:

template<class T> class X : public Array<T> { /* ... */ }; 
template<class T> class Y : public T { /* ... */ };

4. 模板参数可以在它所在的声明区域中嵌套使用。[注意:这样一来,模板参数会将该区域内的同名参数隐藏],例如:

typedef int N;
template<N X, typename N, template<N Y> class T> struct A;

这里,X不是一个“类型”模板参数,而是指定了一个N类型(所以使用这个模板时,不应该传入类型,而应该是int型的整数值),Y也不是一个“类型”模板参数,而是第二个模板参数定义的N类型)。

5. 由于模板参数不能重新定义,所以模板参数的作用域往往就是它的潜在作用域。

 

 3.3.10 名称隐藏 [basic.scope.hiding]

1. 名称可以被它所在的潜在作用域中嵌套的声明区域或者派生类中的同名隐藏。

2. 类名或枚举名可以被同一作用域内的变量名,数据成员,函数或枚举项隐藏。

3. 成员函数中定义的名称会将它所在类中的成员名隐藏。派生类中的成员会隐藏基类的名称。

4. 通过using 引入的名称,可能被这个using 语句所在的作用域中的其它同名名称隐藏。

5. 作用域中未被隐藏的名称被称为是可见的。

排行榜 更多 +
坦克冒险大师安卓版

坦克冒险大师安卓版

策略塔防 下载
自动防御

自动防御

策略塔防 下载
枪战大乱斗2

枪战大乱斗2

飞行射击 下载