《C++基础进阶:函数重载、引用、inline与nullptr全解析》

gitee地址:https://gitee.com/yyyzzzzccc/

目录

1.1参数类型不同

1.2参数个数不同

1.3参数类型的顺序不同

2、不能构成函数重载的情况(易错点)

[2.1 仅返回值不同](#2.1 仅返回值不同)

[2.2 仅参数名不同](#2.2 仅参数名不同)

3、引用

3.1引用的特点

1.引用的对象必须初始化

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

3.引用一个变量,不能在引用其他的变量

3.2引用的使用

1.做函数形参的改变

2.做函数返回值

3.简化代码,当作别名

4、指针和引用的关系(面试题)

5、const引用

6、inline

6.1为啥要用inline???

6.3inline的使用边界(啥时候用/不用)

7nullptr


C++⽀持在同⼀作⽤域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。

1.1参数类型不同

这里我们可以看到我们写了两个同名函数的定义,而我们在调用的时候,编译器会根据你传入的实参,去匹配参数列表最匹配的那个重载版本来执行。

1.2参数个数不同

在这里我们可以看到我们写了两个一样的函数定义,一个不传参数,一个传入参数,编译器会根据参数的个数来匹配相应的函数重载,上面我们写了coke()和coke(2),它们分别匹配了第一个和第二个函数重载。

1.3参数类型的顺序不同

在这里我们可以看到我们把参数的顺序变了一下,让后执行调用两个函数,第一个fou('k',2)匹配了第二个同名函数,第二个执行fou(2,'k')匹配的是第一个同名函数,从而达到函数重载。

2、不能构成函数重载的情况(易错点)

2.1 仅返回值不同

这种情况下,当我们调用同名函数时,参数个数和参数类型完全相同,编译器不知道调用那个同名函数,所以这种情况下不构成函数重载。

2.2 仅参数名不同

这里我们在写同名函数时,我们一个写了int a,一个写了int b,但是这并不影响参数的值,所以这不构成重载。

**函数重载的应用:

  1. 定义:同一作用域内,函数名相同,参数列表(类型/个数/顺序)不同的函数。
  2. 核心作用:统一接口、简化调用,用同一个函数名适配不同参数场景,提升代码可读性与扩展性。
  3. 匹配规则:编译器优先找完全匹配的参数列表,其次类型提升匹配,不支持仅返回值不同的重载。
  4. 避坑点:参数类型不兼容会匹配失败(比如字符串 "a" 和 char 类型),重载逻辑需保持一致。**

3、引用

引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引⽤变量开辟内存空间,它和它引⽤的变量共⽤同⼀块内存空间。

⽐如:⽔壶传中李逵,宋江叫"铁⽜",江湖上⼈称"⿊旋⻛";林冲,外号豹⼦头;

类型& 引⽤别名 = 引⽤对象;

C++中为了避免引⼊太多的运算符,会复⽤C语⾔的⼀些符号,⽐如前⾯的<< 和 >>,这⾥引⽤也和取地址使⽤了同⼀个符号&,⼤家注意使⽤⽅法⻆度区分就可以。(吐槽⼀下,这个问题其实挺坑的,个⼈觉得⽤更多符号反⽽更好,不容易混淆)

我们可以看出,我们给a起了3个别名,分别是b,c,d它们本质上还是a,我们也可以把它的三个别名的地址都打印出来,我们可以看到,3个别名的地址和a本身一样,这意味着引用不额外创建空间。

3.1引用的特点

1.引用的对象必须初始化

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

3.引用一个变量,不能在引用其他的变量

当我们定义一个别名与一个实体变量a绑定之后就不能再引用其他实体变量了,这是规定,大家记者就行了。

3.2引用的使用

1.做函数形参的改变

这里我们可以看到,我们不用指针而用引用也可以交换两个值,相比较指针,引用它不开辟的内存空间。

2.做函数返回值

可返回变量的别名,支持链式调用,不能返回局部变量引用,在这里我们先不讲解,我们在后面的学习当中再来分析做函数返回值的这种情况。

3.简化代码,当作别名

给复杂变量起一个简短的别名,方便读写。

这里我们定义一个很长的变量,为了方便后面的读写,这里给这个长的变量起一个别名b

cpp 复制代码
int aaaaaaaaaaaa=10;
int& b=aaaaaaaaaaaa;

4、指针和引用的关系(面试题)

指针和引⽤的关系C++中指针和引⽤就像两个性格迥异的亲兄弟,指针是哥哥,引⽤是弟弟,在实践中他们相辅相成,功能有重叠性,但是各有⾃⼰的特点,互相不可替代。

• 语法概念上引⽤是⼀个变量的取别名不开空间,指针是存储⼀个变量地址,要开空间。

• 引⽤在定义时必须初始化,指针建议初始化,但是语法上不是必须的。

• 引⽤在初始化时引⽤⼀个对象后,就不能再引⽤其他对象;⽽指针可以在不断地改变指向对象。

(从下面的代码我们可以看出,指针本来指向的是a,让后又改变了指向,指向了b,此时我们打印地址,可以很清晰的看出指向发生了改变)

对于引用来说,则不能改变指向,此时会报错

• 引⽤可以直接访问指向对象,指针需要解引⽤才是访问指向对象。

• sizeof中含义不同,引⽤结果为引⽤类型的⼤⼩,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位下是8byte)

• 指针很容易出现空指针和野指针的问题,引⽤很少出现,引⽤使⽤起来相对更安全⼀些。

5、const引用

• 可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访问权限在引⽤过程中可以缩⼩,但是不能放⼤。

• 不需要注意的是类似 int& rb = a*3; double d = 12.34; int& rd = d; 这样⼀些场景下a*3的和结果保存在⼀个临时对象中, int& rd = d 也是类似,在类型转换中会产⽣临时对象存储中间值,也就是时,rb和rd引⽤的都是临时对象,⽽C++规定临时对象具有常性,所以这⾥就触发了权限放⼤,必须要⽤常引⽤才可以。

• 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象,C++中把这个未命名对象叫做临时对象。

基础定义与语法

cpp 复制代码
const 类型& 引用名=变量/常量/临时变量

示例:

特性 普通引用 const 引用

绑定对象 只能绑定左值(变量) 可以绑定左值、常量、临时对象

可修改性 可以通过引用修改原变量 只能读取,无法修改原变量

拷贝开销 无拷贝,直接绑定原变量 无拷贝,直接绑定原变量

关键区别:接收临时对象/常量
普通引用无法绑定临时对象(比如表达式、常量),但 const 引用可以:

原理: const 引用会延长临时对象的生命周期,临时对象会在引用销毁时才释放。

**3. 最常用场景:函数形参
const 引用是函数传参的"黄金标准",尤其是传递大型对象(如 string 、自定义类)时:

  • 避免拷贝,提升效率(和普通引用一样)**

- 保证函数不会修改传入的参数,代码更安全

这里的printf3(str)是printf2(str)修改过的str,所以输出还是修改了。

1. const 引用不是"变量本身是 const"
引用的变量本身可以不是 const ,但通过 const 引用无法修改它:

cpp 复制代码
int a = 10;
const int& r = a;
a = 20; //  可以直接修改原变量
cout << r; // 输出 20,引用会同步变化

错点:

**这里的关键是:

  • a 本身是非 const 的普通变量,它的值本来就可以被修改。**

**- const int& r = a; 只是给 a 加了一层只读的"视图",通过 r 不能修改 a ,但 a 本身还是可以被直接修改的。
你可以这么理解:

  • a 是一本可以改写的笔记**

- const int& r 是别人借你笔记时,只能看、不能写的"只读权限"

- 你自己(原变量 a )还是可以直接改笔记内容,别人( r )看到的就是修改后的内容

2. 不能通过非 const 引用绑定 const 变量
否则会绕过 const 的只读保护,导致语法错误:

那什么时候 a 不能被修改?
只有当 a 本身是 const 常量时,才不能被修改:

cpp 复制代码
const int a = 10; // a 本身是只读常量
const int& r = a;  // 只能用 const 引用绑定

a = 20; //  错误!a 本身是 const,不能修改
r = 20; // 错误!const 引用也不能修改

**一句话总结这个规则

  • const int& r = a; 只是限制了通过 r 修改 a 的权限,不是把 a 本身变成了 const 。**

- 如果 a 本身是普通变量,你还是可以直接改 a , r 会同步看到变化。

cpp 复制代码
const int a = 10;
int& r = a; //  错误!会破坏 const 变量的只读性
const int& r = a; //  正确
  1. const 引用和右值引用的区别

这里的 const 引用不是 C++11 的右值引用 int&& ,它本质上还是绑定到左值/临时对象的只读别名。

6、inline

inline是C/C++的函数修饰符,写在函数定义前。作用是向编译器建议:把这个函数的代码,直接复制粘贴到每一处调用的位置,而非生成独立函数,走正常函数调用的过程。

下面我们来对比一下不加inline关键字修饰函数和用inline修饰函数定义的两种调用函数的过程:

1.不加inline的普通函数

单独存一份函数体,调用时:压栈->跳转执行->返回,有调用开销。

2.加inline函数(被成功内联)

调用处直接嵌入函数代码,没有跳转,没有压栈出栈。

注意:inline只是建议,编译器有权拒绝。若函数太长,有循环/递归/复杂的逻辑,会自动退化成普通函数。

6.1为啥要用inline???

最主要的目的就是消除函数调用时的开销,提升执行效率,普通的函数每次调用时都会压栈操作,跳转执行,而对于代码极短,调用频率少的函数,调用开销占比较大。而用了inline展开后,直接省略这部分的消耗,提高效率。

6.3inline的使用边界(啥时候用/不用)

适合用inline:函数体只有几行代码的极简函数,高频率调用的函数

不适合用inline:函数代码很长,函数包括循环,递归,多分支等复杂逻辑的函数。调用频率少的函数,因为调用频率少,普通调用的开销可以省略,没必要增加代码体积。

7nullptr

为啥在C++当中引用这个nullptr,是专门替代旧的NULL的,因为在C语言当中的NULL表示空指针,它的本质是宏,简单来说就是0;因为NULL是0,在传参时,会引发函数重载的歧义,举个例子:

这时在C++的环境下,编译的代码,我们可以看到,这里我们第一个的参数是0,是整数,毫无疑问就是执行第一个同名函数,而我们传的第二个参数是NULL,这个我们都认为应该是执行第二个同名函数,但是结果还是第一个,为啥呢?我们的本意就是传空指针,但还是跑到了整数里面。

因为NULL的本质是0,编译器做了隐式类型转换。为了解决这个问题,这里我们就要传nullptr,

什么是nullptr???

• C++中NULL可能被定义为字⾯常量0,或者C中被定义为⽆类型指针(void*)的常量。不论采取何种定义,在使⽤空值的指针时,都不可避免的会遇到⼀些⿇烦,本想通过f(NULL)调⽤指针版本的f(int*)函数,但是由于NULL被定义成0,调⽤了f(int x),因此与程序的初衷相悖。f((void*)NULL);调⽤会报错。

• C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型。

简单来说就是代替NULL,防止被隐式转换为0;本意还是空指针。使用如下:

这里就简单替换NULL就行了,很简单,此时的就不会被隐式转换为0了,就不会进入到整数当中,从而进入到指针当中。

相关推荐
雪度娃娃1 小时前
ASIO异步通信——服务器网络层和逻辑层设计
开发语言·网络·c++·php
Zhang~Ling1 小时前
C++ 多态完全指南:虚函数、重写、虚表与动态绑定深度解析
开发语言·c++
BestOrNothing_20151 小时前
C++零基础到工程实战(5.2.5):函数默认参数和函数重载
c++·函数重载·函数默认参数·nullptr·函数声明与定义
不负岁月无痕1 小时前
STL-- C++ list类 模拟实现
开发语言·c++·list
江屿风1 小时前
C++OJ题经验总结(竞赛)3
开发语言·c++·笔记·算法
NiceCloud喜云1 小时前
Anthropic 发布 Project Glasswing:未公开模型 Mythos 已挖出 10000+ 漏洞,含 OpenBSD 27 年老 bug
android·java·数据库·c++·python·docker·bug
香蕉鼠片2 小时前
八股C++(二)
开发语言·c++
格发许可优化管理系统2 小时前
解决Mentor许可冲突,让您的业务无缝运行
运维·服务器·c语言·c++·人工智能
思麟呀2 小时前
在C++基础上理解CSharp-4
开发语言·jvm·c++·c#