命名空间、缺省参数、函数重载、引用


 
 
 

1.命名空间:

一个命名空间就定义了一个新的作用域,其内的所有内容全部局限于该命名空间。

这样做的目的:避免命名冲突或名字污染

命名空间的定义方式

  1. 常见的:
namespace N
{
	int a;
	int ADD(int left, int right)
	{
		return left + right;
	}
}
  1. 嵌套定义:
namespace N
{
	int a;
	int ADD(int left, int right)
	{
		return left + right;
	}
	namespace N1
	{
		int a;
		int ADD(int left, int right)
		{
			return left + right;
		}
	}
}
  1. 在同一个文件中,存在多个相同名称的命名空间,编译器最终会合并为一个命名空间
namespace N
{
	int a;
	int ADD(int left, int right)
	{
		return left + right;
	}
}
namespace N
{
	int c;
	int d;
}

注意: 第三种方式是同一个命名空间,写在了不同的位置,最终会进行合并,不能有相同的变量或者函数名(参数类型完全一致)。否则会发生重定义

命名空间的使用:

  1. 采用作用域运算符:::N::变量/函数
    如:N::c;
  2. 使用 using N(命名空间名):: 变量/函数
    这种情况适合某命名空间中的某些变量或函数经常用到,就可以直接这样使用。这样就相当于在当前文件中,该变量成为了 “全局变量” ,函数在整个工程中可以直接调用。
    如:
using N::ADD;// ②
using N::a;

int main()
{
	N::c;//①
	return 0;
}
  1. 使用 using namespace N(命名空间);
    该命名空间中的变量或函数都能经常用到。则将该命名空间中的所有成员作用域当成该文件的全局作用。

 
 
 

2.缺省参数:

缺省参数是在声明或定义函数时为参数指定一个默认值。在函数调用的时候,若没有指定实参则使用默认值,否则使用指定的实参

缺省参数类型:

  1. 全缺省参数:所有的函数参数都有默认值。
int ADD(int first = 1, int second = 2, int third = 3)
{
	return first + second + third;
}
  1. 半缺省参数:只有部分函数参数有默认值。
int ADD(int first, int second, int third = 3)
{
	return first + second + third;
}

注意:

  1. 半缺省参数必须从右往左给出。
  2. 缺省参数不能同时在函数声明和定义中出现
  3. 缺省值必须是常量或者全局变量

单独在函数声明和定义中都可以给出。若重复出现,会出现重定义默认参数

一般建议缺省参数默认值在函数声明中给出。

3.函数重载:

函数重载:在C++中,同一个作用域声明几个功能类似的同名函数。这些同名函数的形参列表(参数类型,参数个数,参数顺序)不同。

参数列表的不同体现:

  1. 参数个数不同: 下面这两个同名函数,不同参数个数,构成了函数重载。
int ADD(int right, int left)
{
	return right + left;
}

int ADD()
{
	return 10;
}
  1. 参数类型不同:下面两个同名函数,其参数类型不一样,构成了函数重载。
int ADD(int right, int left)
{
	return right + left;
}

double ADD(double right,double left)
{
	return right + left;
}
  1. 参数类型顺序不同:下面两个同名函数,参数类型的顺序不一样,构成了函数重载。
double ADD(int right, double left)
{
	return right + left;
}

double ADD(double right, int left)
{
	return right + left;
}

这样一份代码:

int ADD(int right, int left)
{
	return right + left;
}

double ADD(double right, double left)
{
	return right + left;
}

int main()
{
	ADD(1, 2.2);
	return 0;
}

在这份代码中,会出现怎样的错误呢?

在函数调用过程中,会进行形参实例化,但都没有完全匹配的类型。则会发生隐式类型转换int->double 或者double->int 类型,但是在上述调用中,会发现,两种转换都可以进行。而编译器并不知道选择哪一个。就会产生错误。

VS2017中的报错:
在这里插入图片描述
这样产生的错误叫做二义性 | 歧义。

函数重载调用原理: 在编译阶段确定调用什么函数

  1. 在编译阶段,编译器对类型进行推演,根据推演的结果寻找类型匹配的函数进行调用。
  2. 若有类型完全匹配的函数,则直接进行调用。
  3. 没有类型完全匹配的函数,则进行隐式类型转换
  4. 若隐式类型转换后有对应的函数(没有产生二义性)则进行调用。
  5. 没有或产生了二义性,进行报错

所以在执行ADD(1,2.2)的时候,产生了二义性,多重转换,编译器报错。

函数的返回值类型不一样,不构成函数重载。

int ADD(int right, int left)
{
	return right + left;
}

double ADD(int right, int left)
{
	return right + left;
}

这样是不构成函数重载的。

C语言为什么不支持函数重载?而C++支持函数重载呢?

在VS环境中看一下:

int ADD(int right, int left);

int main()
{
	ADD(1, 2);
	return 0;
}

没有函数定义,进行报错。
在C++中,报错为:
在这里插入图片描述
在C语言中,报错为:
在这里插入图片描述
上述代码是一样的,但是编译器在编译的时候,在底层使用的名字是不一样的。

C语言:仅仅对函数名字前加了一个_
C++:对函数名解释为?ADD@@YAHHH@Z
若在C++中,返回值类型为double,参数类型都为double,其函数名是什么呢?
在这里插入图片描述
在这里插入图片描述

 
 
 
再看一下Linux环境中是怎样的:
C++:
在这里插入图片描述
C语言:
在这里插入图片描述
可以看到在C语言中,对函数修饰的比较简单。而C++对函数修饰的比较具体,在Linux中,包含了函数名的长度,函数参数的类型第一个字母。

所以,C语言不支持函数重载,因为它对函数修饰的比较简单,如果有重名函数,就会发生重定义。而在C++中,对函数参数类型也进行了修饰,同名函数不同的参数列表,其底层描述是不一样的。

 
 
 

C语言和C++函数的区别?

  1. 返回值类型:

C语言中:函数没有返回值类型,系统默认返回int类型。
C++中:需要带上返回值类型。否则程序报错。

  1. 函数参数:

C语言中:函数无参数,但调用时,传递了参数,编译器可以编译通过。
C++中,函数无参数,调用时,传递了参数,编译器无法编译通过。

  1. C++函数参数可以带默认值:即缺省参数
  2. 函数命名规则不同:C++中支持函数重载,而C语言不支持

C++编译器比C语言编译器语法检测更加严格。
 
 
 
 

4.引用:

引用是对当前的已存在变量取一个别名编译器不会对该引用开辟空间,而是和被引用的变量共用一块内存空间。
在这里插入图片描述
引用和被引用变量共用一个内存空间。
引用发生了数据更改,被引用的变量也会发生变化。

引用类型要和变量的类型保持一致。

引用的注意事项:

  1. 在定义时必须初始化。即指定要引用的变量。
  2. 一个变量可以有多个引用。即一个实体有多个别名。
  3. 引用一个实体之后,不能再引用其它实体。

引用一个常量时,也要加上const,不能通过引用来修改实体。

int main()
{
	double d = 12.56;
	const int& rd = d;
	return 0;
}

上述代码的引用是可以引用成功的。但是会有问题:
在这里插入图片描述

这是为什么呢?
引用变量会与引用实体的类型要一致。double与int之间发生了隐式类型转换,double转化为了int。
rd引用d,编译器创建了一个临时空间作为过渡,让rd去引用这个临时空间。
但是这个空间的地址,在一般情况下是不知道的,则无法修改。所以这个空间具有了常性 (常量特性)。
rd相当于就引用了一个常量。则必须加上一个const。

 
 
引用的应用场景:

  1. 使代码简洁。
  2. 做函数参数。
  3. 做函数返回值。

做函数返回值时要注意:

  1. 若返回对象已经归还给系统,不能用引用返回。否则返回的是一块非法区域。
  2. 若返回对象未归还给系统,可以使用引用返回。

对函数传值和传引用的比较:传引用比传值效率快的多,因为传引用没有形参实例化的过程,形参只是实体的一个别名,减少了参数压栈带来的开销,所以效率快的多。

函数传指针和传引用的比较:两者的效率是差不多的。

void Swap(int& right, int& left)
{
	int tmp = right;
	right = left;
	left = tmp;
}

void Swap(int *right, int *left)
{
	int tmp = *right;
	*right = *left;
	*left = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	Swap(a, b);

	Swap(&a, &b);
	return 0;
}

对其反汇编:
在这里插入图片描述
发现,两个代码进行的操作是一样的。引用按照指针的方式进行了操作。

引用在底层的操作和指针的方式实现的。

引用和指针的区别:

  1. 从概念上看:
    引用:是一个变量的别名,编译器不会对其开辟空间。引用和实体共用一块空间。
    指针:编译器会对其分配空间,指针指向的是地址。
  2. 从底层上看:引用和指针没有什么区别----引用就是指针
  3. 从特性上看:

1.引用必须初始化,指针没有要求
2.引用实体之后,不能再引用其它实体。指针可以指向任意同意类型的地址
3.没有NULL引用,但有NULL指针
4.sizeof中,引用结果为其引用类型的大小,而指针在32位平台下,始终是4个字节
5.引用自加1为实体加1,指针自加1,为加上其类型的大小
6.有多级指针,但没有多级引用
7.访问实体时,指针需要解引用,引用能直接使用
8.引用比指针相对来说更安全

猜你喜欢

转载自blog.csdn.net/w903414/article/details/108714494