C++相关概念和易错语法(1)(命名空间域、缺省参数、函数重载)

本文详细解释了命名空间的作用域、生命周期,以及与全局变量的区别。还讨论了命名空间的访问规则、合并与使用,包括函数重载、缺省参数的注意事项和函数调用中的细节。
摘要由CSDN通过智能技术生成

1.命名空间域的作用域

命名空间域没有生命周期的概念,因为命名空间域里没有任何代码执行。

局部域,全局域,命名空间域,类域影响的是语法编译的查找规则(查找函数,声明,定义),局部域、全局域影响生命周期。

命名空间的作用域比较容易产生误解,下面我来讲解一下:

其中在命名空间里面定义的变量在初始化规则这块和全局变量相同,下面来检验一下:


#include <iostream>
using namespace std;

namespace space
{
	int a;
}

using namespace space;

void fun()
{
	cout << "fun " << space::a << endl;

	space::a = 2;
}

int main()
{
	cout << space::a << endl;

	fun();

	cout << space::a << endl;

	return 0;
}

运行结果如图:

可以看到int a的初始化也遵循全局变量定义的规则,即默认初始化为0

作用域:命名空间域相当于定义了一个新的作用域,有且仅当主动调用它时,才能够访问和使用它。

在理解这一点上,我们可以通过熟悉的函数来理解。在任何函数里面定义的变量,你在函数外面都访问不了,因为这些变量的作用域都仅限于函数的那个大括号。namespace大括号定义的变量也是如此。

但是区别在于函数里的变量只有在进入函数后在函数里面访问,而namespace定义的变量可以在任何地方调用。调用时加的::是域作用限定符,是在namespace这个新的作用域内部对它进行访问。本质上和访问函数内部变量一致。

2.命名空间域的访问规则

一般情况下,命名空间域不会被访问,在使用using被展开后会在先局部再全局访问后,访问这个命名空间。

但是要注意局部是指在程序调用那部分局部域,局部域也有可能是一个命名空间域,下面看一段代码:


#include <iostream>
using namespace std;

int a = 0;

namespace space
{
	int a = 60;

	void fun()
	{
		cout << ::a << endl;
		cout << a << endl;
	}
}

int main()
{
	space::fun();

	return 0;
}

执行结果是:

调用cout << a << endl;时就会先访问局部域,前面已经说过命名空间域是一个新的作用域,所以这里访问的局部域就是这个命名空间域。这里极容易混淆。

在任何地方(可能是全局的函数,可能是命名空间域里定义的函数),在先局部再全局访问后,都会访问using的命名空间下面来看一段代码:


#include <iostream>
using namespace std;

int a = 0;

namespace space2
{
	int b = 20;
}

using namespace space2;

namespace space1
{
	int a = 60;

	void fun()
	{
		cout << ::a << endl;
		cout << a << endl;
		cout << "b == " << b;
	}
}

int main()
{
	space1::fun();

	return 0;
}

运行结果:

注意:上面所说的“先局部再全局访问后,访问这个命名空间”中全局和命名空间的访问同级。也就是说如果全局和已被展开的命名空间中有命名冲突,会报错!

3.namespace的合并和使用

在不同文件中,如果定义的最外层namespace(namespace可以嵌套)名字相同,则最终运行程序时会合并,当然,如果是在同一个文件里定义了相同名字的namespace,也会合并

使用的时候using可以局部展开,也可以将整个命名空间展开。但是在大型的项目中一般用局部展开,因为如果当同时展开多个命名空间时,根据访问规则,如果展开的这些命名空间有重名的情况,就会报错。

在使用namespace定义函数时,要注意声明和定义处都要使用namespace

否则会报错,因为链接时找不到这个函数

我们平时使用的<iostream>最外层就用了namespace std来包装,这也是为什么我们使用流插入cin和流提取cout时要用using namespace std或using std::cout;等

4.缺省参数的使用注意事项

当使用函数时遇到不确定参数时,可以使用缺省参数做默认值。

如果要使用多个文件,用到函数声明,缺省参数只能在声明处使用才生效且在定义时不能使用,就算定义的默认值和声明的默认值相同,因为语法检查时会被识别为重定义。

且如果要使用缺省参数必须在声明处使用,因为语法检查导致编译都无法通过,不会链接。报错会显示不能只接收一个参数

缺省参数需要从右向左给,不然会报错且会产生歧义

5.构成函数重载与使用同名函数之间的关系

函数重载实现条件:同一作用域、传参的类型个数、顺序、种类不同(返回值、缺省参数等不在实现条件内)

函数重载实现的根本:调用函数时类型修饰函数名

我们需要注意的是,两个同名函数如果满足传参的类型个数、顺序、种类不同,那么它们一定都能在using之后正常使用,即使它们不构成函数重载,下面举个例子:


#include <iostream>
using namespace std;

namespace space1
{
	void fun(int a, float b)
	{
		cout << "space1" << endl;
	}
}

namespace space2
{
	void fun(float a, int b)
	{
		cout << "space2" << endl;
	}
}

using namespace space1;
using namespace space2;

int main()
{
	fun(1, 2.0f);

	return 0;
}

该程序能正常运行,结果是:

我们需要知道,函数重载的条件是定义的两个函数在同一作用域,但这两个函数在两个namespace里面定义,一定不构成重载,但展开后却能正常使用。这是因为调用函数时函数名会进行修饰,所以就算同名,只要参数传递不引起歧义,也能正常使用。

所以两个同名函数就算不构成重载,在传参没有歧义下也能使用,函数重载可以说是使用同名函数的一种特殊情况

在参数完全一致的情况下,同名函数就会产生歧义了:

这种情况下必须指定作用域才能正常使用

这种写法跟函数重载肯定也没什么关系了。

6.函数的调用

调用函数时,函数地址是执行第一句汇编指令的地址,要有定义才有这个地址。对于声明和定义分离的函数来说,程序是在.o(.obj)链接时去找函数

7.同名函数调用歧义

函数名、参数完全一致的函数调用歧义,还有一些调用歧义比较容易出错,这里分享一下:

对于整型家族,它们之间可以相互转换,这种情况下要注意参数一一对应。

还有一种是当存在缺省参数时,传参个数导致的歧义:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值