首页 > 编程语言 >C++函数重载探究

C++函数重载探究

时间:2024-01-23 11:56:14浏览次数:29  
标签:调用 函数 程序 C++ 修饰 探究 cpp 重载

函数重载

什么是函数重载

简单来说 ,就是 可以有多个相同函数名的函数, 但是这些函数的参数个数  或者参数类型 或者参数的类型顺序  是不一样的.

通常来处理类似的功能,但是数据个数或者类型不同的情况

如:计算器就是一个例子 ,加法可以是任何个数任何类型的数的加法

但是都只按一个 " + "号就可以完成

如以下示例: 不同类型的加法

int add(int a, int b)
{
	return a + b;
}
double add(double a, double b)
{
	return a + b;
}
int add(int a, int b, int c)
{
	return a + b + c;
}
//下面两个 不同类型的参数的顺序不同
double add(int a, double b)
{
	return a + b;
}
double add(double a, int b)
{
	return a + b;
}
int main3()
{
	cout << add(1, 1) << endl;
	cout << add(1.5, 2.3) << endl;
	cout << add(1, 2, 3) << endl;
	cout << add(1, 1.1) << endl;
	cout << add(1.1, 1) << endl;
	return 0;
}

注意

返回值不同 是不构成重载的,因为调用的时候,无法直接判断出调用谁

举个例子

short add(short a, short b)
{
    return a+b;
}
int add(short a,short b)
{
    return a+b;
}
int main()
{
    add(1,2);//那么此时是调用哪一个呢? 无法区分!
    return 0;
}

函数重载的意义就在于:让用的地方很方便,就像用同一个函数一样.

为什么C++支持函数重载,而C语言不支持?(Linux环境探究)

既然函数重载这么好用,那么函数重载的底层原理是什么呢?

C和C++程序运行起来需要经历 : 预处理、编译、汇编、链接

  • 在汇编之后生成目标文件(Linux下.o)(windows下 .obj),之后就把所有的.o文件合并到一起. 并且main.cpp 里有一些头文件包含了 函数的声明或者变量的声明

  • 此时这些头文件里的变量或者函数实际上是在其他的文件中定义的 , 而在main文件中需要调用 ,而函数的地址其实是函数中第一条指令的地址,它在定义函数的.o文件中, 所以就会去其他的.o文件中 去找这些只有声明的变量或者函数的地址 ,找到之后就可以调用了(编译成功) , 如果找不到 ,就会出现链接错误(LINK ERROR)。

  • 链接的时候,怎么去找这些函数和变量呢?-- 根据他们的函数和变量名字。但是每一个编译器都有自己的函数名修饰规则,在编译的时候会生成一个符号表,里面存放函数修饰后的名字。比如要调用test.cpp中写的一个Add函数,在汇编之后,生成了_ZAddii

  • 这时候,在main.o中 去找声明中的函数的时候就会按照修饰后的名字去找该函数。

    • 在C语言中,函数修饰之后还是原来的函数名,比如Add 修饰之后,符号表中的函数名字还是Add
    • 而C++中,函数修饰之后就变了,在linux下 一般是:修饰符+原来的函数名+参数的首字母缩写

    所以,C语言如果定义相同的函数名,在编译的时候就回出错,因为即使他们的参数不一样,修饰之后也不会生成不同的函数名,所以,相同的函数名字是不可以识别的。

    而C++ 只要参数不同 生成的函数名是不同的,所以编译器就可以识别调用哪一个。这就是为什么C++支持重载而C语言不支持

如图:Linux下C语言的函数 编译修饰之后还是原来的函数名
image

而C++中的函数 编译修饰之后的函数名不一样了

image

Linux下 的函数名修饰规则一般是 _Z+X+函数名+参数首字母

其中_Z是一个前缀,X是一个数字表示 原函数名的长度

而在Windows下的函数名修饰规则比较复杂,了解一下即可:

image

C++程序如何调用C写的程序

我们以C++程序调用C程序写的静态库

和C程序调用C++程序写的库为例

准备两个项目:StackC项目:stack.c文件和stack.h文件

CallClib项目和一个test.cpp文件

StackC项目中的stack.c生成C程序写的静态库,stack.h作为头文件

然后CallClib项目用于测试调用C静态库,其中test.cpp要用到 栈这个数据结构,测试调用C写的栈

  1. 首先创建静态库

    把.c文件包装成静态库:image

    然后生成解决方案

    就可以在上一层目录的Debug文件夹里找到生成的.lib文件(静态库)image

image

  1. 然后Cpp程序调用C的库

    image

    此时 cpp程序 想要用C语言写的库 从而调用栈

    如何用?

    1. 首先包含头文件 让编译器知道 是存在这样的函数的

      利用相对路径或者绝对路径,把库目录下的.h文件包含

      image

      注意 不要有中文

      头文件包含了这些函数的声明,此时编译器是知道 存在这些函数的,认识这些函数名,可以编译成功。不过要去别的.o文件或者库去找 ,此时生成解决方案报的错误是链接错误 image

      因为此时没有.o文件 也没有别的库 自然找不到这些函数的地址(注意这些函数是经过函数名修饰之后的函数)

      然后我们就需要引入库,让编译器去库中找

    2. 引入库

      image

      image

      image

      image

      填写库的名称

      这就是告诉编译器 可以去这个库的符号表里去找上面那些函数的地址

      然后编译一下 :image

      虽然我们已经配置了,但是还会出现错误,为什么??

      因为这是cpp程序,包含头文件之后,头文件中声明的函数名会按照cpp的函数名修饰规则进行修饰,然后以修饰后的函数名去别的地方去找,但是此时静态库是C语言写的,C语言的函数名修饰之后 还是原来的函数名 所以静态库的符号表中的函数的名字 都是原来的函数名,就找不到对应的函数名的地址 出现链接错误

      我们可能会怀疑是不是没导入成功(验证是否成功导入),只需要把库的.c文件 改成.cpp

      然后重新生成解决方案,这时候这个库就是用cpp编译的。发现可以成功(可以自行验证)

      那么如何用cpp程序调用C写的库呢?

      我们只需要让cpp程序以c的函数命名规则去解读不就好了:

      解决:因为有时候在c++工程中需要将某些函数按照c的风格去编译,这样c和c++的项目都可以使用: 在函数前面加extern "C", 意思就是告诉c++的编译器把该函数按照C的风格来编译。(因为c++是兼容c的,c++认识c的规则)

      image

      这样就是把 Stack.h头文件中的所有函数 都按照C的风格来编译 ,这样编译的时候,修饰后的函数名 就和 修饰前一样了。 可以发现生成解决方案成功image

C程序如何调用C++写的程序

c程序调用c++的程序

准备两个项目:StackCPP项目:stack.cpp文件和stack.h文件

CallCPPLib项目和一个test.c文件

StackC项目中的stack.cpp生成C++程序写的静态库,stack.h作为头文件

然后CallCPPLib项目用于测试调用C++静态库,其中test.c要用到 栈这个数据结构,测试调用C++写的栈

把.cpp程序包装成静态库

过程和上面一样

image

在目标目录下生成静态库:

image

然后用一个C程序 包含头文件,链接用cpp写的静态库

生成解决方案:

image

与刚才面临类似的问题,会出现链接错误

这里是为什么呢?是因为C程序中展开Stack.h的头文件之后,按照C语言的函数修饰规则去修饰函数名,函数名修饰后还是原来的函数名

但是导入的库是c++写的,所以库里的函数名字经过修饰都不是原来的名字了,自然在链接的时候找不到对应函数的地址

那么这时候链接不上怎么改?

可以改c程序吗?可以在c程序中用extern "C++" 让C程序认识C++的规则吗?不可能,C是不认识C++的规则的

所以只能改库

把C++库里写的Stack.h文件以C的规则去编译,这样生成的库里面的函数名字就是按照C的规则去修饰的名字

image

然后重新生成解决方案

这时候回到C程序中生成解决方案image

发现还是错误,这是为什么?

这是因为 在C程序中也引用Stack.h文件了

但是注意:Stack.h文件中是存在extern "C"命令的

但是C语言不支持这样的语法啊! 所以才会提示非法的字符串

所以就会报错!

解决方法:条件编译

如果当前是cpp文件,那么就执行extern "C"这个指令

否则不执行这个语句: __cplueplue 是C++的宏标识

如c++primer里所说:image

image

此时,只有在.cpp文件中引用Stack.h的时候,extern"C"才会起作用

这样重新生成解决方案 可以通过image

extern "C"的注意问题

如果使用C程序调用C++写的库,如果C++库中存在函数重载,也会出错

因为C不支持函数重载,所以要给C调用就不要写重载 或者 只extern没用重载的函数

标签:调用,函数,程序,C++,修饰,探究,cpp,重载
From: https://www.cnblogs.com/iiianKor/p/17982010

相关文章

  • C/C++可变参数函数
    C可变参数typedefchar*va_list;voidva_start(va_listap,prev_param);typeva_arg(va_listap,type);voidva_end(va_listap);//32位机器对int大小向上取整,64位机器对int64大小向上取整,因为参数在栈中传递都要对齐#define_INTSIZEOF(n)((sizeof(n)+si......
  • C++类和对象-对象特性(2)
    一.构造函数的分类及调用两种分类方式:按参数分为:有参构造和无参构造按类型分为:普通构造和拷贝构造三种调用方式:括号法显示法隐式转换法二.拷贝构造函数调用时机拷贝构造函数调用时机C++中拷贝构造函数调用时机三种情况使用一个已经创建完毕的对象来初始化一个新......
  • C++学习笔记
    C++学习笔记(1)预编译、编译、链接预编译(Preprocessing)cppreference中:GPT这么说:C++预编译是指在编译阶段之前对代码进行的一系列预处理操作。预编译的目的是为了将代码中的预处理指令和宏展开,以及进行一些其他的预处理操作。预处理指令包括以井号(#)开头的指令,如#include、#......
  • C++日志记录库spdlog
    镜像库https://gitee.com/yctxkj/spdlog.gitspdlog是基于C++11实现的一款纯头文件的日志管理库(git地址:https://github.com/gabime/spdlog,API说明:https://spdlog.docsforge.com/v1.x/1.quickstart/):配置特别简单,仅包含头文件即可;写日志方式简单明了;可实现自动按日期创建日志文......
  • KY85 二叉树C++
    递归判断当前节点和n的关系就好了。如果小于等于n那就是存在。#include<iostream>usingnamespacestd;intcount(inti,intn){if(i>n)return0;returncount(2*i,n)+count(2*i+1,n)+1;}intmain(){intn,m;while(cin>>m>>n){if(n==0)......
  • 一类垂直弦相关的平面解析几何问题探究
    题目一题文今有椭圆\(\Gamma:\frac{x^2}{a^2}+\frac{y^2}{b^2}=1,(a>b>0)\)点\(A,B\in\Gamma\)并且\(OA\botOB\),求\(O\)到\(AB\)的射影\(H\)的轨迹方程。解法一考虑设\[OA:y=kx,OB:y=-\frac{x}{k}\]先考察斜率存在的情形。联立\(OA,\Gamma\)......
  • 【C++入门到精通】C++入门 —— 类和对象(拷贝构造函数、赋值运算符重载、const成员函
    编辑一、前言二、拷贝构造函数⭕拷贝构造函数概念⭕拷贝构造函数的特点⭕拷贝构造函数的几种类型三、赋值运算符重载⭕运算符重载概念⭕赋值运算符重载⭕前置++和后置++重载四、const成员函数⭕const成员函数概念⭕常量成员函数需要满足的特点⭕常量成员函数有利条件⭕const常量的......
  • 【C++进阶】function和bind及可变模板参数
     文章目录1.function和bind1.1function使用方法1.2bind2.可变模板参数2.1可变模板参数函数2.2可变模板参数的展开 1.function和bindC++中的function和bind是为了更方便地进行函数对象的封装和调用而设计的。function是一个通用的函数对象容器......
  • 【C++入门到精通】C++入门 —— 类和对象(初始化列表、Static成员、友元、内部类、匿名
     目录一、初始化列表⭕初始化列表概念⭕初始化列表的优点⭕使用场景⭕explicit关键字二、Static成员⭕Static成员概念......
  • 4147:汉诺塔问题(Tower of Hanoi)C++
    递归C和C++一样,就写个C++了。#include<iostream>usingnamespacestd;voidmove(intn,chara,charb,charc){if(n<=0)return;move(n-1,a,c,b);cout<<n<<":"<<a<<"->"<<c<<'\n�......