hello,各位小伙伴,本篇文章跟大家一起学习C++:函数重载和引用,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !
文章目录
- 函数重载
- 1.函数重载的概念
- 为什么C++支持函数重载
- 引用
- 引用的概念
- 引用特性
- 引用的使用场景
- 1.做参数
- 2.做返回值
- 传值、传引用效率比较
- 引用和指针的区别
函数重载
1.函数重载的概念
函数重载:是函数的一种特殊情况,C++允许在
同一作用域
中声明几个功能类似的同名函数
,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)
不同,常用来处理实现功能类似数据类型不同的问题。
要注意是同一作用域
,不同作用域可以同名,但是不属于函数重载
1.参数个数不同:
void f(int a)
{
printf("%d\n",a);
}
void f(int a,int b)
{
printf("%d %d\n",a,b);
}
2.参数类型不同:
void f(int a,int b)
{
printf("%d %d\n",a,b);
}
void f(double a,double b)
{
printf("%lf %lf\n",a,b);
}
3.参数类型顺序不同:
void f(int a,char b)
{
printf("%d %c\n",a,b);
}
void f(char b,int a)
{
printf("%d %c\n",a,b);
}
在函数重载时要注意可能会发生分歧,例如:
#include<iostream>
using namespace std;
void f(int a)
{
cout<<a<<endl;
}
void f(char a)
{
cout<<a<<endl;
}
int main()
{
int a = 1;
f(a);
return 0;
}
上述代码会报错,因为编译器不知道要调用哪一个f()
函数,在传参时a
可以隐式转换为char
类型,这样就发生了分歧。
为什么C++支持函数重载
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
举个例子,a.cpp调用b.cpp中的Add()函数,那么链接器看到a.o调用Add()函数,但没有地址时,就会在去b.o寻找Add()的地址,然后链接在一起。
a.o、b.o分别为a.cpp、b.cpp预处理、编译、汇编后得到的文件
那么链接器怎么去找呢?
C++会使用已经修饰
过的了Add()函数去寻找,怎么修饰与编译器有关,而C语言会直接使用Add这个函数名
去寻找,那么两个一模一样的函数名,自然会发生分歧,使得编译器不知道走哪一个。
总的来说:C++在链接时,会使用已经修饰
过的函数名去寻找地址,函数参数个数不同、参数类型不同、参数类型顺序不同,修饰后的函数名自然不同,从而不会发生分歧。
!!!知道了为什么支持函数重载后,还有一点细节:返回值不同,构成函数重载吗?
答案是:不支持
如果两个函数函数名和参数是一样的,返回值不同是
不构成
重载的,因为调用时编译器没办
法区分。
引用
引用的概念
引用
不是新定义一个变量,而是给已存在变量取了一个别名
,编译器不会为引用变量开辟内存空间,它和它引用的变量共用
同一块内存空间。
举个例子:陈明的别名是小明,但是小明就是陈明,指的是同一个人。
使用:类型& 引用变量名(对象名) = 引用实体;
例子:
int main()
{
int a = 1;
int& b = a;
// int & b = a;
//(引用类型) & (引用变量) = (引用实体)
b = 3;
cout<<a<<endl;
printf("%p\n",&a);//表达式1
printf("%p\n",&b);//表达式2
return 0;
}
猜猜a的值为多少?没错,答案是3。
前面讲到,它和它引用的变量共用
同一块内存空间,所以b
指的就是a
,b
的值改变,那么a
当然会变,所以表达式1和表达式2打印的结果一样,都是同一个地址。
注意:引用类型
必须和引用实体
是同种类型的
相信小伙伴们就能很容易理解,那么细节来了:
int main()
{
const int a = 1;
int& b = a;
return 0;
}
这么些是否正确呢?相信聪明的小伙伴们一眼就看出答案:这么些是错误的。
const
修饰a
,那么a
是不能改变的,而b
并没有const
修饰,所以b
可以改变,也就是权限被放大了,本来a
自身都不能改变,凭什么b
可以。
再来:
int main()
{
int a = 1;
const int& b = a;
return 0;
}
这么写b
不能改变,a
可以,权限被缩小,是可以的。
继续:
int main()
{
int a = 1;
const int* pa = &a;
int*& b = pa;
return 0;
}
很明显,这么写是错的,权限被放大了。
那么这个:
int main()
{
int a = 1;
int* const pa = &a;
int*& b = pa;
return 0;
}
这么写是正确的。
这两个声明之间存在重要的区别,这涉及到C++中指针和const关键字的使用。
-
int* const pa = &a;
:- 这里
pa
是一个指针,它指向一个整数(int*
)。 - 关键字
const
出现在*
前面,表示指针本身是常量,即指针一旦指向了某个地址之后,就不能再指向其他地址。 - 也就是说,
pa
是一个常量指针,它的值(即指向的地址)不可以被改变,但是它所指向的值可以被修改。
- 这里
-
const int* pa = &a;
:- 这里
pa
是一个指向常量整数的指针。 - 关键字
const
出现在int
前面,表示指针所指向的值是常量,即不能通过pa
修改所指向的值。 - 也就是说,
pa
是一个指针,它可以指向任意整数,但通过pa
只能读取所指向的值,而不能修改。
- 这里
总结来说,int* const pa = &a;
表示 pa
是一个指针常量,而 const int* pa = &a;
表示 pa
是一个指向常量整数的指针。
引用特性
- 引用在
定义时必须初始化
(你总不能想到一个别名先放着,看到谁就给谁用吧)
int& a;//这么些是错的
- 一个变量可以有多个引用
int a = 1;
int& b = a;
int& c = a;
int& d = a;
- 引用一旦引用一个实体,再不能引用其他实体
int a = 1;
int& b = a;
int c = 1;
b = c;//这么些是错的
引用的使用场景
1.做参数
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1;
int b = 2;
int& c = a;
int& d = b;
Swap(c,d);
return 0;
}
2.做返回值
int& Test1()
{
int n = 0;
n++;
return n;
}
int main()
{
int a = 0;
int& b = a;
b = Test1();
cout << a;
return 0;
}
传值、传引用效率比较
我们知道:
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
所以,传引用的效率比传值的效率更高,引用作为返回值比值作为返回值效率更高。
引用和指针的区别
在语法概念上,引用是没有独立空间的,他就是引用实体的另一个名字,和其引用实体共用同一块空间。
int main()
{
int a = 10;
int& ra = a;
cout<<"&a = "<<&a<<endl;
cout<<"&ra = "<<&ra<<endl;
return 0;
}
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main()
{
int a = 10;
int& ra = a;
ra = 20;
int* pa = &a;
*pa = 20;
return 0;
}
我们来看其汇编代码对比指针和引用
可以看出操作上是一样的。
引用和指针的不同点:
- 引用概念上定义一个变量的别名,指针存储一个变量地址。
- 引用在定义时必须
初始化
,指针没有要求 - 引用在初始化时引用一个实体后,就
不能再引用其他实体
,而指针可以在任何时候指向任何
一个同类型实体 没有NULL引用
,但有NULL指针- 在sizeof中含义不同:引用结果为
引用类型的大小
,但指针始终是地址空间所占字节个数(32
位平台下占4个字节) - 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是
没有多级引用
- 访问实体方式不同,
指针需要显式解引用,引用编译器自己处理
引用比指针使用起来相对更安全
好啦,这篇文章就到此结束了
所以你学会了吗?
好啦,本章对于C++:函数重载和引用的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!
如你喜欢,点点赞就是对我的支持,感谢感谢!!!