函数重载和运算符重载

函数重载和运算符重载

  • 一、函数重载
  • 二、运算符重载
    • (一)==运算符重载
    • (二)=运算符重载
    • (三)++运算符重载(前置和后置)
    • (四)+、+=运算符重载
    • (五)<<运算符重载
  • 三、总结

重载分为两种情况,一种是函数重载,一种是运算符重载,它们常用来处理实现功能类似但是数据类型不同的问题。

一、函数重载

函数重载:在C++中,允许在同一作用域中声明多个同名函数,但是这些同名函数的形参列表(形参列表指的是:参数个数或类型或类型顺序)不同。满足这样的同名函数就构成重载。
注意
1、返回值不作为函数重载的判断条件,所以返回值可同可不同。
2、当调用重载的函数时,编译器会根据形参和实参,优先调用最匹配的函数、其次次之,如果没有匹配的函数会报错(下面例子有讲解)。
下面是函数重载的例子:

#include <iostream>
using namespace std;
// 1、参数类型不同
int Add(int x, int y)
{cout << "Add(int x, int y)" << endl;return x + y;
}double Add(double x, double y)
{cout << "Add(double x, double y)" << endl;return x + y;
}double Add(int x, double y)
{cout << "Add(int x, double y)" << endl;
}// 2、参数个数不同
void f()
{cout << "f()" << endl;
}
void f(int a)
{cout << "f(int a)" << endl;
}// 3、参数类型顺序不同
void f(int a, char b)
{cout << "f(int a,char b)" << endl;
}void f(char b, int a)
{cout << "f(char b, int a)" << endl;
}int main()
{	//下面三次Add函数调用,编译器会优先查找参数最匹配的函数,显然这里都有最匹配的,则各自调用最匹配的。如果这里只是实现了三个Add中的任意一个,那么下面三次调用都会调用实现的那一个,虽然没有最匹配的,但是可以调用(可能会报警告)。Add(10, 20); //调用第一个Add函数Add(10.1, 20.2); //调用第二个Add函数Add(10, 20.5); //调用第三个Add函数f();f(10);f(10, 'a');f('a', 10);return 0;
}

二、运算符重载

运算符重载和函数重载类似,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号:比如:operator+
函数原型:返回值类型 operator操作符(参数列表)
注意:
1、不能重载没有的运算符,比如:operator#
2、用于内置类型的运算符,不能改变其含义,比如:+号,不能将它改变成减法
3、运算符函数的参数类型必须有一个是类类型
4、运算符函数的参数个数和运算符的操作数个数是相等的,比如:==号的操作数是两个,重载的时候参数个数也必须是两个
5、当有两个操作数的运算符时,运算符函数第一个参数是左操作数,第二个参数是右操作数
6、当运算符重载是类成员函数的时候,形参看起来比实际参数少一个,因为类成员函数的第一个参数为隐藏的this
7、有五个运算符不能重载,分别为:.*,::,sizeof, ?:, .
下面通过一个例子实现常见的几种运算符重载

(一)==运算符重载

假设我们有下面这样一个日期类,我现在的需求是用日期类实例化两个对象,然后比较两个对象是否相等。此时需要重载运算符==,如果不重载,无法比较,因为运算符==不能比较两个自定义类型。

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day;
};// 重载==运算符,判断日期类对象是否相等(这是一个全局函数)
bool operator==(const Date& d1, const Date& d2)
{//当运算符重载成全局函数的时候,在函数体内访问类成员变量的时候,类成员变量权限必须是公有的,否则函数体内无法访问//但是这也带来了一个问题,类成员的安全性,一旦公有,谁都可以访问篡改类成员变量//解决方法有两种:1、通常将运算符重载成类成员函数(通常采用的方法) 2、将运算符重载(全局函数)声明成类的友元函数return (d1._year == d2._year && d1._month == d2._month && d1._day == d2._day);
}int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);//当重载成全局函数的时候有两种调用方式//第一种:跟普通函数的调用一样if (operator==(d1, d2))cout << "相等" << endl;elsecout << "不相等" << endl;//第二种:跟内置类型判断是否相等写法一样,这样写的话看起来更符合运算符的写法//你可以理解为编译器会将d1==d2处理成第一种写法if (d1 == d2)cout << "相等" << endl;elsecout << "不相等" << endl;return 0;
}

为了解决上述类的成员变量安全性问题,我们将运算符重载成类的成员函数

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//这里看起来少了一个参数,实际上是第一个参数(左操作数)隐藏起来了//函数原型可以看成:bool operator==(Date* this, const Date& d2)bool operator==(const Date& d2){return (this->_year == d2._year && this->_month == d2._month && this->_day == d2._day);}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);//当重载成类的成员函数的时候有两种调用方式//第一种:跟类的成员函数调用一样cout << d1.operator==(d2) << endl;//第二种:跟内置类型使用一样,可以理解为编译器会将是d1==d2处理成第一种cout << (d1 == d2) << endl;return 0;
}

(二)=运算符重载

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//赋值运算符返回值应是Date类型,因为要构成链式反应,支持连续赋值//比如int类型,a = b = c,可看成:a = (b == c)),其次返回引用是为了提高效率Date& operator=(const Date& d2){if (this != &d2) //防止自己给自己赋值{this->_year = d2._year;this->_month = d2._month;this->_day = d2._day;}return *this;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);Date d3;d3 = d1;return 0;
}

注意(下面都是类中知识的注意事项):
1、赋值运算符必须重载成类的成员函数,因为赋值运算符如果不显式在类中实现,编译器会生成一个默认的,此时用户再在类外自己重载一个全局的赋值运算符,在调用的时候会出现二义性,故赋值运算符重载只能是类的成员函数。
2、默认提供的赋值运算符函数,只是简单的值拷贝(将一个对象中成员变量按字节为单位拷贝给另一个成员的变量),如果成员变量中涉及动态开辟的资源,那么用默认的赋值运算符函数可能会出现浅拷贝问题。如果类中未涉及到资源管理,赋值运算符是否实现都可以,一旦涉及到资源管理则必须要实现。

(三)++运算符重载(前置和后置)

前置++和后置++都是一元运算符,重载时怎么区分它们呢?为了能正确重载,c++规定,后置++运算符函数多一个int类型的参数,调用时,由编译器自动传递。

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//前置++//返回值必须返回引用,因为要构成链式反应,支持连续++//比如:++(++a),这里的结果是a增加了两次,括号外面的++也是对a自身的++,所有要返回引用,也就是返回它的自身//不然返回临时对象的话,临时对象增加之后销毁了,而它本身没有增加Date& operator++() {this->_day += 1; //这里只是简单的加1,没有做每个月天数处理return *this;}//后置++//返回值不是引用,因为返回的是临时变量,所有不能使用引用,并且后置++不支持连续++Date operator++(int) {Date tmp = *this;this->_day += 1;return tmp;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);Date d3 = d1++; //d3是(2024,2,29),d1变成了(2024,2,30)Date d4 = ++(++d2); //d4是(2023,2,31),d2变成了(2023,2,31)return 0;
}

前置–和后置–,跟前置++和后置++处理方法一样

(四)+、+=运算符重载

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//返回值是Date,因为要返回它们计算的和//其次返回值不是引用,因为返回值是临时对象,加法的左操作数是不会改变的,所以返回临时对象Date operator+(const int day) {Date tmp = *this;tmp._day += day;return tmp;}//同上Date operator+(const Date& d2){Date tmp = *this;tmp._year += d2._year;tmp._month += d2._month;tmp._day += d2._day;return tmp;}//返回值是Date,因为要返回它们相加的和//返回引用,因为要和其他运算符构成链式反应,比如:++(a += 1),括号计算结束之后,前置++还会导致a增加一次,所以必须返回引用Date& operator+=(const int day){this->_day += day;return *this;}//同上Date& operator+=(const Date& d2){this->_year += d2._year;this->_month += d2._month;this->_day += d2._day;return *this;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);Date d3 = d2 + 1; //(2023, 2, 30)Date d4 = d2 + d3; //(4046, 4, 59)d3 += 1; //(2023, 2, 31)d4 += d3; //(6069, 6, 90)return 0;
}

-、-=运算符重载和+、+=运算符重载类似

(五)<<运算符重载

如果我们直接打印Date类型的数据(如:cout<<d1),是无法打印的,因为Date类型是自定义类型。根本原因还是ostream类中没有重载能处理Date类型的<<运算符函数,因为你是自定义类型,我不知道你要怎样打印,所以我无法重载这样的运算符函数。ostream中只重载了内置数据类型,平时使用的cout是ostream的一个对象,所以可以通过cout<<来打印数据。

下图是ostream类中<<运算符的重载和cout的声明

在这里插入图片描述
在这里插入图片描述

标准输出运算符必须重载成全局函数,并声明为类的友元函数,如果重载在类中,也能使用,但是用法和正常用法会不一样,所以为了统一,定义在类外。
首先看一下重载成类成员函数,会是怎样的情况:

#include <iostream>
using namespace std;class Date //日期类
{
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}//重载在类中//形参类型是ostream,因为我们打印Date类型数据终究还是要依靠标准输出流cout,只不过我们使用cout自定义如何打印,所以要传参cout//返回值是ostream,因为要构成链式反应(重载再类中的链式反应有缺陷),如cout<<a<<b,可以理解为(cout<<a)<<b,先是cout<<a,结果返回cout,再cout<<b。返回引用是因为效率ostream& operator<<(ostream& out){out << "(" << this->_year << ", " << this->_month << ", " << this->_day << ")";return out;}private:int _year;int _month;int _day;
};int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);//这种用法是错误,因为左操作数是运算符函数的第一个参数,而第一个参数是Date类型对象cout << d1 << endl;//正确用法,但是这种用法和我们平常用法相反d1 << cout << endl; //链式反应的缺陷d2 << d1 << cout; //错误,因为是从左到右运算,先是d2<<d1,无法被识别d2 << cout << 1 << "+" << "2" << endl; //正确(可以看出来每次都是第一次操作是反的:d2<<cout,后面都正常了)return 0;
}

重载成全局函数并声明为Date类的友元函数,友元函数意思就是我是Date的好朋友,我可以在我的函数体内访问你的私有成员变量了

#include <iostream>
using namespace std;class Date //日期类
{friend ostream& operator<<(ostream& out, const Date& d); //声明为友元函数
public:Date(int year = 2000, int month = 1, int day = 1) //默认构造{_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};ostream& operator<<(ostream& out, const Date& d)
{out << "(" << d._year << ", " << d._month << ", " << d._day << ")";return out;
}int main()
{Date d1(2024, 2, 29);Date d2(2023, 2, 29);cout << d1 << "," << d2 << endl;return 0;
}

三、总结

无论是函数重载还是运算符重载,它们都是满足三个条件:
1、在同一作用域中
2、函数名相同
3、参数列表不同

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/499837.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【玩转408数据结构】线性表——双链表、循环链表和静态链表(线性表的链式表示 下)

知识回顾 在前面的学习中&#xff0c;我们已经了解到了链表&#xff08;线性表的链式存储&#xff09;的一些基本特点&#xff0c;并且深入的研究探讨了单链表的一些特性&#xff0c;我们知道&#xff0c;单链表在实现插入删除上&#xff0c;是要比顺序表方便的&#xff0c;但是…

为什么推荐使用ref而不是reactive

为什么推荐使用ref而不是reactive 局限性问题&#xff1a; reactive本身存在一些局限性&#xff0c;可能会在开发过程中引发一些问题。这需要额外的注意力和处理&#xff0c;否则可能对开发造成麻烦。数据类型限制&#xff1a; reactive声明的数据类型仅限于对象&#xff0c;而…

javaweb学习(day05-TomCat)

一、介绍 1 官方文档 地址: https://tomcat.apache.org/tomcat-8.0-doc/ 2 WEB 开发介绍 2.1 WEB 在英语中 web 表示网/网络资源(页面,图片,css,js)意思&#xff0c;它用于表示 WEB 服务器(主机)供浏览器访问的资源 2.2 Web 资源 WEB 服务器 ( 主机 ) 上供外界访问的 …

Spring八股 常见面试题

什么是Spring Bean 简单来说&#xff0c;Bean 代指的就是那些被 IoC 容器所管理的对象。我们需要告诉 IoC 容器帮助我们管理哪些对象&#xff0c;这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。 将一个类声明为 Bean 的注解有哪些? Com…

Docker Compose实战指南:让容器管理变得简单而强大

&#x1f9e8;个人主页&#xff1a;明明跟你说过 &#x1f6a9;欢迎&#x1f397;️点赞&#x1f638;关注❤️分享 &#x1f638;希望本文能够对您有所帮助&#xff0c;如果本文有不足之处&#xff0c;或您有更好的建议、见解&#xff0c;欢迎在评论区留下您的看法&#xff0c…

LIS(最长上升子序列, 合唱队形)

最长上升子序列 直接使用动态规划&#xff1a; 这个题目的关键就是在于我们选定一个数&#xff0c;然后利用这个数作为标准和这个数之前的所有数进行比较&#xff0c;如果比前面某一个数要大&#xff0c;那么就需要将这数自己本身的现存的最长长度与比较出来的数的最长加一&am…

Linux学习-C语言-运算符

目录 算术运算符&#xff1a; - * /:不能除0 %:不能对浮点数操作 &#xff1a;自增与运算符 i&#xff1a;先用再加 i:先加再用 --&#xff1a;自减运算符 常量&#xff0c;表达式不可以&#xff0c;--&#xff0c;变量可以 赋值运算符 三目运算符 逗号表达式 size…

OD(13)之Mermaid饼图和象限图

OD(13)之Mermaid饼图和象限图使用详解 Author: Once Day Date: 2024年2月29日 漫漫长路才刚刚开始… 全系列文章可参考专栏: Mermaid使用指南_Once_day的博客-CSDN博客 参考文章: 关于 Mermaid | Mermaid 中文网 (nodejs.cn)Mermaid | Diagramming and charting tool‍‌⁡…

解读人工智能的理论基石

1956年的一个夏天&#xff0c;在达特茅斯学院的一个小会议室里&#xff0c;一群充满好奇和野心的年轻科学家聚集在一起&#xff0c;他们有一个共同的梦想&#xff1a;创造能够模仿人类智能的机器。这不仅仅是科幻小说的情节&#xff0c;更是人工智能历史上一个真实的起点。从那…

react使用@reduxjs/toolkit和react-redux实现store状态管理

一、概述 reduxjs/toolkit和react-redux是用于在React应用中管理全局状态的工具库 1、reduxjs/toolkit&#xff1a; reduxjs/toolkit是Redux官方推荐的工具库&#xff0c;是对 Redux 的二次封装&#xff0c;它提供了一些便捷的API和工具&#xff0c;帮助开发者更快速地编写R…

使用Python,maplotlib绘制树型有向层级结构图

使用Python&#xff0c;maplotlib绘制树型有向层级结构图 1. 效果图2. 源码2.1 plotTree.py绘制层级结构及不同样式2.2 plotArrow.py 支持的所有箭头样式 参考 前俩篇博客介绍了 1. 使用Python&#xff0c;networkx对卡勒德胡赛尼三部曲之《群山回唱》人物关系图谱绘制 2. 使用…

uniapp实现-审批流程效果

一、实现思路 需要要定义一个变量, 记录当前激活的步骤。通过数组的长度来循环数据&#xff0c;如果有就采用3元一次进行选择。 把循环里面的变量【name、status、time】, 全部替换为取出的那一项的值。然后继续下一次循环。 虚拟的数据都是请求来的, 组装为好渲染的格式。 二…