—标识符的作用域与可见性
作用域:是一个标识符在程序正文中有效的区域。
C++中标识符的作用域有函数原型作用域,局部作用域(块作用域),类作用域,命名空间作用域。
1.函数原型作用域:
是C++程序中最小的作用域,在函数原型声明时形式参数的作用范围就是函数原型作用域(在函数说明中一定要包含形参的类型说明)。
注意:由于在函数原型的形参列表中起作用的只是形参类型,标识符并不起作用,可以省去。但考虑到程序的可读性,通常还是要在函数原型声明时给出形参标识符。
2.局部作用域:
函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。
函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号为止。
3.类作用域:
类:一组有名成员的集合,类X的成员m具有类作用域,对m的访问方式有3种:
(1)如果在X的成员函数中没有声明同名的局部作用域标识符,那么在该函数内可以直接访问成员m。
(2)通过表达式x.m或者X::m。这正是程序中访问对象成员的最基本的方法。
X::m用于访问类的静态成员。
(3)通过ptr->m这样的表达式,其中ptr为指向X类的一个对象的指针。
4.命名空间作用域:
命名空间的语法形式:
namespace 命名空间名
{
命名空间内的各种声明(函数声明,类声明…)
}
一个命名空间确定了一个命名空间作用域,凡是在该命名空间之内声明的,不属于前面所述各个作用域的标识符,都属于该命名空间作用域。在命名空间内部可以直接引用当前命名空间中声明的标识符,如果需要引用其他命名空间的标识符,形式为:
命名空间名::标识符名
例:
namespace SomeNs
{
class SomeClass{...};
}
如果需要引用类名SomeClass或函数名someFunc:
SomeNs::SomeClass objl;
命名空间允许嵌套:
例:
namespace OuterNs
{
namespace InnerNs
{
class SomeClass{...}
}
}
例:
#include<iostream>
using namespace std;
int i; //在全局命名空间中的全局变量
namespace Ns {
int j; //在Ns命名空间中的全局变量
}
int main()
{
i = 5; //为全局变量i赋值
Ns::j = 6;//为全局变量j赋值
{ //子块1
using namespace Ns; //使得在当前块中可以直接引用Ns命名空间的标识符
int i; //局部变量,局部作用域
i = 7;
cout << "i=" << i << endl; //输出7
cout << "j=" << j << endl; //输出6
}
cout << "i=" << i << endl; //输出5
return 0;
}
运行结果为:
i=7
j=6
i=5
4.可见性:
作用域可见性的一般规则:
(1)标识符要声明在前,引用在后
(2)在同一作用域中,不能声明同名的标识符
(3)在没有互相包含关系的不同的作用域中声明的同名标识符,互不影响。
(4)如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见
5.对象的生存期:
在生存期内,对象将保持他的状态(即数据成员的值),变量也将保持它的值不变,直到他们被更新为止。
对象的生存期分为静态生存期和动态生存期。
–静态生存期:对象的生存期与程序的运行期相同。
局部作用域中静态变量的特点是:
它不会随着每次函数调用而产生一个副本,也不会随着函数返回而失效。也就是说,当一个函数返回后,下一次再调用时,该变量还会保持上一回的值,即使发生了递归调用,也不会为该变量建立新的副本,该变量会在每次调用间共享。
注意:在定义时未指定初值的基本类型静态生存期变量,会被赋予0值初始化,而对于动态生存期变量,不指定初值意味着初值不确定。
–动态生存期:在局部作用域中声明的具有动态生存期的对象,习惯上也称为局部生存期对象。
局部生存期对象诞生于声明点,结束于声明所在的块执行完毕之时。
变量的生存期与可见性:
#include<iostream>
using namespace std;
int i = 1;
void other()
{
static int a = 2;
static int b; //a.b为静态局部变量,具有全局寿命,局部可见,只第一次进入函数时被初始化
int c = 10; //c为局部变量,具有动态生存期,每次进入函数时都初始化
a += 2;
i += 32;
c += 5;
cout << "---OTHER---" << endl;
cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
b = a;
}
int main()
{
static int a; //a为静态局部变量,具有全局寿命,局部可见
int b = 10;
int c = 0; //b,,c为局部变量,具有动态生存期
cout << "---MAIN---" << endl;
cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
c += 8;
other();
cout << "---MAIN---" << endl;
cout << "i:" << i << "a:" << a << "b:" << b << "c:" << c << endl;
i += 10;
other();
return 0;
}
运行结果为:
---MAIN---
i:1a:0b:10c:0
---OTHER---
i:33a:4b:0c:15
---MAIN---
i:33a:0b:10c:8
---OTHER---
i:75a:6b:4c:15
例:具有静态和动态生存期对象的时钟程序。
#include<iostream>
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;
second = newS;
}
void Clock::showTime()
{
cout << hour << ":" << minute << ":" << second << endl;
}
Clock golbClock; //声明对象golbClock,具有静态生存期,命名空间作用域
//由默认构造函数初始化为0:0:0
int main()
{
cout << "First time output:" << endl;
golbClock.showTime();//引用具有命名空间作用域的对象golbClock,对象的成员函数具有类作用域
//显示0:0:0
golbClock.setTime(8, 30, 30); //将时间设置为8:30:30
Clock myClock(golbClock); //声明具有块作用域的对象myClock,调用复制构造函数,以golbClock为初始值
cout << "Second time output:" << endl;
myClock.showTime(); //引用具有块作用域的对象myClock,输出8:30:30
return 0;
}
运行结果为:
First time output:
0:0:0
Second time output:
8:30:30
6.类的静态成员:
模块间对内存中数据的共享是通过函数与函数之间的数据共享来实现的,其中包括两个途径:参数传递,全局变量。
静态成员是解决同一个类的不同对象之间数据与函数共享问题的。
—静态数据成员:
一个类的所有对象具有相同的属性:指属性的个数,名称,数据类型相同,各个对象的属性值则可以各不相同。
–类属性:描述类的所有对象共同特征的一个数据项,对于任何对象实例,它的属性值是相同的。
静态数据成员具有静态生存期。由于静态数据成员不属于任何一个对象,因此可以通过类名对它进行访问:类名::标识符。
注意:之所以类的静态数据成员需要在类定义之外再加以定义,是因为需要以这样的方式专门为他们分配空间。
非静态数据成员无须以此方式定义,因为他们的空间是与他们所属对象的空间同时分配的。
例:具有静态数据成员的point 类。
#include<iostream>
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=" << count << endl;
}
private: //私有数据成员
int x, y;
static int count; //静态数据成员声明,用于记录点的个数
};
int Point::count = 0; //静态数据成员定义和初始化,使用类名限定
int main()
{
Point a(4, 5); //定义对象a,其构造函数会使count增1
cout << "Point A:" << a.getX() << "," << a.getY();
a.showCount(); //输出对象个数
Point b(a); //定义对象b,其构造函数会使count增1
cout << "Point B:" << b.getX() << "," << b.getY();
b.showCount(); //输出对象个数
return 0;
}
运行结果为:
Point A:4,5Object count=1
Point B:4,5Object count=2
注意:在对类的静态私有数据成员初始化的同时,还可以引用类的其他私有成员。