学做淘宝店的网站优秀企业网站模板
好的,我们继续,这是 C++专栏的第二篇博客,还没读过上一篇博客可以进入我创建的专栏阅读
入门C++(上)再回来哦~
下面我们要讲的第一个概念就是函数重载
函数重载
1. 函数重载概念
什么是函数重载? 简单来说,函数重载就是一词多义 下面给出它的详细定义
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题
注意:不同作用域可以随便同名
同一作用域要同名,则需要满足重载的规则
下面我们来看一下几个构成函数重载的例子:
1.参数个数不同
2.参数类型不同
3.参数类型顺序不同
然后我们再来看一下下面的代码:
为什么这里会报错?
可以肯定的是这是不需要重载关系也是ok的(因为不同作用域可以随便同名)
但是在这里发生了调用歧义 所以才会报错~
再看一个例子:
这里的函数虽然构成重载,但是它们的调用会有歧义
我们再看一个例子:
这里我们可以得出:返回值不同是无法构成重载的
因为返回值不同,无法区分,所以返回值不是重载的条件
综上所属,调用里面中有歧义的都是不行的
最后,关于函数的重载,我们需要记住,它一定是和形参的类型是有关系的
还有就是将调用歧义和函数重载 区分开来
2.C++支持函数重载的原理
所以为什么C++支持函数重载,而C语言不支持函数重载呢?
在这里简单介绍一下:
对于C语言不支持函数重载我们是可以很好理解的,这与之前我们在C语言中讲过的编译链接过程有关 :一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接
链接时:
1.C语言是直接用函数名字去查找函数的地址的,所以不支持函数重载
2.在C++中,是用修饰后的函数名字去查找的,只要参数不同,修饰出来的名字就不一样,所以就支持函数重载
但是在如果我们在调用时不指定返回值类型,那么我们并不知道调函数的哪个返回值,就是调用时有了歧义,无法区分
所以对于C++的函数重载而言,函数返回值不同并不能构成函数重载,因为这不是函数名修饰规则
这也印证了我们在之前说的函数重载与 参数个数 或 类型 或 类型顺序 三者有关
接下来我们来看一下引用
引用
1. 引用概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间
引用的形式: 类型 & 引用变量名 = 引用实体
举个例子:
注意:引用类型必须和引用实体是同种类型的
2. 引用特性
1.引用在定义时必须初始化
2.一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
对于第一个特性,其实很好理解,我们看下面的这行代码
int& a; // 该条语句编译时会出错
对于这里的第二个特性,可以看下面的代码:
我们发现,这里的a,b,c,d都是同一个东西
然后是第三个特性,我们看下面的代码:
int main()
{int x = 0;int& y = x;int z = 1;y = z;return 0;
}
这里是 y变成z的别名呢? 还是z赋值给y?
根据引用的第三个特性:引用一旦引用一个实体,再不能引用其他实体
所以这里的代码y = z 意思是将 z赋值给y
我们可以验证一下:
3. 常引用
我们来看一下下面的代码:
const int a = 10;int& ra = a;
该语句编译时会出错,
因为a经过const修饰后是只读的
而ra变成a的别名,ra的权限是可读可写的
这是一种权限的放大,即原来 只是可读的变成的 可读可写的 所以不可以
总之,这里我们需要知道,权限不能放大
再看下面的代码:
const int* p1 = &m;
int* p2 = p1;
该语句编译时也会出错,这也是权限的放大
因为 const修饰是的*p1 , 所以 p1可以修改 *p1不可以
而int* p2 = p1 ,如果我们改变*p2,那么*p1就也会被修改,所以不可以
对应的,我们可以将 int* p2 = p1 修改为 const int& p2 = p1 这样就是权限的平移,是可以的
再看一个代码:
int* p1 = &x;const int* p2 = p1;
这是可以的,
p1是可读可写的,p2只可读
这是一种权限的缩小,即原来可读可写的变成了只可读 所以可以
这里我们可以知道,权限可以缩小
看下面代码:
double d = 12.34;
int& rd = d;
int& rd = d; 该语句编译时会出错,这是为什么呢?仅仅因为类型不同吗?
可是如果我换成 const int& rd = d 就不会报错,这又是为什么呢?
这里其实会进行一个类型的转换,而类型转换又会生成临时变量
知道了这点,我们就可以分析其中的原因了,int& rd = d 这里要将d给rd,这里是要类型转换的,而类型转换又需要临时变量,所以其实rd引用的不是d,而是那个临时变量
我们还知道,临时变量具有常性(相当与const修饰),所以这里的int& rd = d存在权限的放大
而我们将nt& rd = d 改成 const int& rd = d ,就不会是权限的放大,而是权限的平移,所以可以
总结一下,权限可以缩小,可以平移,但绝不可以放大!
4.使用场景
做参数
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
这里我们用引用的话就不用像C语言那样去传指针了,就会变得更加的方便~
而关于做返回值的使用场景,我们暂且不讲,留到以后讲类和对象时再说~
5. 传值、传引用效率比较
下面我们用一段代码看一下传值和传引用效率上的区别:
#include <time.h>
struct A{ int a[10000]; };void TestFunc1(A a){}
void TestFunc2(A& a){}void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc1(a);
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc2(a);
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}
(Tips:第二个函数运行的时间为0是取整后的结果,表示运行时间不超过1ms)
通过上述代码的比较,我们由此发现传值和指针在作为传参上效率相差很大
我们也能感知到引用传参的一个极大的好处
6.引用和指针的区别
讲到这里,引用的知识差不多就介绍完了,最后我们来简单的对比一下:
引用和指针的不同点:
1. 在语法上,我们认为引用定义一个变量的别名,指针存储一个变量地址
所以在语法层面上,我们认为引用不开空间(比如:int & b = a;),而指针是要开空间的
(比如: int * p = &a;) (但是在底层,其实它们是一样的,都是指针)
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
4. 没有NULL引用,但有NULL指针(但这个也不是绝对的,比如说 int* ptr = NULL;
int& r = *ptr; 这里看起来对空指针解引用,但是不会报错,因为在底层其实只是存ptr的地址,没有解引用,不过如果我们接下来要访问 :cout<< r<<endl; 才会解引用)
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节,64位平台下占8个字节)
比如我们看下面的代码及运行结果:
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用 是编译器自己处理(因为引用底层也是指针,但是它解引用不需要显式处理)
9. 引用比指针使用起来相对更安全(所以在C++我们更倾向用引用)
结语
好啦,这篇C++入门中篇的博客就先写到这里吧 我们介绍了C语言没有的两个新的概念:函数重载和引用,希望大家能有所收获~
如果文中分析,题解代码有不足的地方欢迎大家在评论区讨论和指正
接下来我也会持续更新与C++相关内容的博客
让我们在接下来的时间里一起学习,一起进步吧~