C++入门--缺省、函数重载、引用学习

1.缺省参数

1.1缺省参数概念

缺省参数是指在声明或定义函数时为函数的参数指定一个缺省值,如果在调用该函数的时候没有指定参数,函数会使用该参数的缺省值,否则使用指定的参数。

#include
using std::cout;
using std::cin;
using std::endl;
void Func(int a=0)
{
	cout << a << endl;
}
int main()
{
	Func();//没有传参数,使用缺省参数
	Func(10);//使用指定参数
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第1张图片

1.2缺省参数分类

全缺省参数

void Func(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

半缺省参数

#include
using namespace std;
void Func(int a, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

使用半缺省参数,必须传值给没有缺省值的参数,否则会报错。

C++入门--缺省、函数重载、引用学习_第2张图片

注意: 1.半缺省参数必须从右向左给出,也不能间隔着给,因为函数是从左往右传参的。

错误的例子:

#include
using namespace std;
void Func(int a = 10, int b, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	Func(10);
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第3张图片
2.缺省参数不能在函数声明和定义中同时给出,声明给缺省参数,定义则不给。

在预处理阶段头文件会展开,函数声明会出现在文件中,到编译阶段的时候,会进行检查。如果你使用的函数没有传参,同时声明的函数中的参数不是缺省参数,编译器会报错,如果声明和定义中同时出现缺省参数,恰巧两个位置参数的缺省值不同,编译器无法确定用哪个缺省值。

3.缺省值必须是常量或者全局变量
4.C语言不支持(某些编译器不支持)

使用缺省参数的好处,比如在建立顺序栈的时候,不知道要插入多少个数据的时候,可以使用缺省参数,同时可以建立不同空间大小的顺序栈

2.函数重载

2.1函数重载的概念

函数重载:C++允许在同一作用域中申明几个功能类型的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,可以解决功能类似功能不同的函数问题。

参数类型不同

1.参数类型不同
#include
using namespace std;
int Add(int left, int right)
{
	cout << "int Add( int left,int right)" << endl;
	
	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;

	return left + right;
}
int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 2.2) << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第4张图片

2.参数个数不同

#include
using namespace std;
void Func()
{
	cout << "void Func()" << endl;
}
void Func(int a)
{
	cout << "void Func(int a)" << endl;
}
int main()
{
	Func();
	Func(10);
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第5张图片

3.参数顺序不同

#include
using namespace std;
void f(int a, char b)
{
	cout << "void f(int a, char b)" << endl;
}
void f(char b, int a)
{
	cout << "void f(char b, int a)" << endl;
}
int main()
{
	f(10, 'a');
	f('a', 10);
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第6张图片
4.如果仅仅是返回值不相同,不构成重载(编译器不能通过),调用歧义

#include
using namespace std;
void Add(int x, int y)
{
	cout << "void Add(int x, int y)" << endl;
}
int Add(int x, int y)
{
	cout << "int Add(int x, int y)" << endl;
}
int main()
{
	Add(1, 2);
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第7张图片
函数重载和缺省参数,在无参调用时存在歧义

#include
using namespace std;
void Func()
{
	cout << "void Func()" << endl;
}
void Func(int a = 1)
{
	cout << "void Func(int a = 1)" << endl;
}
int main()
{
	Func();
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第8张图片

函数重载的原理

首先我们知道,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接

预处理:头文件展开\宏替换、删除注释等,生成.i为后缀的文件
编译:检查语法是否正确,生成汇编代码,生成.s为后缀的文件
汇编:汇编代码转化为二进制代码,生成.o为后缀的文件
链接:合并符号表,生成.exe的可执行程序

//game.h
#include
using namespace std;
int Add(int left, int right);
double Add(double left, double right);
//game.c
#include"game.h"
int Add(int left, int right)
{
	cout << "int Add( int left,int right)" << endl;

	return left + right;
}
double Add(double left, double right)
{
	cout << "double Add(double left, double right)" << endl;

	return left + right;
}
//test.c
#include"game.h"

int main()
{
	cout << Add(1, 2) << endl;
	cout << Add(1.1, 2.2) << endl;
	return 0;
}

编译阶段:
C++入门--缺省、函数重载、引用学习_第9张图片

1.使用同名函数,在编译的时候,编译器通过参数不同来修饰函数名
2.在链接的时候,通过修饰后的函数名在符号表中找到相应的地址,从而做到对同名函数的调用
3.不是所有函数都需要链接,当定义函数和调用在同一个文件的时候,会直接给相应地址
4.修改的是符号表的函数名

总结: C语言不支持函数名修饰规则,无法对同名函数进行区分,所以不支持重载;C++可以通过函数名修饰规则在编译的时候,会对同名函数进行修改,使编译器可以区分同名函数支持重载,至于函数名的修饰规则,则取决于不同的编译器。

3.引用

3.1引用的概念

引用不是新定义一个变量,而是给已经存在的变量取一个别名,编译器不会为引用的变量开辟内存空间,它和它引用的变量公用一块内存空间。

引用的使用形式:类型& 引用的变量名{对象名}=引用实体

eg:

#include
using std::cout;
using std::cin;
using std::endl;
int main()
{
	int a = 1;
	int& b = a;
	int& c = b;
	int& d = c;
	
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
	cout << d << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第10张图片

3.2引用的特性

1.引用在定义时必须初始化

#include
using namespace std;
int main()
{
	int a = 10;
	int& ra;//编译会报错
	return 0;
}

编译运行的结果为:
C++入门--缺省、函数重载、引用学习_第11张图片
正确的eg:

#include
using namespace std;
int main()
{
	int a = 10;
	int& ra = a;
	return 0;
}

2.一个变量可以有多个引用

#include
using namespace std;
int main()
{
	int a = 10;
	int& b = a;
	int& c = a;
	int& d = a;

	cout << b << endl;
	cout << c << endl;
	cout << d << endl;

	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第12张图片
3.引用一旦引用一个实体,再不能引用其他实体

#include
using namespace std;
int main()
{
	int a = 10;
	int x = 5;
	int& b = a;
	int& b = x;
	
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第13张图片

3.3使用场景

注意:引用的类型必须和实体是同种类型

eg1:

//交换变量
#include
using namespace std;
void Swap1(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int a = 1;
	int b = 2;
	cout << "交换之前:>" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	Swap1(a, b);
	cout << "交换之后:>" << endl;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第14张图片
代码运行的结果为:
eg2:

//交换地址
#include
using namespace std;
void Swap2(int*& p1, int*& p2)
{
	int* tmp = p1;
	p1 = p2;
	p2 = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	int* p1 = &a;
	int* p2 = &b;
	cout << "交换地址之前:>" << endl;
	cout << "p1 = " << p1<< endl;
	cout << "p2 = " << p2 << endl;
	//Swap2(&a, &b);
	Swap2(p1, p2);
	cout << "交换地址之后:>" << endl;
	cout << "p1 = " << p1 << endl;
	cout << "p2 = " << p2 << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第15张图片

总结: 引用做参数的优点:①输出型参数,可以通过形参改变实参,减少编译器压栈的操作,提高了效率;②引用做参数还适用于大对象/深拷贝对象。

2.引用做返回值
eg1:

//引用做返回值--全局变量
#include
using namespace std;
int& Count()
{
	static int n = 0;
	n++;
	return n;
}
int main()
{
	int ret = Count();
	cout << ret << endl;
	return 0;
}

正常传参返回:不是直接返回,在前面我们学习过的函数栈帧的知识,在销毁栈帧之前,把返回的值存放到一个临时变量(可能是寄存器)中,然后再把临时变量的值拷贝到main函数的ret中。

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第16张图片
eg2:

//引用做返回值--局部变量
#include
using std::cout;
using std::cin;
using std::endl;
int& Count(int x)
{
	int n = x;
	n++;
	return n;
}
int main()
{
	int ret = Count(1);//ret的值是函数栈帧销毁后,变量n里面存放的值,可能会被清理
	//修改ret不会访问到n的内存空间
	cout << ret << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第17张图片

当在返回值中使用引用的时候,返回局部变量的引用是否存在问题呢?这里打印的ret的值是不确定的,如果Count函数结束,没有清理栈帧,那么ret的结果侥幸正确,如果Count函数结束,清理栈帧,那么ret的结果是随机值。

eg3:

#include
using std::cout;
using std::cin;
using std::endl;
int& Count(int x)
{
	int n = x;
	n++;
	return n;
}
int main()
{
	int& ret = Count(1);//ret是变量n的别名,指向变量n的空间,修改ret会造成非法访问
	cout << ret << endl;
	//使用其他函数,函数栈帧会被清理
	printf("aaaaaaaaaa\n");
	rand();
	cout << ret << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第18张图片
eg4:

//引用做返回值,具有修改返回值和获取返回值的功能
#include
#include
using std::cout;
using std::cin;
using std::endl;
int& WRarry(int* a, int pos)
{
	assert(a);
	return a[pos];
}
int main()
{
	int a[10] = { 0,1,2,3,4,5,6,7,8,9 };
	WRarry(a, 0) = 1;
	cout << WRarry(a, 0) << endl;
	WRarry(a, 0) += 5;
	cout << WRarry(a, 0) << endl;
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第19张图片

总结: 1.基本任何场景都可以引用传参;2.谨慎用引用返回值,出了函数作用域,对象不在了,不能用引用返回,出了函数作用域;对象还在,可以用引用返回3.使用静态变量、动态内存开辟空间的变量;4.引用做返回值,具有修改返回值和获取返回值的功能。

3.4常引用

eg1:

#include
using std::cout;
using std::sin;
using std::endl;
void TestConstRef()
{
	//引用过程中,权限可以平移或者缩小
	int x = 0;
	int& y = x;
	const int& z = y;
	x++;
	cout << x << endl;
}

int类型的yint类型的x取别名,相当于权限平移,对const int 类型的z对int类型的y取别名,变量z不能被修改,权限缩小,但不会影响变量x\y的性质。

eg2:

void TestConstRef()
{
	//int& a = 10;
	const int& a = 10;
}

如果直接对常量取别名,此时别名是一个变量,相当于常量的权限扩大了,不能直接对常量进行引用,需要加上const修饰–常引用

eg3:

void TestConstRef()
{
	const int a = 10;
	//int& b = a;
	int& b = 10;
}

在上面代码中,aconst int修饰成为了一个常量,如果使用int&a取别名bb的类型为int类型,b可以被修改相当于权限扩大了。

eg4:

void TestConstRef()
{
	int a = 1;
	double d = 1.1;
	int& c = d;
}

在上面的代码中,发生了强制类型转换。以上面的代码为例,double类型的d在转换int类型的时候,会转换后的变量d存放到一个临时变量中去,再由临时变量拷贝到变量a中去,而该临时变量具有常性(即常量的性质,相当于有const修饰),不直接进行引用,而是进行常引用。

eg5:

#include
using std::cout;
using std::sin;
using std::endl;
void TestConstRef()
{
	//引用过程中,权限可以平移或者缩小
	int x = 0;
	int& y = x;
	const int& z = y;
	x++;
	cout << x << endl;
}

int TestConstRef()
{
	static int x = 0;
	return x;
}
int main()
{
	int& ret=TestConstRef();
	return 0;
}

代码运行的结果为:
C++入门--缺省、函数重载、引用学习_第20张图片

在函数中不管使用局部变量还是全局变量,在返回值之前会先把值存放到临时变量中,再由临时变量拷贝到变量ret中去,该临时变量具有常性(相当于有const修饰),不能直接进行引用,而是进行常引用

总结: 引用过程中,权限可以缩小或平移,但权限不能放大,对常量或具有常量性质的变量(实质也是常量),需要进行常引用(有const修饰),对变量进行常引用相当于权限缩小。

3.5引用和指针的区别

eg:

#include
using std::cout;
using std::cin;
using std::endl;
int main()
{
	int a = 10;
	//语法层面:不开空间,是对a取别名
	int& ra = a;
	ra = 20;
	//语法层面:开空间存储,存储a的地址
	int* pa = &a;
	*pa = 30;
	return 0;
}

转化为汇编代码的结果为:
C++入门--缺省、函数重载、引用学习_第21张图片

从汇编代码实现的角度看,引用类似指针的实现方式;但是C++在语法层面上默认引用是不开空间的,而指针需要开辟空间存储地址。

4.总结

本章我们一起学习了C++缺省、函数重载、引用的相关知识,希望对大家学习C++有些许帮助!感谢大家阅读,如有不对,欢迎纠正!⭐⭐⭐

你可能感兴趣的:(C++初阶学习,c++,学习,笔记)