c++之重载函数学习总结

2023-11-03

一、C++中的函数重载:

1、函数重载的概念:

  • 用同一个函数名定义不同的函数

  • 当函数名和不同的参数搭配时函数的含义不同

  • 注意:在c语言中是没有函数重载这个概念的。

代码示例演示:

#include <stdio.h>
#include <string.h>
int func(int x)
{
    return x;
}

int func(int a, int b)
{
   return(a+b);
}

int func(const char* s)
{
    return strlen(s);
}

int main()
{

   return 0;

}

上面在c++编译器里面编译时没有问题的,如果放在c语言编译器里面编译是会报错的:

root@txp-virtual-machine:/home/txp# gcc test5.c
test5.c:8:5: error: conflicting types for ‘func’
 int func(int a, int b)
     ^
test5.c:3:5: note: previous definition of ‘func’ was here
 int func(int x)
     ^
test5.c:13:5: error: conflicting types for ‘func’
 int func(const char* s)
     ^
test5.c:3:5: note: previous definition of ‘func’ was here
 int func(int x)

所以说c语言里面不支持函数重载。

2、函数重载至少要满足下面的一个条件:

  • 参数个数不同

  • 参数类型不同

  • 参数顺序不同

比如下面两个函数可以构造重载函数吗?

int func (int a,const char* s)
{
   return a;
}

int func(const char*s,int a)
{
    return strlen(s)
}

答案肯定是可以构造重载函数的,读者可以自己试试(这个比较好理解)。

3、当函数默认参数遇上函数重载会发生什么?

例如下面的两个函数:

int func(int a, int b, int c =0)
{
    return a*b*c;
}
int func(int a, int b)
{
   return a+b;
}

到底会发生啥,我们还是看下面这个实验:

#include <stdio.h>

int func(int a, int b, int c = 0)
{
    return a * b * c;
}

int func(int a, int b)
{
    return a + b;
}


int main(int argc, char *argv[])
{
    int c = func(1, 2);
    
    return 0;
}

运行结果:

root@txp-virtual-machine:/home/txp# g++ test5.cpp
test5.cpp: In function ‘int main(int, char**)’:
test5.cpp:16:22: error: call of overloaded ‘func(int, int)’ is ambiguous
     int c = func(1, 2);
                      ^
test5.cpp:16:22: note: candidates are:
test5.cpp:3:5: note: int func(int, int, int)
 int func(int a, int b, int c = 0)
     ^
test5.cpp:8:5: note: int func(int, int)
 int func(int a, int b)

从上面报错的结果里面有一个单词ambiguous(意思是梦棱两可的),也就是说默认参数这种使用时不允许的。

4、C++编译器调用重载函数的准则:

  • 将所有同名函数作为候选者

  • 尝试寻找可行的候选函数:

  • 精确匹配实参

       通过默认参数能够匹配实参

      通过默认类型转换匹配实参

  • 匹配失败:

      最终寻找到的候选函数不唯一,则出现二义性,编译失败

      无法匹配所有候选者,函数未定义编译失败

5、函数重载的注意事项:

  • 重载函数在本质上是相互独立的不同函数

  • 重载函数的函数类型不同

  • 函数返回值不能作为函数重载的依据

  • 函数重载是由函数名和参数列表决定的

代码测试:

#include <stdio.h>

int add(int a, int b)  // int(int, int)
{
    return a + b;
}

int add(int a, int b, int c) // int(int, int, int)
{
    return a + b + c;
}

int main()
{
    printf("%p\n", (int(*)(int, int))add);
    printf("%p\n", (int(*)(int, int, int))add);

    return 0;
}

运行结果:

root@txp-virtual-machine:/home/txp# ./a.out
0x40052d
0x400541

从输出结果我们可以看出这两个函数的入口地址不一样,这表明这两个函数是不同的函数。

6、小结:

  • 函数重载是c++中引入的概念

  • 函数重载的本质是相互独立的不同函数

  • c++中通过函数名和函数参数确定函数调用

二、重载函数进阶学习

1、重载与指针:

下面的函数指针将保存哪个函数的地址?

int func(int x)
{
    return x;
}
int func(int a, int b)
{
    return a+b;
}
int func(const char* s)
{
  return strlen(s);
}

typedef int (*PFUNC) (int a);

int c =0;

PFUNC p = func;

c = p(2)//到底选择哪个func函数

函数重载遇上函数指针:

  • 将函数名赋值给函数指针时

  • 根据重载规则跳线与函数指针参数列表一致的候选者

  • 严格匹配候选者的函数类型与函数指针的函数类型

代码试验:

#include <stdio.h>
#include <string.h>

int func(int x)
{
   return x;
}

int func(int a, int b)
{
     return a+b;
}

int func(const char* s)
{
    return strlen(s);
}
typedef int(*PFUNC)(int a);

int main(int argc,char *argv[])
{
     int c =0;
     PFUNC p =func;

     c = p(2);
     printf("c=%d\n",c);
     return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp# ./a.out
c=2

从输出结果来看,很明显调用了第一个func函数。

2、注意:

  • 函数重载必然发生在同一个作用域中

  • 编译器需要用参数列表或者函数类型进行函数选择(也就是说碰到指针,要注意函数类型了)

无法直接通过函数名得到重载函数的入口地址,这里还是通过上面的例子演示一下:

#include <stdio.h>

int add(int a, int b)  // int(int, int)
{
    return a + b;
}

int add(int a, int b, int c) // int(int, int, int)
{
    return a + b + c;
}

int main()
{
    printf("%p\n", add);
    printf("%p\n", add);

    return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp# g++ test5.cpp
test5.cpp: In function ‘int main()’:
test5.cpp:15:23: error: overloaded function with no contextual type information
     printf("%p\n", add);
                       ^
test5.cpp:16:23: error: overloaded function with no contextual type information
     printf("%p\n", add);

三、C++和C相互调用:

  • 实际工程中C++和c代码相互调用是不可避免的

  • c++编译器能够兼容c语言的编译方式

  • c++编译器会优先使用c++编译的方式

extern关键字能够强制让C++编译器进行c方式的编译:

extern "c"
{


}

1、下面进行一个c++中调用c函数,这里我在当前创建三个文件:add.c 、add.h 、main.cpp。内容分别如下:

add.c内容:


#include "add.h"
int add(int a, int b)

{

    return a + b;

}

add.h内容:

int add(int a, int b);

然后我用gcc编译编译生成add.o文件:

root@txp-virtual-machine:/home/txp/add# vim add.c
root@txp-virtual-machine:/home/txp/add# vim add.h
root@txp-virtual-machine:/home/txp/add# gcc -c add.c -o add.o
root@txp-virtual-machine:/home/txp/add# ls
add.c  add.h  add.o

然后main.cpp里面调用add.c

#include <stdio.h>
int main()
{
    int c = add(1, 2);
    
    printf("c = %d\n", c);
    
    return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp/add# g++ main.cpp add.o
/tmp/ccqz3abQ.o: In function `main':
main.cpp:(.text+0x13): undefined reference to `add(int, int)'
collect2: error: ld returned 1 exit status


结果显示找不到这个函数,为了能够在c++里面调用c语言里面的函数,我们就要使用刚才上面讲的第四点了;这里我们先用nm命令来查看一下add.o文件里面是否生成符号表(有生成):

root@txp-virtual-machine:/home/txp/add# nm add.o
0000000000000000 T add

解决方法,main.cpp改成:

#include <stdio.h>

extern "c"
{
    #include "add.h"

}


int main()
{
    int c = add(1, 2);
    
    printf("c = %d\n", c);

    return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp/add# ./a.out
c = 3

2、c中如何调用c++函数:

这里我把main.cpp的内容改成:

extern "C"
{
   int add(int a, int b);

}

int add(int a, int b)
{
    return a+b;    
}


编译输出:

root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.o
root@txp-virtual-machine:/home/txp/add# nm -s test.o
0000000000000000 T add


add.c文件内容改成:

#include <stdio.h>
int main()
{ 
     int c =0;
     c = add(2,3);
     printf("c=%d\n",c);
     return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp/add# gcc add.c test.o
root@txp-virtual-machine:/home/txp/add# ./a.out
c=5

3、如何保证一段c代码只会以c的方式被编译?

解决方法如下:

  • __cplusplus是c++编译器内置的标准宏定义

  • __cplusplus的意义,确保c代码以统一的c方式被编译成目标文件

#ifdef __cplusplus
extern "C"
{
  #endif

  #ifdef __cplusplus
}
#endif

这里把main.cpp改成:

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

#include "add.h"

#ifdef __cplusplus
}
#endif

int main()
{
    int c = add(1, 2);
    
    printf("c = %d\n", c);
    
    return 0;
}

输出结果:

root@txp-virtual-machine:/home/txp/add# g++ main.cpp add.o
root@txp-virtual-machine:/home/txp/add# ./a.out
c = 3

4、注意事项

  • C++编译器不能以c的方式编译重载函数

  • 编译方式决定函数名被编译后的目标名

  • c++编译方式将函数名和参数列表编译成目标名,这里举个例子main.cpp:

int add(int a, int b)
{
  return a+b;
}

int add(int a, int b , int c)
{
   return a+b+c;
}

编译输出:

root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.oo
root@txp-virtual-machine:/home/txp/add# nm test.oo
0000000000000000 T _Z3addii
0000000000000014 T _Z3addiii

说明ii表示两个参数,iii表示三个参数

  • c编译方式只将函数名作为目标名进行编译,这里还是以main.cpp为例:

extern "C"
{
   int add(int a, int b)
  {
       return a+b;
  }

  int add(int a, int b , int c)
   {
      return a+b+c;
   }
}

输出结果:

root@txp-virtual-machine:/home/txp/add# g++ -c main.cpp -o test.oo
main.cpp: In function ‘int add(int, int, int)’:
main.cpp:8:29: error: declaration of C function ‘int add(int, int, int)’ conflicts with
 int add(int a, int b , int c)
                             ^
main.cpp:3:5: error: previous declaration ‘int add(int, int)’ here
 int add(int a, int b)

目标名起冲突所以报错。

5、小结:

  • 函数重载是c++对c的一个重要升级

  • 函数重载通过参数列表区分不同的同名函数

  • extern关键字能够实现c和c++的相互调用

  • 编译方式决定符号表中的函数名的最终目标名

四、总结:

好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。我是txp,下期见!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++之重载函数学习总结 的相关文章

  • 由 IHttpClientFactory 注入时模拟 HttpClient 处理程序

    我创建了一个自定义库 它会自动为依赖于特定服务的 Polly 策略设置HttpClient 这是使用以下方法完成的IServiceCollection扩展方法和类型化客户端方法 一个简化的例子 public static IHttpClie
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 在 Visual Studio 2010 中从 Fortran 调用 C++ 函数

    我想从 Fortran 调用 C 函数 为此 我在 Visual Studio 2010 中创建了一个 FORTRAN 项目 之后 我将一个 Cpp 项目添加到该 FORTRAN 项目中 当我要构建程序时出现以下错误 Error 1 unr
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 如何使用 Python 3 检查目录是否包含文件

    我到处寻找这个答案但找不到 我正在尝试编写一个脚本来搜索特定的子文件夹 然后检查它是否包含任何文件 如果包含 则写出该文件夹的路径 我已经弄清楚了子文件夹搜索部分 但检查文件却难倒了我 我发现了有关如何检查文件夹是否为空的多个建议 并且我尝
  • Protobuf 如何编码 oneof 消息结构

    对于这个 python 程序 在编码时运行 protobuf 编码会给出以下输出 0a 10 08 7f8a 0104 08 02 10 0392 0104 08 02 10 03 18 01 我不明白的是为什么8a后面有一个01 为什么9
  • 带有 LSTM 的 GridSearchCV/RandomizedSearchCV

    我一直在尝试通过 RandomizedSearchCV 调整 LSTM 的超参数 我的代码如下 X train X train reshape X train shape 0 1 X train shape 1 X test X test
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • 如何使用 AWS Lambda Python 读取 AWS S3 存储的 Word 文档(.doc 和 .docx)文件内容?

    我的场景是 我尝试使用 python 实现从 Aws Lambda 读取 AWS 存储的 S3 word 文档 doc 和 docx 文件内容 下面的代码是我使用的 我的问题是我可以获取文件名 但无法读取内容 def lambda hand
  • C# HashSet 只读解决方法

    这是示例代码 static class Store private static List
  • 外键与独立关系 - Entity Framework 5 有改进吗?

    我读过了several http www ladislavmrnka com 2011 05 foreign key vs independent associations in ef 4 文章和问题 https stackoverflow
  • python 中的“槽包装器”是什么?

    object dict 和其他地方的隐藏方法设置为这样的
  • 制作一份 Python 文档的 PDF 文件

    Python 官方网站提供 PDF 文档下载 但它们是按章节分隔的 我下载了源代码并构建了 PDF 文档 这些文档也是单独的 PDF 我怎么能够从源代码中的 Makefile 构建一个 PDF 文件 我认为这样阅读起来会更方便 如果连接单独
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • 如何在 Flask 中的视图函数/会话之间传递复杂对象

    我正在编写一个 Web 应用程序 当 且仅当 用户登录时 该应用程序从第三方服务器接收大量数据 这些数据被解析为自定义对象并存储在list 现在 用户在应用程序中使用这些数据 调用不同的视图 例如发送不同的请求 我不确定什么是最好的模式在视
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • python 对浮点数进行不正确的舍入

    gt gt gt a 0 3135 gt gt gt print 3f a 0 314 gt gt gt a 0 3125 gt gt gt print 3f a 0 312 gt gt gt 我期待 0 313 而不是 0 312 有没有
  • 从列表中选择项目以求和

    我有一个包含数值的项目列表 我需要使用这些项目求和 我需要你的帮助来构建这样的算法 下面是一个用 C 编写的示例 描述了我的问题 int sum 21 List
  • 如何在Python脚本中从youtube-dl中提取文件大小?

    我是 python 编程新手 我想在下载之前提取视频 音频大小 任何 YouTube 视频 gt gt gt from youtube dl import YoutubeDL gt gt gt url https www youtube c

随机推荐

  • IDEA创建SpringBoot项目并整合SSM+Redis

    1 创建SpringBoot项目 1 File gt New gt Project 选择Spring Initiatilizr 2 点击下一步 填入相应的GroupId 选择Java Version等 在选择下一步 3 选择包依赖 如果只是
  • 【Linux入门之密码登陆】centOS密码输入正确却登陆不上,原来是我误会了

    搜索Linux修改密码 出来的基本都是修改root用户的密码 而我的GUI界面一打开显示的用户列表里是普通用户 每次登陆我都使用我刚改好的root密码 自然密码错误 无法登陆 我还跑到百度搜 centOS密码输入正确却登陆不上 等等相似的描
  • springboot项目热部署

    快捷键Ctrl Alt Shift 选Registry 勾选下面这个 然后 apply一下 pom xml
  • sc不是内部命令也不是外部命令处理方法

    sc不是内部命令也不是外部命令 用户环境变量里path里加上 SystemRoot system32
  • 【小沐学写作】程序员必备技能:在线协作文档汇总

    文章目录 1 简介 2 微软Office在线文档 2 1 功能简介 2 2 使用费用 2 3 用户体验 3 石墨文档 3 1 功能简介 3 2 使用费用 4 腾讯文档 4 1 功能简介 4 2 使用费用 5 语雀 5 1 功能简介 5 2
  • SQL grouping sets 子句

    grouping sets子句允许你指定多个group by 选项 增强了group by 的功能 可以通过一条select 语句实现复杂繁琐的多条select 语句的查询 并且更加的 高效 解析存储一条SQL于语句 下面通过使用 grou
  • 图像分割高铁扣件

    图像分割 针对高铁扣件 在对图像的研究和应用中 人们往往仅对图像中的某些部分感兴趣 这些部分通常被称为前景或目标 其余部分则称为背景 目标一般对应于图像中特定的 具有独特性质的区域 独特性质可以是像素的灰度值 物体轮廓曲线 颜色和纹理等 为
  • GBDT(GBM)调参方法

    GBM参数 总的来说GBM的参数可以被归为三类 树参数 调节模型中每个决定树的性质 Boosting参数 调节模型中boosting的操作 其他模型参数 调节模型总体的各项运作 GBDT类库弱学习器参数 即定义一个决定树所需要的参数 由于G
  • 【源码分析】zeebe actor模型源码解读

    zeebe actor 模型 如果有阅读过zeebe 源码的朋友一定能够经常看到actor run 之类的语法 那么这篇文章就围绕actor run 方法 说说zeebe actor 的模型 环境 zeebe release 8 1 14
  • Java统一返回结果自动封装组件【Response-boxing】

    0 需求 统一封装返回结果 包括code message data数据 不用手动封装 通过自定义注解标记即实现封装 如果controller结果已经手动封装 则不重复封装 1 项目结构 2 创建自定义注解 import java lang
  • Paxos算法的java实现demo(只是为了简单的测试)

    Paxos 的概念我就不在这里啰嗦了 网上有很多优秀的博客 下面是我推荐的一个写的比较好的 https www cnblogs com linbingdong p 6253479 html 我们直接上代码吧 代码里面都有注释 先看一下项目结
  • 基于mulitisim14仿真的数字电子称

    参考了下面的文章做了一个数字电子称 https www renrendoc com paper 119413660 html 仿真如下 需要仿真文件的私聊
  • 中国工程院院士郑纬民:元宇宙是一个赋能实体经济的重要新赛道

    2022年3月31日 元宇宙产业委共同主席郑纬民院士在第三届元宇宙产业论坛发表了题为 元宇宙创新应用全面启航 算力是基础 的演讲 以下为郑纬民院士的演讲全文 今年全国两会中一些代表和委员提出了关于元宇宙的建议和提案 说明元宇宙已经得到了大家
  • 吉林大学超星MOOC学习通高级语言程序设计 C++ 实验04 数组及其在程序设计中的应用(2021级)(1)

    1 索引数组排序 题目编号 Exp04 Enhance04 GJBook3 06 21 题目名称 索引数组排序 题目描述 已知n n 100 个元素的整型数组 A 未排序 一个索引数组 B 保存 A 的下标 编写程序 在不改变数组A的情况下
  • Unikernels 解读

    转载于https zhuanlan zhihu com p 29053035 Unikernels Beyond Containers to the Next Generation of Cloud是 Russ Pavlicek的一本动物书
  • (Animator详解二)Unity Animator的基本属性

    在Inspector下 Animator的第一项为状态机的名称 注意 这里的名称不是动画名称 Tag 当前动画的Tag标签 可以通过Tag值来处理一些逻辑 Motion 动画片段的名称 Speed 动画的播放速度 1表示正常播放 speed
  • spring一些捞到的东西

    spring指令重排和多线程 原来在编写程序的时候要考虑这么多东西 要想清楚每一个代码 每一个线程在哪执行 还有要懂得jvm 的一些优化的 任重而道远啊 单例模式 只允许一个实例的存在 构造函数是私有的 对外提供获取实例的方法 getIns
  • CSS -网页动画

    目录 制作网页动画 1 CSS变形 2 CSS过渡 3 CSS动画 4 总结 制作网页动画 1 CSS变形 CSS3变形是一些效果的集合 如平移 旋转 缩放 倾斜效果 每个效果都可以称为变形 transform 它们可以分别操控元素发生平移
  • 第七十六篇 MIPI简单说明

    MIPI 移动行业处理器接口 是Mobile Industry Processor Interface的缩写 MIPI是MIPI联盟发起的为移动应用处理器制定的开放标准 目的是把手机内部的接口如摄像头 显示屏接口 射频 基带接口等标准化 从
  • c++之重载函数学习总结

    一 C 中的函数重载 1 函数重载的概念 用同一个函数名定义不同的函数 当函数名和不同的参数搭配时函数的含义不同 注意 在c语言中是没有函数重载这个概念的 代码示例演示 include