对象(包括简单变量)都有诞生和消失的时刻。对象诞生到结束的这段时间就是它的生存期。在生存期内,对象将保持它的状态(即数据成员的值),变量也将保持它的值不变,直到它们被更新为止。对象的生存期可以分为静态生存期和动态生存期。
1.静态生存期
如果对象的生存期与程序的运行期相同,则称它具有静态生存期。
(1)在命名空间作用域中声明的对象都具有静态生存期。比如说,全局变量。
【例1】
#include<iostream>
using namespace std;
int i = 10;//i为全局变量,具有静态生存期
int fun1(int a=i)
{
i = a;//给i重新赋值为传给形参a的值50
return i;
}
int fun2(int a=i)
{
return i;
}
int main()
{
cout << "i=" << fun1(50) << endl;//通过调用函数fun1返回变量i的值
cout << "i=" << fun2(20) << endl;//通过调用函数fun2返回变量i的值
cout << "i=" << i << endl;//直接输出变量i的值
return 0;
}
运行结果及分析:
因为在fun1函数中给全局变量i重新赋值为传给形参a的值,主函数中传给fun1函数的形参为50,所以给全局变量i重新赋值为50。因此主函数中无论是调用fun2函数,还是在主函数中直接输出i的值,结果都为50。又因为变量i是全局变量,所以它生存期与程序的运行期相同,整个程序运行结束时才消失。
【例2】
int i = 10;//i为全局变量,具有静态生存期
int fun1(int a=i)
{
return i;
}
int fun2(int b=i)
{
return i;
}
int main()
{
cout << "i=" << fun1(50) << endl;
cout << "i=" << fun2(20) << endl;
cout << "i=" << i << endl;
return 0;
}
运行结果及分析:
因为函数fun1和函数fun2只是通过传参的方式返回全局变量i的值,并没有真正改变i的值,所以在主函数中无论给函数fun1和fun2传任何整型类型的参数,输出的结果都为10。因为变量i是全局变量,所以它生存期与程序的运行期相同,整个程序运行结束时才消失。
(2)如果要在函数内部的局部作用域中声明具有静态生存期的对象,则要关键字 static。
例如下面语句定义的变量i便是具有静态生存期的变量,也称为静态局部变量(静态变量)。
int i = 10;//i为全局变量,具有静态生存期
int fun(int a)
{
static int i = a;//i为静态局部变量,具有静态生存期(全局寿命),局部可见
i=i+1;
return i;
}
int main()
{
cout << "i=" << fun(20)<<endl;//输出的是fun函数中静态变量i的值
cout << "i=" << fun(30)+2 << endl;//输出的是fun函数中静态变量i加2的
cout << "i=" << i << endl;//输出的是全局变量i的值
return 0;
}
运行结果及分析:
在fun函数中,定义变量i时加了关键字static使得变量i为静态变量,所以该变量i只在调用fun函数时起作用,在fun函数之外,仍然是全局变量i在起作用,所以第一次输出i的值为21。当fun第一次返回i的值为21之后,下一次再调用fun函数时无论传参的值时多少,fun函数中的变量i的值在一开始仍然为第一次返回的值21,然后执行语句i=i+1;
,i的值变为22,第二次返回i的值为22,然后主函数中第二次输出i的值的时候又给22加2,所以第二次输出i的值为24。第三次输出的i的值是全局变量i的值,所以第三次输出i的值为10。说明在函数fun中通过static定义了变量i的值并赋值之后就不可以再通过调用fun函数进行传参来修改变量i的值,且此静态变量i的值只可以在函数fun的函数体中进行修改。
局部作用域中静态变量的特点是:
它并不会随着每次函数调用而产生一个副本,也不会随着函数返回而失效。也就是说,当一个函数返回后,下一次调用时,该变量还会保持上一回的值,及时发生了递归调用,也不会为该变量建立新的副本,该变量会在每次调用间共享。
在定义静态变量的同时可以给它赋初值,如:static int i = 20;
,这表示i会被赋予20初始化,但是并不是每次执行函数时都将i赋值为20。
【注意】在定义时未指定初值的基本类型静态生存期变量,会被赋予0初始化,而对于动态生存期变量,不指定初值意味着初值不确定。
2.动态生存期
除了上述的两种情况,其余对象都具有动态生存期。块作用域中声明的,没有用static修饰的对象是动态生存期的对象。在局部作用域中声明的具有动态生存期的对象,习惯上也称为局部生存期对象。动态生存期对象开始于程序执行到声明点时,结束于命名该标识符的作用域结束处。局部生存期对象诞生于声明点,结束于声明所在块执行完毕时。
【例1】变量的生存期于可见性
#include<iostream>
using namespace std;
int i = 1;//i为全局变量,具有静态生存期
void other()
{
//a、b为静态局部变量,具有全局寿命,局部可见,只第一次进入函数时被初始化
static int a = 2;
static int 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;//b=4
}
int main()
{
static int a;//a为静态局部变量,具有全局寿命,局部可见,只第一次进入函数时被初始化
//b,c为局部变量,具有动态生存期
int b =-10;
int c = 0;
cout << "main: " << endl;
cout << "i: " << i << " a: " << a << " b: " << b << " c: " << c << endl;
c += 8;//c=8
other();
cout << "main: " << endl;
cout << "i: " << i << " a : " << a << " b : " << b << " c : " << c << endl;
i += 10;
other();
return 0;
}
运行结果及分析:
在命名空间作用域中定义全局变量i,在主函数中,定义a为静态局部变量,b,c为局部变量,在other函数中定义a、b为静态局部变量,c为局部变量。
当程序从主函数开始运行,第一次输出主函数main中的i,a,b,c的值时,由于i是全局变量,所以输出1;a是静态局部变量但没有赋初值,所以输出0;b在定义时初始化为-10,所以输出-10;c在定义时初始化为0,所以输出0。**因此第一次输出主函数中的i,a,b,c的值分别为1,0,-10,0。**在主函数中第一次输出i,a,b,c的值之后,将c的值变为8。
主函数main第一次调用other函数,在other函数输出i,a,b,c的值之前,在other函数中给局部静态变量a赋初值为2,没有给局部静态变量b赋初值,所以默认为0,定义局部变量c并初始化为10。然后给静态局部变量a+2,a的值变为4,给全局变量i+32,i的值变为33,给局部变量c+5,c的值变为15,所以第一次输出other函数中的i,a,b,c的值分别为33,4,0,15。 在other函数第一次输出i,a,b,c的值之后,将a的值赋给b,b=4。
第二次输出主函数main中的i,a,b,c的值时,由于在other函数中将全局变量i的值变为了33,所以全局变量i在之后的所有程序中应用到的值都为33,a的值仍然为一开始在主函数中定义的静态局部变量a的默认值0,局部变量b的值也没有发生改变仍然为-10,由于在主函数中第一次输出i,a,b,c的值之后,将c的值变为8,所以此时主函数中c的值应为8。**因此第二次输出主函数中的i,a,b,c的值分别为33,0,-10,8。**在主函数中第二次输出i,a,b,c的值之后,给全局变量i的值加10,即33+10,此时全局变量i的值为43,且全局变量i在之后的所有程序中应用到的值都为43。
主函数第二次调用other函数,在other函数输出i,a,b,c的值之前,给静态局部变量a+2,由于第一次在other函数结束时,a的值为4,现在再给a+2,所以a的值就变为6;给全局变量i+32,由于在主函数中将全局变量i的值变为了43,所以i的值为75;局部变量c则没有改变仍然为15;由于在other函数第一次输出i,a,b,c的值之后,将a的值4赋给静态局部变量b,b=4,所以,在第二次调用other函数时,静态局部变量b的值仍然为上一次other函数结束时b的值4。因此第二次输出other函数中的i,a,b,c的值分别为75,6,4,15。
【例2】具有静态和动态生存期对象的时钟程序。
#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 globClock;// 声明对象globClock,具有静态生存期,命名空间作用域
//由默认构造函数初始化为0:0:0
int main()
{
cout << "第一次输出时间为:" << endl;
//引用具有命名空间作用域的对象globClock
globClock.showTime(); //显示0:0:0
globClock.setTime(8, 30, 30);//将时间设置为8:30:30
Clock myClock(globClock);//声明具有块作用域的对象myClock
//调用默认的拷贝构造函数,以globClock的值为初值
cout << "第二次输出时间为:" << endl;
myClock.showTime();//引用具有块作用域的对象myClock
return 0;
}
运行结果及分析:
对象globClock为全局变量,具有静态生存期,命名空间作用域。在主函数中,引用对象globClock访问showTime函数时,因为globClock没有初始化,所以它由默认构造函数初始化为0:0:0,所以第一次输出的时间为0:0:0,然后全局对象globClock调用setTime函数,将时间设置为为了8:30:30,由于globClock是全局对象,所以在之后的程序中应用对象globClock的值都为8:30:30。然后定义了Clock类的局部对象myClock,并调用默认的拷贝构造函数,以对象globClock的值8:30:30初始化对象myClock,接下来引用局部对象myClock访问showTime函数时,由于局部对象myClock的值被初始化为globClock的值8:30:30,所以第二次输出的时间为8:30:30。
在这个程序中,包含了具有各种作用域类型的变量和对象。其中时钟类定义中函数成员setTime的三个形参具有函数原型作用域;setTime函数定义中的3个参数以及主函数中的对象myClock都具有局部作用域;时钟类的数据、函数成员具有类作用域;对象globClock具有命名空间作用域。在主函数中,这些变量、对象以及对象的公有成员都是可见的。就生存期而言,除了命名空间作用域的对象globClock具有静态生存期,与程序运行期相同之外,其他变量及对象都具有动态生存期。