数据的共享与保护 C++语言是适合于编写大型复杂程序的语言,数据的共享与保护机制是C++ 语言的 重要特性之一。本章介绍标识符的作用域、可见性和生存期的概念,以及类成员的共享 与保护问题。最后介绍程序的多文件结构和编译预处理命令,即如何用多个源代码文件 来组织大型程序。 5.1 标识符的作用域与可见性 作用域讨论的是标识符的有效范围,可见性是讨论标识符是否可以被引用。我们知道, 在某个函数中声明的变量就只能在这个函数中起作用,这就是受变量的作用域与可见性的 限制。作用域与可见性既相互联系又存在着很大差异。 5.1.1 作用域 作用域是一个标识符在程序正文中有效的区域。C++ 语言中标识符的作用域有函数 原型作用域、局部作用域(块作用域)、类作用域、文件作用域、命名空间作用域和限定作用域 的enum 枚举类。 1.函数原型作用域 函数原型作用域是C++程序中最小的作用域。第3章中介绍过,在函数原型中一定要 包含型参的类型说明。在函数原型声明时形式参数的作用范围就是函数原型作用域。例 如,有如下函数声明: double area(double radius); 标识符radius的作用(或称有效)范围就在函数area形参列表的左右括号之间,在程序 的其他地方不能引用这个标识符。因此标识符radius的作用域称作函数原型作用域。 注意 由于在函数原型的形参列表中起作用的只是形参类型,标识符并不起作用,因 此是允许省去的。但考虑到程序的可读性,通常还是要在函数原型声明时给出形参标 识符。 2.局部作用域 为了理解局部作用域,先来看一个例子。 void fun(int a) { int b=a; cin>>b; if(b>0) { int c; … } c 的作用域 } } ü t y .... .... b 的作用域 ü t y ..... ..... a 的作用域 这里,在函数fun的形参列表中声明了形参a,在函数体内声明了变量b,并用a的值初 始化b。接下来,在if语句内,又声明了变量c。a、b和c都具有局部作用域,只是它们分别 属于不同的局部作用域。 函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为 止。因此,形参a的作用域从a的声明处开始,直到fun函数的结束处为止。函数体内声明 的变量,其作用域从声明处开始,一直到声明所在的块结束的花括号为止。所谓块,就是一 对花括号括起来的一段程序。在这个例子中,函数体是一个块,if语句之后的分支体又是一 个较小的块,二者是包含关系。因此,变量b的作用域从声明处开始,到它所在的块(即整个 函数体)结束处为止;而变量c的作用域从声明处开始,到它所在的块,即分支体结束为止。 具有局部作用域的变量也称为局部变量。 3.类作用域 类可以被看成是一组有名成员的集合,类X的成员m 具有类作用域,对m 的访问方式 有如下3种。 (1)如果在X 的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以 直接访问成员m。也就是说m 在这样的函数中都起作用。 (2)通过表达式x.m 或者X::m。这正是程序中访问对象成员的最基本方法。X::m 的方式用于访问类的静态成员,相关内容将在5.3节介绍。 (3)通过ptr->m 这样的表达式,其中ptr为指向X 类的一个对象的指针。关于指针将 在第6章详细介绍。 C++中,类及其对象还有其他特殊的访问和作用域规则,在后续章节中还会深入讨论。 4.文件作用域 不在前述各个作用域中出现的声明,就具有文件作用域,这样声明的标识符其作用域开 始于声明点,结束于文件尾。例5-1中所声明的全局变量就具有文件作用域,它们在整个文 件中都有效。 5.命名空间作用域 生活中存在重名现象,在C++应用程序中,也存在同名变量、函数和类等情况,为避免 重名冲突,使编译器能够区分来自不同库的同名实体,C++引入了命名空间的概念,它本质 上定义了实体所属的空间。命名空间定义使用namespace关键字,声明方式如下: namespace namespace_name{ //代码声明 } 第5章 数据的共享与保护·151· 使用某个命名空间中的函数、变量等实体,需要命名空间∷ 实体名称或通过using namespacenamespace_name的方式。例5-1中usingnamespacestd使得标准命名空间中 实体调用无须加空间前缀,而my_space中的func通过∷方式调用。 6.限定作用域的enum 枚举类 我们在第4章介绍了enum 枚举类,枚举类分为限定作用域和不限定作用域两种,由于 之前未涉及作用域的概念,因此第4章只给了不限定作用域的例子,在这里对限定作用域的 enum 枚举类型做更深入的讨论。 定义限定作用域的枚举类型的方式是enumclass{...},即多了class或struct限定符, 此时枚举元素的名字遵循常规的作用域准则,即类作用域,在枚举类型的作用域外是不可访 问的。相反,在不限定作用域的枚举类型中,枚举元素的作用域与枚举类型本身的作用域 相同: enum color {red, yellow, green}; //不限定作用域的枚举类型 enum color1 {red, yellow, green}; //错误,枚举元素重复定义 enum class color2 {red, yellow, green}; //正确,限定作用域的枚举元素被隐藏了 color c=red; //正确,color 的枚举元素在有效的作用域中 color2 c1=red; //错误,color2 的枚举元素不在有效的作用域中 color c2=color::red; //正确,允许显式地访问枚举元素 color2 c3=color2::red; //正确,使用了color2 的枚举元素 例5-1 作用域实例。 //5_1.cpp #include using namespace std; int i; //全局变量,文件作用域 int main() { i=5; //为全局变量i 赋值 { //子块1 int i; //局部变量,局部作用域 i=7; cout<<"i="< using namespace std; class Clock { //时钟类定义 public: //外部接口 Clock(); void setTime(int newH, int newM, int newS); //3 个形参均具有函数原型作用域 void showTime(); private: //私有数据成员 int hour, minute, second; }; //时钟类成员函数实现 Clock::Clock() : hour(0), minute(0), second(0) {} //构造函数 void Clock::setTime(int newH, int newM, int newS) { //3 个形参均具有局部作用域 hour=newH; minute=newM; 第5章 数据的共享与保护·155· second=newS; } void Clock::showTime() { cout< using namespace std; class Point { //Point 类定义 public: //外部接口 Point(int x=0, int y=0) : x(x), y(y) {//构造函数 //在构造函数中对count 累加,所有对象共同维护同一个count count++; } Point(Point &p) { //复制构造函数 x=p.x; y=p.y; count++; } ~Point() { count--;} int getX() {return x;} int getY() {return y;} void showCount() { //输出静态数据成员 cout<<" Object count="<