新手C++之函数重载 引用 内联函数

函数重载 在c语言中 是不允许同名函数存在的

但是c++支持 1. 要求定义在同一个作用域中

2.形参不同(个数,类型 ,类型顺序)

分别定义在两个命名空间的不叫函数重载 因为在两个命名空间的属于不同作用域 违反了定义要求在同一作用域中 就算两个命名空间展开也不算函数重载  依旧作用域不同 展开后会产生歧义(也就是命名空间展开以后 已经不在同一个域中)

要注意的是返回值不同于函数重载无关  :

这两种弄情况 选择不接收返回值调用 这是就无法分别调用哪个。

函数重载与函数缺省无关 此时报错是因为发生了调用歧义:

函数重载为什么c语言不支持 c++支持?

首先要知道预处理过程 要从编码方面入手回答这个问题:

在函数调用时 转换成机械码 会生成call 函数地址和jump这个函数地址  函数地址指的是函数定义转换成机械码后的那一堆汇编指令的第一句指令的地址 ( 就会类型与数组一样 数组名就指向数组的第一个成员地址   )

在编译开始时 函数调用和函数声明是匹配的 可以进行编译

举个例子就好比 要买房子或者车子 但是存款不够 要向朋友借钱 而朋友给的口头答复会接给你 这就是声明 而定义就好比朋友要借你的钱 只不过此时朋友只是口头答应还没有给你钱 也就是只有声明 没有定义

在上图编译中在.i .s .o 文件中都是没有函数定义的 只存在函数声明 原因是编译开始时 .h头文件和.cpp源文件是分开编译的 在没有链接之前 .cpp的源文件的机械码中只有只有声明 没有定义 这是是不会通过call和jump跳到 函数定义的 只有链接后 才会存在函数定义  若在链接后只有函数声明 而没有找到函数定义 这时产生无法解析的外部符号错误 这种错误是链接错误  (编译错误是指语法出现一些问题)

而在c语言中 通过函数声明直接去查找函数定义时 函数地址是不经过修饰的 所以同名的函数去调用是会产生歧义的 而c++去通过函数地址寻找时 同名不同形参的函数是会经过修饰 形参不同 修饰也不同 这样就可以支持函数重载 (这一部分重在理解道理 并不一定非要理解机械码和编码的原理) 而且不同编译器对于修饰函数地址的方式是不同的

这里的编译器上 形参的int类型是通过H修饰的 而char类型是通过D修饰的

引用

又被称作取别名

格式 :int a = 0; int &b= a; int &c = a; int &d = b;

这时b c d都是a的别名 a b c d 共用一块空间  一个比较有趣的例子 在水浒传中的李逵 被人称作铁牛 我们再给铁牛 取个外号叫黑旋风 这几个外号都是在说李逵的

引用的应用 用于代替传址 我们在c语言中想通过函数且不返回值改变一个值时传值是无法实现的 c中我们通常通过传地址 而在c++中我们可以通过引用定义函数的形参 也可以达到c语言传地址的效果 引用做参数可以提高相较于传值可以提高运行效率 传值需要拷贝一份作为参数 而引用则是直接使用  这个对于实参的体积越大 对比越明显  传值传参是会产生消耗的

例子1:

            

例子2:

引用还可以给指针取别名

这是pr就是p1 pr为NULL p1也会为NULL

例子:

上图可以看到通过引用给一级指针参数取别名 可以起到代替二级指针的作用

 例子:

这是对typedef的一种应用  typedef struct listnode{}LTNode, *PNode中   PNode相当于已经是一级指针的结构体类型了 而PNode &phead 相当于给一级指针取别名 代替二级指针

引用还可以做返回值

引用特性

1. 引用在定义时必须初始化

2. 一个变量可以有多个引用

3. 引用一旦引用一个实体,再不能引用其他实体 (引用的类型必须与实体是同种类型)

y是x的别名 z赋值给y(也就相当于赋值给x)

存在int&&m =move(y);目前只说存在 不说其他

引用也可以给函数取别名 

这里是应为m 作为常量只有可读没有可写的权限 而n作为别名具有可读可写的权限 也就是进行了权限的放大 是不可取的 权限只能平级或者缩小 不能放大

const int m = 0;

const int & n = m; 这里是权限的平级是可以的 

int m = 0;

const int &n = m;权限的缩小是不可以的

这里y修改 是会影响z的 因为它只是说不能修改z 并没有设定不能修改y

const int x = 0;int p = x ;这里是不会报错的 这不是权限的放大 是通过x的拷贝给p p的修改不会影响到x的 

const 修饰的是*p1 也就是m的地址 p1是可以修改的 *p1无法修改 如果将p1赋值给p2 此时*p2是可以修改的 相当于权限的放大  是会报错的

 

这是一个权限的缩小 是不会报错的

临时变量具有常量性

 double d = 12.34; int x = d;  int & y = x; 这串代码是会报错的 原因是通过d赋值给x 发生类型转换 而类型转换过程中d 产生的临时变量会赋值给x  而临时变量具有常量性 使得x具有常量性 这是用y引用x 明显是属于权限的放大 是会报错的

int x = 0; int y = 1; int &z = x+y; 这也是会发生报错的 原因是运算过程中也是会产生临时变量 这是取别名属于权限的放大

引用和指针的不同点 

1.使用引用取别名是不需要开空间的 int a= 0; int &b = a;

用指针指向一个变量的地址是需要开空间的 int * a = &b;

但其实在c++中 从汇编的角度看 无论是指针还是引用 都是要开辟空间的

int *ptr =NULL;

int &r =*ptr; 在这里是不会报错的 因为此时*ptr还并未解引用

cout << r<< endl;这里就会报错 因为这是ptr解引用了 而引用的实体是不能为空的

2.引用在定义时必须初始化 而指针是可不初始化的

3.引用初始化后是不能再进行修改的 而指针是可以修改的

4.类型所占大小不同 引用为引用实体类型的大小 而指针始终是地址空间所占字节个数(32位下占4个字节)

int *b=NULL;

cout << sizeof(b)<<endl;

cout << sizeof(int &)<<endl;

5.引用自加1即是引用的实体加1 而指针自加1则是指针向后偏移一个类型的大小

6.有多级指针 却无多级引用

7.方位实体的方式不同 指针通过解引用 而引用是编译器自己处理

引用比指针更安全但也不能完全替代

比如在链表删除中 引用不能改变链表中成员指向

内联函数

 频繁调用小函数每次都是会付出一定的代价的

在C语言中 是通过定义宏函数解决的

比如加法add 

#define add(a,b)((a)+(b))

几种错误写法(1.#define add(a,b)a+b;  #define add(a,b)(a+b))在正确写法中为什么要加外括号 为什么加内括号 为什么不加分号 

 add(a,b)* 2 这时如果没有外括号就会成为 a+b*2 这样就b首先*2 会得出错误的结果

add(a|2,b&2)这时如果没有内括号就会变成a|2+b&2 其中+的优先级是高于| 和& 这时也会得到错误的结果

cout << add(a,b)<<endl; 这是如果在定义宏时加上 ; 就会变成 cout << add(a,b);<<endl; 产生语法错误

c++ inline   使用内联函数后的 机械码会在call 函数地址处直接展开函数定义 而不会去jump跳跃寻找函数定义 这样就会减少损耗  而在debug版本下 机械码无论是否是内联函数都是默认不展开的 想要查看机械码 需要自行调整编译器

这样调整编译器就可以在debug版本下机械码inline函数展开了

内联函数特性

1.若函数很大时 即使内联也不会展开

2.以空间换时间 会将目标文件变大 

3.inline只是编译器的一个建议 编译器可接收也可不接收 

4.内联函数不能定义和声明分离 这样不便管控

宏的特性

优点 增强代码复用性 提高性能

缺点 不方便调试 (在预编译阶段就会发生替换)可读性 可维护性差 易误用 没有类型安全的检查

有哪些可以替代宏 

1.用常量定义函数 const enum 2. 短小函数定义 换用内联函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值