1.缺省参数
1.1缺省参数概念
缺省参数是指在声明或定义函数时为函数的参数指定一个缺省值,如果在调用该函数的时候没有指定参数,函数会使用该参数的缺省值,否则使用指定的参数。
#include<iostream>
using std::cout;
using std::cin;
using std::endl;
void Func(int a=0)
{
cout << a << endl;
}
int main()
{
Func();//没有传参数,使用缺省参数
Func(10);//使用指定参数
return 0;
}
代码运行的结果为:
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<iostream>
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;
}
使用半缺省参数,必须传值给没有缺省值的参数,否则会报错。
注意: 1.半缺省参数必须从右向左给出,也不能间隔着给,因为函数是从左往右传参的。
错误的例子:
#include<iostream>
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;
}
代码运行的结果为:
2.缺省参数不能在函数声明和定义中同时给出,声明给缺省参数,定义则不给。
在预处理阶段头文件会展开,函数声明会出现在文件中,到编译阶段的时候,会进行检查。如果你使用的函数没有传参,同时声明的函数中的参数不是缺省参数,编译器会报错,如果声明和定义中同时出现缺省参数,恰巧两个位置参数的缺省值不同,编译器无法确定用哪个缺省值。
3.缺省值必须是常量或者全局变量
4.C语言不支持(某些编译器不支持)
使用缺省参数的好处,比如在建立顺序栈的时候,不知道要插入多少个数据的时候,可以使用缺省参数,同时可以建立不同空间大小的顺序栈
2.函数重载
2.1函数重载的概念
函数重载:C++允许在同一作用域中申明几个功能类型的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,可以解决功能类似功能不同的函数问题。
参数类型不同
1.参数类型不同
#include<iostream>
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;
}
代码运行的结果为:
2.参数个数不同
#include<iostream>
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;
}
代码运行的结果为:
3.参数顺序不同
#include<iostream>
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;
}
代码运行的结果为:
4.如果仅仅是返回值不相同,不构成重载(编译器不能通过),调用歧义
#include<iostream>
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;
}
代码运行的结果为:
函数重载和缺省参数,在无参调用时存在歧义
#include<iostream>
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;
}
代码运行的结果为:
函数重载的原理
首先我们知道,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接
预处理:头文件展开\宏替换、删除注释等,生成.i为后缀的文件
编译:检查语法是否正确,生成汇编代码,生成.s为后缀的文件
汇编:汇编代码转化为二进制代码,生成.o为后缀的文件
链接:合并符号表,生成.exe的可执行程序
//game.h
#include<iostream>
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;
}
编译阶段:
1.使用同名函数,在编译的时候,编译器通过参数不同来修饰函数名
2.在链接的时候,通过修饰后的函数名在符号表中找到相应的地址,从而做到对同名函数的调用
3.不是所有函数都需要链接,当定义函数和调用在同一个文件的时候,会直接给相应地址
4.修改的是符号表的函数名
总结: C语言不支持函数名修饰规则,无法对同名函数进行区分,所以不支持重载;C++可以通过函数名修饰规则在编译的时候,会对同名函数进行修改,使编译器可以区分同名函数支持重载,至于函数名的修饰规则,则取决于不同的编译器。
3.引用
3.1引用的概念
引用不是新定义一个变量,而是给已经存在的变量取一个别名,编译器不会为引用的变量开辟内存空间,它和它引用的变量公用一块内存空间。
引用的使用形式:类型& 引用的变量名{对象名}=引用实体
eg:
#include<iostream>
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;
}
代码运行的结果为:
3.2引用的特性
1.引用在定义时必须初始化
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int& ra;//编译会报错
return 0;
}
编译运行的结果为:
正确的eg:
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int& ra = a;
return 0;
}
2.一个变量可以有多个引用
#include<iostream>
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;
}
代码运行的结果为:
3.引用一旦引用一个实体,再不能引用其他实体
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int x = 5;
int& b = a;
int& b = x;
return 0;
}
代码运行的结果为:
3.3使用场景
注意:引用的类型必须和实体是同种类型
eg1:
//交换变量
#include<iostream>
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;
}
代码运行的结果为:
代码运行的结果为:
eg2:
//交换地址
#include<iostream>
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;
}
代码运行的结果为:
总结: 引用做参数的优点:①输出型参数,可以通过形参改变实参,减少编译器压栈的操作,提高了效率;②引用做参数还适用于大对象/深拷贝对象。
2.引用做返回值
eg1:
//引用做返回值--全局变量
#include<iostream>
using namespace std;
int& Count()
{
static int n = 0;
n++;
return n;
}
int main()
{
int ret = Count();
cout << ret << endl;
return 0;
}
正常传参返回:不是直接返回,在前面我们学习过的函数栈帧的知识,在销毁栈帧之前,把返回的值存放到一个临时变量(可能是寄存器)中,然后再把临时变量的值拷贝到
main
函数的ret
中。
代码运行的结果为:
eg2:
//引用做返回值--局部变量
#include<iostream>
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;
}
代码运行的结果为:
当在返回值中使用引用的时候,返回局部变量的引用是否存在问题呢?这里打印的
ret
的值是不确定的,如果Count
函数结束,没有清理栈帧,那么ret
的结果侥幸正确,如果Count
函数结束,清理栈帧,那么ret
的结果是随机值。
eg3:
#include<iostream>
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;
}
代码运行的结果为:
eg4:
//引用做返回值,具有修改返回值和获取返回值的功能
#include<iostream>
#include<assert.h>
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;
}
代码运行的结果为:
总结: 1.基本任何场景都可以引用传参;2.谨慎用引用返回值,出了函数作用域,对象不在了,不能用引用返回,出了函数作用域;对象还在,可以用引用返回3.使用静态变量、动态内存开辟空间的变量;4.引用做返回值,具有修改返回值和获取返回值的功能。
3.4常引用
eg1:
#include<iostream>
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
类型的y
对int
类型的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;
}
在上面代码中,
a
由const int
修饰成为了一个常量,如果使用int&
对a
取别名b
,b
的类型为int
类型,b
可以被修改相当于权限扩大了。
eg4:
void TestConstRef()
{
int a = 1;
double d = 1.1;
int& c = d;
}
在上面的代码中,发生了强制类型转换。以上面的代码为例,
double
类型的d在转换int
类型的时候,会转换后的变量d
存放到一个临时变量中去,再由临时变量拷贝到变量a
中去,而该临时变量具有常性(即常量的性质,相当于有const
修饰),不直接进行引用,而是进行常引用。
eg5:
#include<iostream>
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;
}
代码运行的结果为:
在函数中不管使用局部变量还是全局变量,在返回值之前会先把值存放到临时变量中,再由临时变量拷贝到变量
ret
中去,该临时变量具有常性(相当于有const
修饰),不能直接进行引用,而是进行常引用
总结: 引用过程中,权限可以缩小或平移,但权限不能放大,对常量或具有常量性质的变量(实质也是常量),需要进行常引用(有
const
修饰),对变量进行常引用相当于权限缩小。
3.5引用和指针的区别
eg:
#include<iostream>
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++在语法层面上默认引用是不开空间的,而指针需要开辟空间存储地址。
4.总结
本章我们一起学习了C++缺省、函数重载、引用的相关知识,希望对大家学习C++有些许帮助!感谢大家阅读,如有不对,欢迎纠正!⭐⭐⭐