iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++深入浅出讲解函数重载
  • 900
分享到

C++深入浅出讲解函数重载

2024-04-02 19:04:59 900人浏览 八月长安
摘要

目录前言函数重载1.1 函数重载的概念1.2 函数重载的意义1.3 名字修饰(name Mangling)1.4 extern "C"前言 自然语言中,一个词可以

前言

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

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

函数重载

1.1 函数重载的概念

函数重载:

  1. 它是函数的一种特殊情况,c++允许在同一作用域中同一作用域中声明几个功能类似的同名函数
  2. 函数重载的关键是函数的参数列表,也称为“函数特征标”
  3. 这些同名函数的形参列表(参数个数、类型和顺序(不同类型的顺序))必须不同,常用来处理实现功能类似数据类型不同的问题
  4. 函数重载也是多态的一种,多态指的是“有多种形式”
//C语言不支持重载,C++支持重载
int Add(int left, int right)
{
   return left+right;
}
double Add(double left, double right)
{
   return left+right;
}
int Add(int left, double right)
{
   return left+right;
}
int Add(double left, int right)
{
   return left+right;
}
int main()
{
   Add(10, 20);
   Add(10.0, 20.0);
   Add(10, 20.0);
   Add(10.0, 20.0)
   return 0;
}

下面两个函数属于函数重载吗?

short Add(short left, short right)
{
   return left+right;
}
int Add(short left, short right)
{
   return left+right;
}
int main()
{
   Add(10, 20);
   Add(10, 20);
   return 0;
}

代码解析:

  1. 上述代码中的两个函数不属于函数重载
  2. 因为重载的形参列表(参数个数、类型和顺序)必须不同
  3. 函数重载与函数返回值的类型无关,并且在函数调用时,也是无法识别它的

1.2 函数重载的意义

意义:

在C语言中,想要定义多个不同类型交换数据的子函数,需要不同的函数名来命名,比如SweapA、SweapB…等等

void SweapA(int *pa, int *pb)
{
   int temp = *pa;
   *pa = *pb;
   *pb = temp;
}
void SweapB(double *pa, double *pb)
{
   double temp = *pa;
   *pa = *pb;
   *pb = temp;
}
int main()
{
   int a = 10, b = 20;
   double c = 10.0, d = 20.0;
   SweapA(&a, &b);
   SweapB(&c), &d);
   return 0;
}
  1. 但是,在C++中,通过函数重载,只需要命名一次就可以了
  2. 虽然跟C语言一样要重复定义函数,但是后面会学到函数模板后,可以很好的解决这个重复定义问题
void Sweap(int *pa, int *pb)
{
   int temp = *pa;
   *pa = *pb;
   *pb = temp;
}
void Sweap(double *pa, double *pb)
{
   double temp = *pa;
   *pa = *pb;
   *pb = temp;
}
int main()
{
   int a = 10, b = 20;
   double c = 10.0, d = 20.0;
   Sweap(&a, &b);
   Sweap(&c), &d);
   return 0;
}

1.3 名字修饰(name Mangling)

名字修饰(name Mangling):

  • C++为了跟踪每一个重载函数,它都会给这些函数指定一个私密身份
  • 使用C++编译器编写函数重载程序时,C++编译器将执行一些奇特的操作 — — ---名称修饰 或 名称矫正
  • 它根据函数原型中指定的形参对每个函数名进行加密
  • 对参数数目和类型进行编码,添加的一组符号符合随函数形参列表而异,修饰时使用的约定(函数名)随编译器而异

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

  • C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接
  • 预处理(.i):文件展开、宏替换、条件编译、去注释
  • 编译(.s):检查语法是否正确,生成汇编代码
  • 汇编(.o):将汇编代码转化成二进制的机器码
  • 链接(a.out):生成符号表,找调用函数的地址,链接匹配,合并到一起

  • 实际我们的项目通常是由多个头文件和多个源文件构成,当前a.cpp中调用了b.cpp中定义的Add函数
  • 在编译后链接前的处理阶段,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。那么怎么办呢?
  • 链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起
  • 链接时,面对Add函数,链接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则

linux下使用GCc和g++编译器演示函数名被修饰后的名字

采用C语言编译器编译后结果(反汇编)

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

采用C++编译器编译后结果(反汇编)

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

总结

gcc的函数修饰后名字不变。而g++的函数修饰后变成(_Z+函数长度+函数名+类型首字母)

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

windows下名字修饰规则

结论:对比Linux会发现,windows下C++编译器对函数名字修饰非常奇怪,但道理都是一样的

扩展学习:C/C++函数调用约定和名字修饰规则

C++函数重载

C/C++的调用约定

接下来,再演示一个例子

f.h
#include <stdio.h>

void f(int a, double b);
void f(double b, int a);

f.cpp
#include "f.h"

void f(int a, double b);
{
   printf("%d %lf\n", a, b)
}

void f(double b, int a);
{
   printf("%lf %d\n", b, a)
}
Test.cpp
#include "f.h"

int main()
{
   f(1, 2.222);
   f(2.222, 1);
   return 0;
}

编译后,生成汇编指令;链接时,生成符号表

Linux下g++(C++)编译器的命名:

Linux下gcc(C)编译器的命名:

1.4 extern "C"

  • 有时候在C++工程中可能需要将某些函数按照C的风格来编译
  • 但是,大多数情况下是C工程需要将某些函数按照C++的风格来编译
  • C可以调用CPP的静态/动态库,而CPP也可以调用C的静态/动态库
  • extern “C”是告诉编译器,它所声明的函数,是C的库,要用C的链接方式去调用静态库或动态库

那么CPP是怎么调用C中的静态/动态库呢?(vs2022演示)

首先,我们用C来生成一个静态库或动态库

Test.h
#include <stdio.h>
void PrintArray(int* p, int n); //显示数组内容
void InsertSort(int* p, int n); //插入排序
Test.C
#include "Test.h"
void InsertSort(int* p, int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int end = i;
        int tmp = p[end + 1];
        while (end >= 0)
        {
            if (tmp < p[end])
            {
                p[end + 1] = p[end];
                --end;
            }
            else
            {
                break;
            }
        }
        p[end + 1] = tmp;
    }
}

配置类型改成静态库后,生成解决方案,就得到后缀.lib文件了

在CPP项目中添加新的库目录(这个库是你生成的静态库的路径)

增加新的依赖项(依赖项为生成静态库的文件名+后缀"Test.lib")

做完这些准备后,我们来进行编译程序

  • 编译后我们发现链接阶段时出现了错误
  • 原因是:C++调用C时,它们之间的函数命名规则(名称修饰)不同
  • 我们需要C++中的extern "C"来解决
  • extern “C”是告诉编译器,它所声明的函数,是C的库,要用C的链接方式去调用静态库或动态库
extern "C"
{
    //"../"是在当前目录的上一个目录中找文件
    #include "../../Test/Test/Test.h"
}
#include <iOStream>
using namespace std;
void TestInsertSort()
{
	int Array[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
	InsertSort(Array, sizeof(Array) / sizeof(Array[0]));
	for (int i = 0; i < 10; ++i)
		cout << Array[i] << " ";
	cout << " " << endl;
}
int main()
{
	TestInsertSort();
	return 0;
}

如果C想调用CPP的静态或动态库呢?

  • C调用CPP库时,也会遇到名称修饰的问题
  • 这里需要对CPP的名称修饰规则改成C的规则
  • 这里我们需要用到"条件编译"来解决问题
Test.h
#include <stdio.h>
#ifdef __cplusplus
      #define EXTERN_C extern "C"
#else 
      #define EXTERN_C
#endif
EXTERN_C void PrintArray(int* p, int n);
EXTERN_C void InsertSort(int* p, int n);
Test.cpp
#include "Test.h"
void PrintArray(int* p, int n)
{
    for (int i = 0; i < n; ++i)
    {
        printf("%d ", p[i]);
    }
    printf("\n");
}
void InsertSort(int* p, int n)
{
    for (int i = 0; i < n - 1; ++i)
    {
        int end = i;
        int tmp = p[end + 1];
        while (end >= 0)
        {
            if (tmp < p[end])
            {
                p[end + 1] = p[end];
                --end;
            }
            else
            {
                break;
            }
        }
        p[end + 1] = tmp;
    }
}

感谢大家支持!!!

到此这篇关于C++深入浅出讲解函数重载的文章就介绍到这了,更多相关C++函数重载内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++深入浅出讲解函数重载

本文链接: https://www.lsjlt.com/news/150235.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++深入浅出讲解函数重载
    目录前言函数重载1.1 函数重载的概念1.2 函数重载的意义1.3 名字修饰(name Mangling)1.4 extern "C"前言 自然语言中,一个词可以...
    99+
    2022-11-13
  • C++深入讲解函数重载
    目录函数重载概念重载依据值型别判断函数重载的规则名字粉碎-名字修饰函数重载 概念 在C++中可以为两个或者两个以上函数提供相同的函数名称,只要参数类型不同,或者参数数目不同,参数顺序...
    99+
    2022-11-13
  • C++深入浅出讲解缺省参数
    目录缺省参数定义用法缺省参数 一般情况下,函数调用时的实参个数应与形参相同,但为了更方便地使用函数,C++也允许定义具有缺省参数的函数,这种函数调用时,实参个数可以与形参不相同。 定...
    99+
    2022-11-13
  • 深入浅出讲解Java8函数式编程
    目录什么是函数式编程Java8内置了一些常用的方法接口FunctionalInterface用的比较多的函数接口总结什么是函数式编程 函数式编程就是一种抽象程度很高的编程范式,纯粹的...
    99+
    2022-11-13
  • C++深入分析讲解函数与重载知识点
    目录函数的默认(缺省)参数1、默认参数的定义2、默认参数的注意点占位参数1、占位参数 函数内部无法使用2、占位参数 可以设置成缺省参数函数重载函数的默认(缺省)参数 1、默认参数的定...
    99+
    2022-11-13
  • c++深入浅出讲解堆排序和堆
    目录堆是什么最大堆最小堆堆排序最终代码关于堆堆是什么 堆是一种特殊的完全二叉树 如果你是初学者,你的表情一定是这样的 别想复杂 首先,你一定见过这种图 咱们暂时不管数字 这就是一个...
    99+
    2022-11-13
  • 深入了解C++函数重载解析策略
    参考《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,张海龙 袁国忠译,人民邮电出版社。C++ 使用重载解析策略来决定为函数调用使用哪一个函数定义。...
    99+
    2022-11-13
    C++函数重载解析 C++函数重载
  • C语言深入浅出讲解指针的使用
    目录一、利用指针倒序字符串二、题目实例三、总结一、利用指针倒序字符串 void _reversal(char* left, char* right) { while (lef...
    99+
    2022-11-13
  • 【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)
         目录 一. 前言 二. 什么是C++ 三. C++关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C++的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺...
    99+
    2023-09-02
    c++ 开发语言 笔记 函数重载 缺省参数 命名空间
  • C语言深入浅出讲解顺序表的实现
    目录1.线性表2.顺序表2.1 概念及结构2.2 提供接口2.3 接口实现今天起开始编写数据结构中的各种数据结构及算法的实现,说到顺序表,我们首先得了解下线性表。 1.线性表 线性表...
    99+
    2022-11-13
  • C++深入浅出讲解隐藏this指针的用法
    目录1.this指针的引出2.this指针的特性3.练习一下本篇文章我们将一起讨论在有趣的知识点--隐藏的this指针。本篇我们要使用到之前我们所学习到的C++类与对象,如果有各位小...
    99+
    2022-11-13
  • C++深入分析回顾函数重载
    目录一、函数重载回顾二、类中的重载三、重载的意义四、小结一、函数重载回顾 函数重载的本质为相互独立的不同函数C++ 中通过函数名和函数参数确定函数调用无法直接通过函数名得到重载函数的...
    99+
    2022-11-13
  • C语言深入浅出分析函数指针
    我们先看一个代码: #include<stdio.h> void test() { printf("haha\n"); } int main() { printf("...
    99+
    2022-11-13
  • C++超详细讲解函数重载
    目录1 函数重载的定义2 构成函数重载的条件3 编译器调用重载函数的准则4 函数重载的注意事项4.1 避开重载带有指定默认值参数的函数4.2 注意函数重载遇上函数指针4.3 C++编...
    99+
    2022-11-13
  • 深入浅出讲解Angular变更检测
    Angular 中的变更检测是一种用来将应用程序 UI 的状态与数据的状态同步的机制。当应用逻辑更改组件数据时,绑定到视图中 DOM 属性上的值也要随之更改。变更检测器负责更新视图以...
    99+
    2022-11-13
  • Java深入浅出讲解代理模式
    目录1、动态代理模式2、JDK动态代理3、JDK动态代理代码演示1、动态代理模式 动态代理的特点: 当代理对象的时候,不需要实现接口代理对象的生成,是利用JDK的API,动态的在内存...
    99+
    2022-11-13
  • C++深入浅出讲解希尔排序算法的实现
    目录希尔排序1.基本思想预排序2.算法实现3.时间复杂度插入排序分为两种:直接插入排序&希尔排序 希尔排序 1.基本思想 希尔排序是在直接插入排序基础上的优化,属于非常牛掰的...
    99+
    2022-11-13
  • 深入浅出讲解MySQL的并行复制
    一、并行复制的背景 首先,为什么会有并行复制这个概念呢? 1. DBA都应该知道,MySQL的复制是基于binlog的。  2. MySQL复制包括两部分,IO线程 和 SQL线程。 ...
    99+
    2022-10-18
  • 深入浅出讲解Java中的枚举类
    目录一、枚举类的使用 二、如何定义枚举类 背景:类的对象只有有限个,确定的。举例如下: > 星期: Monday (星期一)、….、 Sunday (星期天) > 性别:...
    99+
    2022-11-12
  • C语言深入讲解函数的使用
    目录关于函数1. 函数的定义形式2. 函数的声明3. 返回语句4. 函数参数4.1 形式参数(传值调用)4.2 实际参数(传址调用)4.3 无参数5. 函数的调用5.1 嵌套调用5....
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作