目录
一、函数重载
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。(如下就是几个实现重载功能的函数)
int Add(int left,int right)
{
return left+right;
}
double Add(double left, double right)
{
return left+right;
}
long Add(long left, long right)
{
return left+right;
}
int main()
{
Add(10, 20);
Add(10.0, 20.0);
Add(10L, 20L);
return 0;
}
那么问题来了:
为什么C语言不支持函数重载,而C++支持?
首先,他们实现程序的方式都是 预处理 - 编译 - 汇编 - 链接这几个步骤,首先来回顾这几个步骤。
编译四步骤
1.预处理:
头文件展开(比如有#include,展开iostream库,将他所在的文件拷贝到当前文件)/宏替换/去掉注释(文字注释等)/条件编译(ifdef__win32,跨平台问题等)生成 .i文件
2.编译:
检查语法,生成汇编代码(指令级代码,在驱动,嵌入式开发等),生成.s文件
在此处可以看到汇编代码
3.汇编:
生成指令
在汇编生成指令后,最后将这些指令交给cpu来执行,但是cpu不认识,所以进行汇编,将汇编代码生成二进制机器码 (01011),这个过程会产生符号表(每个全局变量和函数会形成一个符号,这个规程就是将每一个符号
结合生成一个符号表,一个符号是一个标识,此过程还会给符号一个地址,最终将所有符号及其地址组合,形成符号表),最终生成.o文件
4.链接:
将上述三步产生的三个文件进行合并,合并符号表,合并段表,.o文件的结合,形成段表(所谓短表,即每个.c/.cpp文件在经过编译都会生成一个自己的段),生成可执行程序(.out/.exe等)
函数重载真相
而C++能实现函数重载,主要是因为它在编译时与C语言不同。
这边首先提到函数名修饰规则 :
编译器在编译期间创建的一个字符串(也叫符号表名),用来指明函数的定义或原型。用于在汇编时找到这个函数。
C语言编译:
无函数名修饰规则,用函数名充当符号表名,直接根据符号表去找,自己区分不开,所以不支持函数重载
C++编译:
根据函数名修饰规则,来找地址(vs情况),将类型名带入函数名,用修饰以后的函数名充当符号表名,所以支持重载
如存在如下函数:
int func(int i,double d)
{
//...
}
int func(double d, int i)
{
//...
}
我们通过gcc编译器到达编译这一步,会看到两个函数的符号表名:
(gcc内,符号表名的编译规则是Z+函数名长度+类型首字母
)
根据上述特性,我们也可以得出一个结论:函数返回类型不同,并不会构成函数重载.因为函数返回类型并未在符号表中有体现。
二、引用(&)
所谓引用,就是给已经存在的变量起一个别名。
(正如鲁迅,也叫周树人,哈哈哈哈~)
换句话说,引用=取别名,用法就是类型& 变量名
如:
int a = 10;
int& b = a;
那么这边的a和b都是指向着由a开辟的那块空间
普通场景下没什么意义,但是在某些场景下,在输出型参数时,就很需要。比如,我们的C语言交换函数。
旧写法:
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
新写法:
void swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
两种在运行后的结果都是一样的。
*引用满足的条件
那么需要注意的是,引用需要满足以下几个条件:
1.引用初始化时要给值。
int &a;//这是错误写法
int& a = b;//这才是正确的
2.一个变量可以有多个引用。
int a = 10;
int& b = a;
int& c = a;
a,b,c指向的是同一块空间所表示的值。
3.引用一旦引用一个实体,再不能引用其他实体
这句换很好理解,引用其他实体,也就意味着重定义,如:
int a = 10;
int b = 20;
int& c = a;
int& c = b;//这里的c引用了其他实体,换个角度就是重定义了,这本身就是不满足语法要求的
4.可以给别名取别名
以下写法是合法的:
int a = 10;
int& b = a;
int& c = b;
注意事项
现有如下代码:
int a = 10;
int& b = a;
int& c = b;
int x = 1;
b = x;
这串代码在运行后,a,b,c的值会被x赋值为1,但是a,b,c是同一个地址,x的地址与三者不同。
它的引用赋值不会改变指向 (Java会改变指向)
*引用做返回值
函数返回值可以是类型,也可以是指针。
如普通函数写法:
int Add(int x, int y);
Node* ListNode(LT* plist);
但是引用同样可以做函数返回值,不过引用做返回值必须满足:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回
当函数在引用返回时,这个值必须是全局/堆/静态的对象,才可以使用引用返回。如以下代码就是不合法:
int& Add(int x, int y)
{
int c = x + y;
return c;
}
int main()
{
int ret = Add(1, 2);
return 0;
}
在上述代码中,c是一个局部变量,当他以引用的方式被返回时,内存空间已经被清理,那么是否返回理想值主要是看是否被栈空间清理。实际上这一行为是未定义行为,这串代码本身就有问题,这里是一个错误示范
合法写法如下:
int a = 10;
int& Add(int x, int y)
{
a = x + y;
return a;
}
int main()
{
int x = 20;
int y = 20;
int ret = Add(x, y);
cout << ret << endl;
return 0;
}
上述引用做返回值写法是合法写法,返回值是局部变量,不会有错误。
当返回值在静态区,栈,全局变量时,就可以用引用做返回值这样的写法去写
欢迎一起交流~~