C++入门1(下)

简介: C++入门1

4.函数重载

C++是支持函数重载的,

所谓函数重载就是指:两个函数名称相同,但是参数的个数或者类型或者类型顺序不同,即构成函数重载

注意:如果两个函数只有返回值不同的话,并不会构成函数重载

1.函数重载的形式

这里以Add函数为例

int Add(int num1, int num2)
{
  return num1 + num2;
}
double Add(double num1, double num2)
{
  return num1 + num2;
}
int main()
{
  cout << Add(1, 2) << endl;
  cout << Add(1.1, 1.2) << endl;
  //cout << Add(1, 1.2) << endl;//err
  return 0;
}

大家注意到了这一行了吧

cout << Add(1, 1.2) << endl;//err
• 1

为什么会报错呢?

首先,在C语言的学习中,我们知道int和double类型是可以发生隐式类型转换的

也就是说在Add(1,1.2)这个函数调用中,有可能会发生两种情况:

(1)(int类型的1)被隐式转换为double类型,去匹配double Add(double num1, double num2)

(2)(double 类型的1.2)被隐式转换为int类型,去匹配int Add(int num1, int num2);

也就是说这个函数调用存在歧义,所以编译器会报错

也就是说哪怕我们去掉其中的一个Add函数,这个函数调用就不会报错了

下面是函数重载的几种错误形式

1.两个函数仅有返回值不同

2.参数仅有名称不同或者仅有参数名称顺序不同

注意:并不是说两个函数构成重载之后对它们的调用就不会产生歧义

2.函数重载和缺省参数的联系

下面请大家看一下下面两个函数能不能构成函数重载

void f(int num)
{
  cout << "f(int num)" << endl;
}
void f(int num = 1)
{
  cout << "f(int num = 1)" << endl;
}

答案是:不构成重载,因为参数的个数,类型,类型顺序均相同

那么在请大家看一下这两个函数会不会构成重载呢?

void f()
{
  cout << "f()" << endl;
}
void f(int num = 1)
{
  cout << "f(int num = 1)" << endl;
}

答案是:构成重载

但是这意味着这两个函数在调用时就真的没有歧义了吗?

这种情况下是可以的

但是这种情况下就不行了:

因为在调用的时候产生了歧义

这就像是一个经典的问题:

你妈妈和你老婆掉水里,你只能救一个,你救谁?

C++懒得跟你折腾,直接在语法上不允许这样做,直接报错

3.函数重载的底层原理:函数名修饰规则

还有一个问题:函数的参数不同就能构成函数重载,那我返回值不同凭什么就无法构成函数重载呢?

这就要谈一下函数名修饰规则了

在谈这个之前,我们先建立一个Func.h,一个Func.cpp,一个Test.cpp文件

并且复习一下C语言阶段的编译链接的知识

现在我们有Func.h,Func.cpp,Test.cpp这三个文件
其中在Func.cpp文件中定义了两个重载的func函数
我们知道编译分为4个阶段:
1.预处理:
头文件展开
宏替换
去注释
条件编译
Fun.h在Func.cpp和Test.cpp中展开
Func.cpp中同时拥有func函数的声明和定义,Test.cpp中拥有func函数的声明和具体调用
Fun.cpp ->  Fun.i
Test.cpp -> Test.i
2.编译:
把.i文件进行语法检查(语法分析,词法分析,语义分析)生成汇编代码(.s文件)
Fun.i  ->  Fun.s
Test.i ->  Test.s
3.汇编阶段:
把汇编代码转换为二进制机器码,生成目标文件(.o文件)
Fun.s -> Fun.o
Test.s -> Test.o
4.链接阶段:
合并.o目标文件,链接一些没有确定的函数的地址,合并段表,符号表的重定位等等
生成可执行程序:
Windows: .exe,
Linux:   a.out
而这个函数名修饰规则就是在链接阶段进行的
在预处理阶段结束后
Func.cpp中同时拥有func函数的声明和定义,Test.cpp中拥有func函数的声明和具体调用
所谓声明就是一种承诺,是承诺,就要兑现
而链接阶段就是这个承诺兑现的时候
怎么兑现呢?让我的Test.cpp能够找到func函数的定义,就是兑现
怎么找到呢?
(通过函数声明去找地址)
.o文件中有一个东西叫做符号表,符号表中存储了函数名跟函数地址的一种映射关系
在链接阶段,通过符号表跟一种映射关系找到函数的地址,就能进行函数的调用了
而对于这个符号表来说
在C语言中,符号表中只会建立函数名跟函数地址的映射关系,
也就决定了在C语言中不允许存在同名函数,否则在链接阶段会找到多个函数地址,产生歧义
而对于C++来说
有函数名修饰规则:把函数的参数代入进去对符号表中的映射进行修饰
在Linux下可以尝试看到这个名字
Linux下函数名修饰规则:
_Z4func(_Z是前缀+函数名的字符个数+函数名+参数首字母)
_Z4funcdi(第一个参数类型:double,第二个:int)
_Z4funcid(第一个参数类型:int,第二个:double)
int* :Pi
有兴趣,可以自己在Linux下去试试

下面我们在Linux下给大家看一看

我们在Linux下建立了两个文件:

test.c

test.cpp

先看.c:

cpp:

然后我们用gcc把test.c编译出来,编译成testc

然后用这个命令查看testc的内容

这个 < func>前面的就是它的地址,也就是第一个指令的地址,

而且我们发现这个函数名根本就没有被修饰

下面我们来看一下.cpp文件

先用g++把test.cpp编译成了testcpp

还是用这个命令进行查看

然后我们发现C++编译后的函数名的确被修饰了

修饰成了

_Z4funcid
• 1

大家也可以试一下其他的类型,指针的话:

例如int* 类型的是:会被修饰为Pi

下面我们就可以回答这个问题了:

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

因为在链接阶段

C语言文件的函数地址是完全只通过函数名去进行查找的,如果存在同名函数,会导致找到多个函数地址,让函数调用产生歧义

而C++文件的函数名经过了修饰,那么如果两个函数名相同,但是参数不同就可以进行区分了

所以对于构成重载的函数,只会找到那唯一一个,不会产生歧义

Linux下函数名修饰规则:

_Z4func(_Z是前缀+函数名的字符个数+函数名+参数首字母)

注意:不同编译器下的函数名修饰规则是不同的

那么为什么我刚才不在windows下演示呢?

因为windows下的函数名修饰太过复杂

我们把Func.cpp中的代码注释掉,让它发生链接错误

看到蓝色标注的地方,windows下也是一个类型对应一个符号,不过不像Linux下那么直观

以上就是C++入门1的全部内容,希望能对大家有所帮助

相关文章
|
8月前
|
存储 安全 编译器
c++入门
c++作为面向对象的语言与c的简单区别:c语言作为面向过程的语言还是跟c++有很大的区别的,比如说一个简单的五子棋的实现对于c语言面向过程的设计思路是首先分析解决这个问题的步骤:(1)开始游戏(2)黑子先走(3)绘制画面(4)判断输赢(5)轮到白子(6)绘制画面(7)判断输赢(8)返回步骤(2) (9)输出最后结果。但对于c++就不一样了,在下五子棋的例子中,用面向对象的方法来解决的话,首先将整个五子棋游戏分为三个对象:(1)黑白双方,这两方的行为是一样的。(2)棋盘系统,负责绘制画面。
125 0
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
11月前
|
存储 分布式计算 编译器
C++入门基础2
本内容主要讲解C++中的引用、inline函数和nullptr。引用是变量的别名,与原变量共享内存,定义时需初始化且不可更改指向对象,适用于传参和返回值以提高效率;const引用可增强代码灵活性。Inline函数通过展开提高效率,但是否展开由编译器决定,不建议分离声明与定义。Nullptr用于指针赋空,取代C语言中的NULL。最后鼓励持续学习,精进技能,提升竞争力。
|
11月前
|
C++
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
175 2
C++入门12——详解多态1
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
241 1
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
201 1
|
C语言 C++ 容器
C++入门7——string类的使用-1
C++入门7——string类的使用-1
148 0
|
程序员 C语言 C++
C++入门5——C/C++动态内存管理(new与delete)
C++入门5——C/C++动态内存管理(new与delete)
294 1
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
247 0
C++入门6——模板(泛型编程、函数模板、类模板)