<初识C++(2)>《C++初阶》

目录

<初识C++(2)>《C++初阶》

                                                                             ——By 作者:新晓·故知

问题导引:

【名企面试题】

  解答:

1.函数重载

1.1 函数重载概念

1.2 名字修饰(name Mangling)

★采用C语言编译器编译后结果​

★采用C++编译器编译后结果

★Windows下名字修饰规则​

1.3 extern “C”

问题分析:

在Linux环境下,分析C++函数重载的实现:

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


<初识C++(2)>《C++初阶》

                                                                             ——By 作者:新晓·故知

问题导引:

【名企面试题】

1. 下面两个函数能形成函数重载吗?有问题吗或者什么情况下会出问题?
void TestFunc(int a = 10)
{
 cout<<"void TestFunc(int)"<<endl;
}
void TestFunc(int a)
{
 cout<<"void TestFunc(int)"<<endl;
}
2. C 语言中为什么不能支持函数重载?
3. C++ 中函数重载底层是怎么处理的?
4. C++ 中能否将一个函数按照 C 的风格来编译?

解答:

1.

不能形成函数重载。

根据函数重载定义:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的 形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题。这里可以看出函数参数个数,类型,顺序都相同,但此处缺省值并不影响,无法构成函数重载。

使用VS编译器,点开错误C2084可用看到:说明该函数已经被定义过了,这两个函数重复定义了,因为没有重载成功所以导致重复定义,因而使得编译失败。

2.

C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载

3.

详见后文图示讲解。

4.

可以。有时候在C++工程中可能需要 将某些函数按照 C 的风格来编译 在函数前加 extern "C" ,意思是告诉编译器, 将该函数按照 C 语言规则来编译。

1.函数重载

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。

比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前 者是“谁也赢不了!”,后者是“谁也赢不了!”

1.1 函数重载概念

函数重载:是函数的一种特殊情况,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;
}

1.2 名字修饰(name Mangling)

为什么C++支持函数重载,而C语言不支持函数重载呢?

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

1. 实际我们的项目通常是由多个头文件和多个源文件构成,而通过我们 C 语言阶段学习的编译链接,我们可以知道,【当前a.cpp 中调用了 b.cpp 中定义的 Add 函数时】,编译后链接前, a.o 的目标文件中没有 Add的函数地址,因为 Add 是在 b.cpp 中定义的,所以 Add 的地址在 b.o 中。那么怎么办呢?
2. 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就会到 b.o 的符 号表中找 Add 的地址,然后链接到一起 ( 如果同学们忘记了上面过程,咋们老师要带同学们回顾一下 )
3. 那么链接时,面对 Add 函数,连接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
4. 由于 Windows vs 的修饰规则过于复杂,而 Linux gcc 的修饰规则简单易懂,下面我们使用了 gcc 演示了这个修饰后的名字。
5. 通过下面我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类 型首字母】。

★采用C语言编译器编译后结果

结论: 在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

★采用C++编译器编译后结果

 

结论: linux 下,采用 g++ 编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息 添加到修改后的名字中

★Windows下名字修饰规则

 对比Linux会发现,windows下C++编译器对函数名字修饰非常诡异,但道理都是一样的。 【扩展学习:C/C++函数调用约定和名字修饰规则】

6. 通过这里就理解了 C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载
7. 另外我们也理解了,为什么函数重载要求参数不同!而跟返回值没关系。

1.3 extern “C”

有时候在 C++ 工程中可能需要 将某些函数按照 C 的风格来编译 在函数前加 extern "C" ,意思是告诉编译器, 将该函数按照 C 语言规则来编译
比如: tcmalloc google C++ 实现的一个项目,他提供 tcmallc() tcfree 两个接口来使用,但如果是C 项目就没办法使用,那么他就使用 extern “C” 来解决。
extern "C" int Add(int left, int right);
int main()
{
 Add(1,2);
 return 0;
}
链接时报错: error LNK2019: 无法解析的外部符号 _Add ,该符号在函数 _main 中被引用

问题分析:

Linux环境下,分析C++函数重载的实现:

 这里虽然创建的项目文件后缀为".cpp",但这和windows环境下的编译器不同,Windows环境下区分后缀(即".cpp"使用C++编译器,会兼容C语言。".c"使用C编译器,只能编译C语言)

一般的,gcc使用C语言编译器,不能编译cpp,但升级后的可能编译!(这里涉及Linux的知识,后续学习将会理解,此处仅需了解即可!)

 

这里解释函数重载“不允许在函数声明处不赋值,在函数定义处赋值”的原因:因为预处理后头文件展开,编译时只能在当前展开的头文件后调用,检查到函数定义与函数声明的相异,语法错误。

解释为什么C语言不支持函数重载?

C语言的函数名使用的是原来的函数名 ,编译链接后生成的符号表里面的函数名相同,但调用时main函数指令中的地址不同,就会冲突!

而在C++中:

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

猜你喜欢

转载自blog.csdn.net/m0_57859086/article/details/124501857