函数重载
函数重载允许函数间可以重名,在函数调用时,不再单单以函数名来寻址调用,而是函数名加参数类型及其顺序来调用的,这方便了一些函数的命名。
例如要实现整型之间的交换,以及浮点型之间的交换:
用C语言写时,得想两个不同函数名,由于又都是实现交换,两个函数名也一般只有前后缀的区别,一来二去使得实现时麻烦,在调用时也麻烦,你看我解释估计都会嫌麻烦;
但用C++来实现的话就少很多事了,两个函数的函数名都写Swap,后面参数按需求写就行,就少了不少烦心事,代码如下:
void Swap(int* num1, int* num2)
{
int tmp = *num1;
*num1 = *num2;
*num2 = tmp;
}
void Swap(double* num1, double* num2)
{
double tmp = *num1;
*num1 = *num2;
*num2 = tmp;
}
int main()
{
int num1 = 3;
int num2 = 6;
double _num1 = 1.1; // 变量就不支持重名了哦
double _num2 = 2.2;
cout << "交换前" << endl;
cout << num1 << " " << num2 << endl;
cout << _num1 << " " << _num2 << endl;
Swap(&num1, &num2);
Swap(&_num1, &_num2);
cout << "交换后" << endl;
cout << num1 << " " << num2 << endl;
cout << _num1 << " " << _num2 << endl;
return 0;
}
这里利用重载就解决了一些烦心事
函数重载的注意点
不同的函数返回类型不能构成函数重载,构成函数重载的条件有三个:函数名,参数类型及其顺序。
我们站在编译器的角度看下重载是怎么实现的,对于每个函数,编译器都会生成一个名称,对于C语言,这个名称只与函数名有关,而C++中这个生成的名称与函数名,参数类型及其顺序有关,这就使得后续汇编和链接、以及调用时能够做出区分。
引用
上面的Swap()函数调用起来是不是有点别扭?指针变量用起来是不是有点难受?这里介绍一下指针的兄弟—引用&。
先看下&的效果,再进行讲解:
// 指针版
void Swap(int* num1, int* num2)
{
int tmp = *num1;
*num1 = *num2;
*num2 = tmp;
}
// 引用版
void Swap(int& num1, int& num2)
{
int tmp = num1;
num1 = num2;
num2 = tmp;
}
void Swap(double& num1, double& num2)
{
double tmp = num1;
num1 = num2;
num2 = tmp;
}
int main()
{
int num1 = 3;
int num2 = 6;
double _num1 = 1.1; // 变量就不支持重名了哦
double _num2 = 2.2;
cout << "交换前" << endl;
cout << num1 << " " << num2 << endl;
cout << _num1 << " " << _num2 << endl;
//Swap(&num1, &num2);
//Swap(&_num1, &_num2);
Swap(num1, num2);
Swap(_num1, _num2);
cout << "交换后" << endl;
cout << num1 << " " << num2 << endl;
cout << _num1 << " " << _num2 << endl;
return 0;
}
用了引用之后是不是清爽了许多,有人可能会问:“&用来做引用了,那哪个符号是取地址呢?”C++是向前兼容C语言的,你要相信C++之父,&在C++中有了多重意思,就像函数能重载一样,如果是 类型名&,如int&、double&、自定义类型& 等,这里的&就是引用的意思;而&变量,这里的&还是取地址的功能。
阅读以下代码,猜猜输出多少:
A. -1 1 B. 0 0
int a = 0;
int& x = a;
a--;
x++;
cout << a << " " << x << endl;
恭喜蒙B的同学答对了!
笔者称引用是指针的兄弟是因为,在底层的汇编指令中,引用和指针的指令是一样的,只不过在语法上引用用起来是更方便的,不需要刻意传址传参,并在想修改实参时,不需要额外*解引用形参,直接对形参做对应操作即可,还有就是引用不用额外地开辟空间,相对于指针而言能节省一部分的空间。
int& x = a的意思就像是给a取了一个外号叫x,既然是外号,那a和x就是指同一个变量,就像鲁迅和周树人是同一个人一样,通过取地址可以发现,a和x的地址是一样的,即a和x管理着同一块空间,那么x改变a也会跟着改变,反之a变x也会变,这跟C语言中的联合体有点像,区别就是引用时,要求二者的类型要相同。
sizeof(外号)和被引用对象的大小是一样的,而不是和地址大小有关,这也就验证了外号和指针变量是不同的。
需要注意的是引用时必须要有对象,并且外号在给定某一个对象后,就不允许把这个外号给别人了,从用法上也更改不了,就像这样是行不通的:
int a = 0;
int b = 0;
int& x = a; // 给a取外号叫x
int& x = b; // 突然想把外号给b了
// 在计算机的世界里,外号x就是个变量,是变量就不允许多次定义
// 这就是引用较指针而言不同的地方,指针变量可以通过赋值修改指向对象
有同学可能会说我这样行不行:
int a = 0;
int& x; // 先定义一个外号,但不初始化
x = a; // 给a取外号x
int b = 0;
x = b; // 再给b取外号x
答案是不行的,语法要求引用时必须要初始化,不能单单定义外号但不给确定的人或对象,引用但不给具体的引用对象,就像调用函数没给参数一样,假设&是一个函数,int& x = a,就相当于int& x = &(a);我想表达的意思是引用时如果没有外号x是可以的,但没有a就是不行的了,因为a才是那个被引用的对象。
引用传参
void Swap(int& num1, int& num2)
{
int tmp = num1;
num1 = num2;
num2 = tmp;
}
// 调用时
Swap(num1, num2); // 不取地址真能完成交换?
这里不取地址传参也行的原因是函数参数用了引用&,传参的效果就相当于:int& num1(形参) = num1(实参),这样一来,实参和形参就是指同一个对象了,在背调函数体内改变形参,就相当于改变实参,于是就可以完成交换效果。
引用返回值
如果一个要返回的对象在出了被调函数后,生命周期没有结束,此时就可以考虑用引用该对象进行返回,格式如下:
int& Add(int& num1, int num2) //
{
num1 += num2;
return num1; // 不直接写return num1 + num2;是因为此时是引用返回,引用返回要有具体的对象且该对象在出了函数域后仍然存在
}
int main()
{
int a = 1;
int b = 2;
int& c = Add(a, b);
cout << a << endl;
c -= b;
cout << a << endl;
return 0;
}
由于还没学类和对象,这里还不好讲合适的应用场景,后续写运算符重载、类中的赋值重载等类似的东西时,引用返回的作用就很明显了,目前来说引用返回的作用就是能减少拷贝带来的花销。
总结
一个词有多个解释就构成了这个词的重载,本篇文章就讲了重载在函数中的应用,其实运算符也能重载,比如自定义结构体本没法进行加减乘除等运算,我们可以重载内置运算符的使用来达到,后续会总结,另外注意函数重载的三个条件,返回类型不同不构成重载哦。
引用就是给被引用对象取外号,然后直接用外号就能影响该对象,引用和指针是俩兄弟,没有什么……引用能完全替代指针的说法,因为二者各有特点优势,一个指针变量随时可以修改指向的对象,而外号只能和一个对象从始至终绑定在一起,这样也让引用用起来比指针更安全,不会出现访问野指针的风险,在具体使用场景中选合适即可;引用在目前的作用就是减少拷贝带来的花销,这和指针能带来的效果差不多,既然C++之父要可以设计引用,就说明它的作用不止于此,后续笔记中还会出现引用的,到时便能体会引用的好处。