C++函数特殊点详解

局部对象

在c++语言中,名字有作用域,对象有声明周期(lifetime):

  • 名字的作用域是程序文本的一部分,名字在其中可见。
  • 对象的生命周期是程序执行过程中该对象存在的一段时间。

如我们所知,函数体是一个语句块。块构成一个新的作用域,我们可以在其中定义变量。形参和函数体内部定义的变量统称为局部变量(local variable)。他们对函数而言是局部的,仅在函数的作用域内可见,同时局部变量还会隐藏(hide)在外层作用域中同名的其他所有生命中。意思是,函数体内定义的变量和函数体外定义的变量,即使是同名,但在函数运行的时候,只对函数体内的变量操作(除非将函数体外同名变量作实参传入),请看以下实例代码:

void Test()
{
	int Name1 = 2;
	cout << Name1;
}
void main()
{
	int Name1 = 1;
	Test();//结果是2
	system("pause");
}

上面讲的是在函数内定义的局部变量,只存在于函数的执行过程中;而在函数体之外定义的局部对象存在于整个程序的执行过程中。此类对象在程序启动时被创建,直到程序结束时才会销毁。
因此,局部对象的生命周期依赖于定义的方式。而局部对象又具体分为自动对象和局部静态对象

自动对象

对于普通局部变量对应的对象来说,当函数的控制路径经过变量定义语句时创建该对象,当到达定义所在的块末尾时销毁它。我们把只存在于块执行期间的对象称为自动对象(automatic object)。当块的执行结束后,块中创建的自动对象的值就变成未定义的了。
形参是一种自动对象。函数开始时为形参申请存储空间,因为形参定义在函数体作用域之内,所以一旦函数终止,形参也就被销毁。

静态局部对象

某些时候,有必要另局部变量的生命周期贯穿函数调用及之后的时间。可以将局部变量定义成static类型从而获得这样的对象。**局部静态对象(local static object)**在程序执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。意思是,如果执行完这个函数,下次再执行但注意,即使在函数体内声明了一个与函数体外同名的静态局部变量,函数执行完也不会对函数体外的变量改变,请看以下代码:

void Test()
{
	static int Name1 = 2;
	static int Name2 = 3;
	cout << Name1;
}
void main()
{
	int Name1 = 1;
	Test();//结果是2
	cout << Name1 << endl;//结果是1
	cout<<Name2;//报错
	system("pause");
}

请再看以下代码:

size_t count_calls()
{
	static size_t ctr = 0;
	return ++ctr;
}
size_t count_calls2()
{
	size_t ctr = 0;
	return ++ctr;
}
void main()
{
	for (size_t i = 0;i < 10;i++)
	{
		cout << count_calls() << endl;
	}//结果是1-10的数
	for (size_t i = 0;i < 10;i++)
	{
		cout << count_calls2() << endl;
	}//结果是111111(10个)
	system("pause");
}

在控制流第一次经过count_calls的ctr的定义之前,ctr被创建并初始化为0。每次调用使得ctr+1。每次执行count_calls时,变量ctr的值都已经存在并且等于函数上一次退出时ctr的值,因此第二次调用时ctr的值是1,第三次是2,以此类推。

或许有人会有这样的理解:每次执行count_calls()时都对ctr重新初始化一次为0。这个想法看上去合理,但恰恰相反。诸君应该了解,在c++中,是禁止对一个变量进行两次初始化的,可以这样理解:调用count_calls()时,编译器发现ctr是静态变量,首先先去找了它是否被初始化,如果被初始化,则直接取出值,否则才执行初始化语句。但普通变量编译器则会直接再做一次初始化,这就是调用count_calls2()会是10个1的原因。

函数声明

和其他名字一样,函数的名字也必须在使用之前声明。类似于变量,函数只能定义一次,但可以多次声明,如果一个函数永远也不会被用到,那么它可以只有声明没有定义。

函数的生命和函数的定义非常类似,唯一的区别是函数声明无须函数体,用一个分号代替即可。

因为函数的生命不包含函数体,所以也就无须形参的名字(但最好加上,便于阅读)。声明方法请看以下代码:

void print(vector<int>::const_iterator beg,vector<int>::const_iteratir end);

函数的三要素(返回类型、函数名、形参类型)描述了函数的接口,说明了调用该函数所需的全部信息。函数声明也被称为函数原型。

在头文件中进行函数声明

定义函数的源文件需要把含有函数声明的头文件包含进来,编译器负责验证

const形参和实参

和其他初始化一样,当用实参初始化形参时会忽略掉顶层const。也就是说,当形参有顶层const时,传给它常量对象或非常量对象都是可以的:

void fcn(const int i){/*fcn能读取i,但是不能向i写值*/}

调用fcn时,既可以传入const int 也可以传入int。但是忽略形参的顶层const会带来意想不到的结果:

void fcn(const int i){/*fcn能读取i,但是不能向i写值*/}
void fcn(int i){/*错误,重复定义了fcn(int i)*/}

在c++中,允许我们定义若干具有相同名字的函数,不过前提是不同函数的形参列表有明显的区别。因为顶层const被忽视,所以以上两个函数本质上是相同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值