- 要想要调用
命名空间域
中的成员的话,我们需要用到域作用限定符(::
) - 简单来说:就是限定域作用限定符(
::
)后面的成员是来自前面空间名字对应的命名空间域 - 即一个命名空间就相当于定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中
➡️用法: 想要调用的命名空间域的空间名➕::
➕调用的命名空间域的成员
❓这里我们便可以思考一下如下代码经常出现在C++语言中的含义
using namespace std;
如上代码就代表着:
- cpp为了防止命名冲突,把自己库里面的东西都定义在一个叫
std
的命名空间中 - 而我们如果要调用std库里的成员,就有三种方式:
std::
➕要调用的成员 :此方式是最规范的写法using namespace std;
:此方式相当于把std
这个命名空间域里所有成员展开到全局域中
- 虽然方便日常的我们使用,但会使我们后续的命名可能会与标准库中的命名相冲突
- 所以在规范的工程项目中是不推荐这种方式的
using std::
➕要调用的成员:此方式就是介于前两种之间,可以用于对常用的成员进行展开,这样后续再调用这些成员就更加方便,也更加规范了
❗特别注意:
- 若单独
::➕要调用的成员
,这样子表示在全局域中寻找要调用的这个成员
🥯Ⅱ.总结
⭐综上:
- 命名空间中的内容,既可以定义变量,也可以定义函数
- 命名空间是可以嵌套的【相对应的:调用的时候也需要嵌套调用】
- 同一个工程中允许存在多个相同名称的命名空间,编译器最后会自动合成同一个命名空间中
🍞二.缺省参数
💡缺省参数:
- 缺省参数是声明或定义函数时为函数的参数指定一个默认值
- 在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参
👉示例:
1️⃣全缺省参数【即给函数的所有参数都给上缺省值】
👆从示例中不难发现:
- 我们对
MoonCake
函数的缺省值为0
- 若调用
MoonCake
函数的时候没有传参数,则函数会利用缺省参数的值(0
)充当参数,进而在函数内部被使用 - 若调用
MoonCake
函数的时候有传参数,则函数会利用实参的值赋值给形参(100
),进而在函数内部被使用
2️⃣半缺省参数【即给函数的部分参数给上缺省值】
👆从示例中不难发现:
- 我们对
MoonCake
函数的形参nums
没有给缺省值,形参size
给的缺省值为10
- 若调用
MoonCake
函数的时候没有传形参size
,则函数会利用其缺省参数的值(10
)充当形参size
参数,进而在函数内部被使用 - 若调用
MoonCake
函数的时候有传参数,则函数会利用实参的值赋值给形参(100
),进而在函数内部被使用
❗特别注意:
- 半缺省参数必须从右往左依次来给出,不能间隔着给【这是因为参数是从左往右传给形参的,若中间隔着给缺省值的话,程序便不知道哪个实参对应哪个形参(但实际形参入栈的时候:是从右往左入栈)】
- 缺省参数最好不要在函数声明和定义中同时出现【若两个位置提供的缺省值不同,会给编译器造成歧义不知道该用哪个缺省值】
- 缺省值必须是常量或者全局变量
🥯Ⅰ.总结
⭐综上: 缺省参数是C++中新添加的语法,使调用函数时变得更加灵活了
🍞三.函数重载
💡函数重载:
- 是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数
- 这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
👆简单来说:
- 即实现了允许可以同时有多个同名函数的存在,且同名函数可以用来解决不同的实际问题【即赋予了函数不同的意义】
- 本质是为了解决C语言中不允许同名函数问题而出现的
❗特别注意:
-
构成函数重载,必须满足(三者满足其一即可):
- 函数参数的个数不同
- 函数参数的类型不同
- 函数参数的顺序不同
👉示例:
👆从示例中不难发现:
-
我对写了两个名字同为
Add
的函数,它们之间构成函数重载(因为函数参数的类型不同) -
可以看出编译器会根据我们函数参数的类型去匹配相对应的函数进行调用
-
其中之所以第二个
Add
函数也输出2
,是因为其返回值的类型为int
,使其原本返回类型为double
的值被强制转换类型为int
- 这也侧面反映了:构成函数重载的因素只与
参数
有关,与返回值得类型
无关
- 这也侧面反映了:构成函数重载的因素只与
❓想必同学们都会产生如下例子中的问题:下列两个函数是否构成函数重载
⭐答案是:构成函数重载,但并不能通过编译
- 这是因为这种代码在执行的时候有可能产生歧义,导致编译器不知道该执行哪个函数,最终调用不明确,编译失败
🥯Ⅰ.面试真题
💡在面试中,会经常出现如上知识点的相关问题:
- C语言为什么不支持函数重载
- C++又是怎么支持函数重载的
👆以上问题,可以都归结于一个原因:
- 函数名修饰规则
➡️简单来说:
-
我们调用函数,在汇编时期本质是通过指令
call 函数实现的汇编指令的地址
的 -
而对于函数是声明和定义分开在不同的文件时,并不会一开始就找到函数实现的汇编指令的地址:
-
1️⃣而是因为有了
函数声明
,在编译阶段就让这个指令暂时通过(即地址处暂时空出),此时编译器会认为函数定义
在其它地方,后续链接
时再找函数定义的地址 -
2️⃣
链接
的时候:拿着函数名去找其函数实现的汇编指令的地址,具体是在其它文件的符号表中搜索地址- 只要找到就放入地址
- 找到不到就相当于
链接失败
-
-
⭐所以这也就为什么:
- C语言中不支持函数重载,是因为C语言中函数实现的汇编指令的地址是根据函数名称去匹配的,面对同名函数编译器链接的时候无法区分,所以链接失败
- 而C++支持函数重载,是因为函数名经过了
函数名修饰规则
【即现在的函数名不是直接拿原函数名作为名字,而是通过_Z+原函数名的长度+原函数名+函数参数的类型的首字母
的规则进行修饰,这也就为什么构成重载函数的三个要求都与函数参数
有关】,这样就可以同名函数从而链接到地址
🥯Ⅱ.总结
⭐综上: 正是有函数名修饰规则
的加持下,让C++相较于C语言上有了更加丰富的实现
🍞四.引用
💡引用:
- 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
👆简单来说:
- 就是对一个已有的变量起一个别名【类似于指针,只不过指针是指向变量的地址,但需要给指针这个变量开辟空间】
- 而引用本质还是自己,只是自己有了一个新的名字,但本体还是自己,所以对引用的修改会影响自身
❗特别注意:
- 引用类型必须和引用实体是同种类型的
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
👉示例:
👆从示例中不难发现:
- 我将变量
MoonCake
起了一个别名MoonCake1
- 通过打印地址也可以证明它们其实是在同一块空间,这也就证明了
引用
本质就相当于对引用对象的那块空间起多一个变量名字而已
🥯Ⅰ.权限问题
1️⃣权限放大问题
const int MoonCake = 10;
int& YueBing = MoonCake;
❓同学们觉得如上操作是正确的的吗
❗其实是错误的,这是因为:
MoonCake
这个变量被const
所修饰,变成常变量,拥有常属性,在这种情况下后续的操作中是不允许对MoonCake
这个变量的值进行修改的- 而如果用
int&
进行引用的话,那YueBing
这个别名就表示可以对MoonCake
本身就行读和写 - 即
const
修饰的变量只允许读,不能写,而现在别名
竟然可以对自身读和写,这样就属于对自身权限放大
问题
👆那我们该如何修改呢:只需要匹配权限即可
- 即在引用类型将
int&
改为const int&
即可权限相匹配
2️⃣权限缩小问题
int MoonCake = 10;
const int& YueBing = MoonCake;
❓同学们觉得如上操作是正确的的吗
❗是正确的,这是因为:
- 变量本体是允许读和写,而引用后这个
别名
的权限只有读
- 但这样并不会影响本体,因为
别名
的权限小于本体
✨综上:
- 只要引用后的
别名
权限<=
本体权限即可
🥯Ⅱ.常引用
❓同学们觉得如下操作是正确的的吗
int MoonCake = 10;
double& YueBing = MonnCake;
👆在解答上述问题前,我们先了解一下这个机制:以如下代码为例子
int c = 10;
double d = 1.11;
d = c;
➡️同学们肯定知道上述代码在d = c
中会发生隐式类型转换
- 但
隐式类型转换
并不是将c
的值直接转换后赋值给d
- 而是编译器会自动产生一个临时变量(我们看不见),类型为转换后的类型,最后再将这个临时变量的值赋值给
d
【即我们接收的是临时变量的值】
✨综上:
- 不仅仅是
隐式类型转换
,还是强制类型转换
or函数值返回
……只要会发生类型偏差的,本质并不是对本体产生影响,而是产生一个值相同、转换类型后的临时变量(具有常属性)
✊有了以上的了解补充后,我们再看回最初的疑问:操作其实是错误的
- 因为
double&
引用的是隐式类型转换后的临时变量,而非MoonCake
- 又因为临时变量具有
常属性
,所以这属于权限放大
问题,只有引用类型为const double&
才匹配权限 - 且这样的引用后续如果修改
MoonCake
的值,对YueBing
这个别名是没有任何影响的,因为我们本质是引用一个临时变量
而非MoonCake
🥯Ⅲ.使用场景
💡引用常见的使用场景有两个:
- 做参数
- 做返回值
🧇1.做参数
- 以上这种情况属于做
输出型参数
🧇2.做返回值(少数情况)
💡在深入了解前,我们先作补充:
❗我们可知:
- 函数的返回值返回并不是直接赋值给接收值(ret)
- 而是先赋值给
临时变量
,再由临时变量
赋值给接受值(ret),类似于前面类型转换的模式 - 这样的原因是:返回值
c
这个变量一旦出函数栈帧,生命周期就结束了,变量就会销毁,所以为保证正常返回想要的值,返回的便是对象C
的临时拷贝
👆有了以上补充,我们再来看看引用返回:
- 一般多作用于对象的生命周期不随着
函数栈帧
的结束而销毁的引用返回 - 即如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回
🥯Ⅳ.总结
⭐综上:
- 如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回
- 如果已经还给系统了,则必须使用传值返回
🍞五.内联函数
💡内联函数:
- 以
inline
修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率
➡️简单来说:
- 就是C语言中的
宏函数
➕函数
的合体 - 即
宏函数
的作用是和内联函数
的作用是一样的
❓既然c语言已经解决了,为什么c++还提供inline
函数呢
❗这是因为宏函数
具有如下缺点:
- 不支持调试【在预处理阶段就替换了】
- 宏语法复杂,容易出错【涉及符号优先级的问题】
- 没有类型安全的检查【即直接就替换了,没有检查类型是否匹配】
✨所以C++为了完美解决上述问题,就提出了inline
函数
-
但使用
inline
函数的场景多推荐于将频繁调用的小函数定义成内联函数,这就涉及到inline
函数的特性:- inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数
- inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联
- inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到
🥯Ⅰ.总结
⭐综上:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数
+ inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联
+ inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到
🥯Ⅰ.总结
⭐综上:
[外链图片转存中…(img-7FzVJfaI-1714440468826)]
[外链图片转存中…(img-ec6ig3Su-1714440468827)]
[外链图片转存中…(img-Hqme8naJ-1714440468827)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新