C++重载函数和重载运算符
liebian365 2024-12-27 16:17 24 浏览 0 评论
重载概念
C++可以在同一个作用域中对某个函数和运算符进行多个定义,这些分别成为函数重载和运算符重载。通俗地讲,就是同一个名称可以完成不同的功能,而编译器是通过参数类型进行匹配的。
在C++中,存在编译时的多态性与运行时的多态性。源程序->编译->链接->可执行文件,该过程中将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编(binding)。在编译过程中进行联编被称为静态联编(static binding);而在程序运行时才完成的联编被称为动态联编 (dynamic binding)。
静态联编(static binding):指系统在编译时就决定实现某一动作,要求在编译时就知道调用函数的全部信息。静态联编支持的多态性成为编译时多态性(静态多态性),通过函数重载(包括运算符重载)和模板实现。
动态联编(dynamic binding):指系统在运行时动态实现某一动作。采用这种联编方式,一直要到程序运行时才能确定调用哪个函数。 动态联编支持的多态性成为运行时的多态性(动态多态性),通过虚函数实现。其优点是提供了更好的灵活性、问题抽象性和程序易维护性。
函数重载(Overroad)
2.1 函数重载概念
函数重载是指同一作用域内,可以定义一组具有相同函数名称,不同参数列表的函数,这些函数被称为重载函数;虽然函数名称相同,但和不同参数搭配时执行的内容就不同。函数重载的判断标准为:1) 参数个数不同;2) 参数类型不同;3) 参数顺序不同。函数重载至少满足其中一个条件,但注意的是不能将函数返回值作为函数重载的判断标准。
When two or more different declarations are specified for a single name in the same scope, that name is said to overloaded. By extension, two declarations in the same scope that declare the same name but with different types are called overloaded declarations. Only function declarations can be overloaded; object and type declarations cannot be overloaded. ——摘自《ANSI C++ Standard. P290》
重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。举个例子:
#include <iostream>
using namespace std;
void Max(int a, int b)
{
cout << "Max 1" << endl;
}
void Max(double a, double b)
{
cout << "Max 2" << endl;
}
void Max(double a, double b, double c)
{
cout << "Max 3" << endl;
}
int main()
{
Max(3, 4); //调用 void Max(int, int)
Max(2.4, 6.0); //调用 void Max(doubleA double)
Max(1.2, 3.4, 5); //调用 void Max(double, double, double)
Max(1, 2, 3); //调用 void Max(double, double, double)
//Max(3, 1.5); //出错:二义性
return 0;
}
可以看出,编译器根据调用 Max 函数的语句所给的实参的个数和类型,可以找到完全匹配的函数。而最后一个Max(3, 1.5)编译会出错,因为两个实参一个是整型,一个是实数型。如果将整型自动转换成实数型,那么看来应该调用 void Max(double, double) 这个函数;可是如果将实数型去尾自动转换为整型,那么调用 void Max(int, int) 似乎也说得过去。
2.2 编译器调用重载函数的准则
- 将所有同名函数作为候选者;
- 尝试寻找可行的候选函数;
- 精确匹配实参;
- 通过默认参数能够匹配实参;
- 通过默认类型转换匹配实参;
- 匹配失败;
- 最终寻找到的可行候选函数不唯一,则出现二义性,编译失败;
- 无法匹配所有候选者,函数未定义,编译失败。
2.3 函数重载遇上函数默认参数
举个例子:
#include <iostream>
using namespace std;
int func(int a, int b, int c = 0)
{
return a * b* c;
}
int func(int a, int b)
{
return a + b;
}
int func(int a)
{
return a;
}
int main()
{
int c = 0;
c = func(1, 2); // 存在二义性,调用失败,编译不能通过
printf("c = %d\n", c);
printf("Press enter to continue ...");
return 0;
}
其中func(1, 2)存在二义性,可以看成缺省函数,可以调用int func(int a, int b)函数,也可以调用int func(int a, int b, int c = 0)函数。
2.4 函数重载和函数指针结合
当使用重载函数名对函数指针进行赋值时,根据重载规则挑选与函数指针参数列表一致的候选者,严格匹配候选者的函数类型与函数指针的函数类型。举个例子:
#include <iostream>
using namespace std;
typedef int(*PFUNC)(int a); // int(int a)
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()
{
int c = 0;
PFUNC p = func;
c = p(1);//调用int func(int x)
printf("c = %d\n", c);
printf("Press enter to continue ...");
return 0;
}
2.5 引入函数重载的原因
- 在C语言中没有重载机制,需要对具有不同参数类型或不同参数个数,但具有同一种功能的函数定义多个名称,对编程来说比较麻烦。
- 类的构造函数跟类名相同。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!
- 操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!
2.6 C++是如何做到函数重载的
int max(int a,int b) 映射为_Z3maxii、double max(double a,double b) 映射为_Z3maxdd。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)。不同的编译器有不同的重命名方式,同时函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。
2.7 编译器解析重载函数调用过程
编译器实现调用重载函数解析机制的时候,肯定是首先找出同名的一些候选函数,然后从候选函数中找出最符合的,如果找不到就报错。下面介绍一种重载函数解析的方法:编译器在对重载函数调用进行处理时,由语法分析、C++文法、符号表、抽象语法树交互处理,交互图大致如下:
这个四个解析步骤所如下:
- 由匹配文法中的函数调用,获取函数名;
- 获得函数各参数表达式类型;
- 语法分析器查找重载函数,符号表内部经过重载解析返回最佳的函数;
- 语法分析器创建抽象语法树,将符号表中存储的最佳函数绑定到抽象语法树上。
运算符重载
3.1 运算符重载概念
与函数重载类似,运算符也可以重载,如图所示是运算符重载的例子:
重载的运算符是带有特殊名称的函数,函数名是由关键operator和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。例如:
Box operator+(const Box&);
声明加法运算符用于把两个Box对象相加,返回最终的Box对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果定义上面的函数为类的非成员函数,那么需要为每次操作传递两个参数,例如:
Box operator+(const Box&, const Box&);
举个例子, 对象作为参数进行传递,对象的属性使用 this 运算符进行访问:
#include <iostream>
using namespace std;
class Box
{
public:
double get_volume(void)
{
return length * width * height;
}
void set_param(double len, double wid, double hei)
{
length = len;
width = wid;
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.width = this->width + b.width;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double width; // 宽度
double height; // 高度
};
// 程序的主函数
int main()
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
Box1.set_param(6.0, 7.0, 8.0);
Box2.set_param(12.0, 13.0, 14.0);
// Box1 的体积
volume = Box1.get_volume();
cout << "Volume of Box1 : " << volume << endl;
// Box2 的体积
volume = Box2.get_volume();
cout << "Volume of Box2 : " << volume << endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.get_volume();
cout << "Volume of Box3 : " << volume << endl;
return 0;
}
3.2 可重载运算符/不可重载运算符
3.3 运算符重载准则
- 只能对已有运算符进行重载,不允许用户自己定义新的运算符。
- 运算符重载是针对新类型数据的实际需要,不建议改变原运算符的含义。(例如将+运算符重载为进行减法运算)。
- 运算符重载不能改变运算符的操作对象(即操作数)的个数。
- 运算符重载不能改变运算符原有的优先级。
- 运算符重载不能改变运算符原有的结合性。
- 运算符重载函数的参数至少有一个是类对象(或其引用)。目的是防止用户修改用于标准类型数据的运算符性质。
- 运算符重载函数可以是普通函数、类成员函数、类的友元函数。
- 赋值运算符“="可以不必用户进行重载。
相关推荐
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
-
大家都知道go语言生态并没有什么好的gui开发框架,“能用”的一个手就能数的清,好用的就更是少之又少。今天为大家推荐一个go的gui库go-fltk。它是通过cgo调用了c++的fltk库,性能非常高...
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
-
这几天老毛桃整理了几个微型Linux发行版,准备分享给大家。要知道可供我们日常使用的Linux发行版有很多,但其中的一些发行版经常会被大家忽视。其实这些微型Linux发行版是一种非常强大的创新:在一台...
- codeblocks和VS2019下的fltk使用中文
-
在fltk中用中文有点问题。英文是这样。中文就成这个样子了。我查了查资料,说用UTF-8编码就行了。edit->Fileencoding->UTF-8然后保存文件。看下下边的编码指示确...
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
-
FLTK(FastLightToolkit)是一个轻量级的跨平台GUI库,特别适用于开发需要快速、高效且简单界面的应用程序。本文将介绍Python中的FLTK库,包括其特性、应用场景以及如何通过代...
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
-
IT之家1月29日消息,去年6月份,中科院大学教授、中科院计算所研究员包云岗,发布了开源高性能RISC-V处理器核心——香山。近日,包云岗在社交平台晒出图片,香山芯片已流片,回片后...
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
-
预计Linux5.13将初步支持苹果SiliconM1处理器,不过完整的支持工作可能还需要几年时间才能完全完成。虽然Linux已经可以在苹果SiliconM1上运行,但这需要通过一系列的补丁才能...
- Ubuntu系统下COM口测试教程(ubuntu port)
-
1、在待测试的板上下载minicom,下载minicom有两种方法:方法一:在Ubuntu软件中心里面搜索下载方法二:按“Ctrl+Alt+T”打开终端,打开终端后输入“sudosu”回车;在下...
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
-
很多年轻人毕业即失业、面试总是不如意、薪酬不满意、在家躺平。“就业难”该如何应对,参加培训是否能改变自己的职业走向,在湖北,有哪些嵌入式软件工程师培训怎么选值得推荐?粤嵌科技在嵌入式培训领域有十几年经...
- 新阁上位机开发---10年工程师的Modbus总结
-
前言我算了一下,今年是我跟Modbus相识的第10年,从最开始的简单应用到协议了解,从协议开发到协议讲解,这个陪伴了10年的协议,它一直没变,变的只是我对它的理解和认识。我一直认为Modbus协议的存...
- 创建你的第一个可运行的嵌入式Linux系统-5
-
@ZHangZMo在MicrochipBuildroot中配置QT5选择Graphic配置文件增加QT5的配置修改根文件系统支持QT5修改output/target/etc/profile配置文件...
- 如何在Linux下给zigbee CC2530实现上位机
-
0、前言网友提问如下:粉丝提问项目框架汇总下这个网友的问题,其实就是实现一个网关程序,内容分为几块:下位机,通过串口与上位机相连;下位机要能够接收上位机下发的命令,并解析这些命令;下位机能够根据这些命...
- Python实现串口助手 - 03串口功能实现
-
串口调试助手是最核心的当然是串口数据收发与显示的功能,pzh-py-com借助的是pySerial库实现串口收发功能,今天痞子衡为大家介绍pySerial是如何在pzh-py-com发挥功能的。一、...
- 为什么选择UART(串口)作为调试接口,而不是I2C、SPI等其他接口
-
UART(通用异步收发传输器)通常被选作调试接口有以下几个原因:简单性:协议简单:UART的协议非常简单,只需设置波特率、数据位、停止位和校验位就可以进行通信。相比之下,I2C和SPI需要处理更多的通...
- 同一个类,不同代码,Qt 串口类QSerialPort 与各种外设通讯处理
-
串口通讯在各种外设通讯中是常见接口,因为各种嵌入式CPU中串口标配,工业控制中如果不够还通过各种串口芯片进行扩展。比如spi接口的W25Q128FV.对于软件而言,因为驱动接口固定,软件也相对好写,因...
- 嵌入式linux为什么可以通过PC上的串口去执行命令?
-
1、uboot(负责初始化基本硬bai件,如串口,网卡,usb口等,然du后引导系统zhi运行)2、linux系统(真正的操作系统)3、你的应用程序(基于操作系统的软件应用)当你开发板上电时,u...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- go语言也可以做gui,go-fltk让你做出c++级别的桌面应用
- 旧电脑的首选系统:TinyCore!体积小+精简+速度极快,你敢安装吗
- codeblocks和VS2019下的fltk使用中文
- FLTK(Fast Light Toolkit)一个轻量级的跨平台Python GUI库
- 中科院开源 RISC-V 处理器“香山”流片,已成功运行 Linux
- Linux 5.13内核有望合并对苹果M1处理器支持的初步代码
- Ubuntu系统下COM口测试教程(ubuntu port)
- 湖北嵌入式软件工程师培训怎么选,让自己脱颖而出
- 新阁上位机开发---10年工程师的Modbus总结
- 创建你的第一个可运行的嵌入式Linux系统-5
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)