C++ 运算符重载

目录

前言 

算术运算符重载

加号运算符

位运算符重载

左移运算符

自增自减运算符重载

前置++自增运算符

后置++自增运算符

赋值运算符重载

等号赋值运算符重

关系运算符重载

相等 

不等 

函数调用运算符重载

总结


前言 

在C++中,运算符重载是一种强大的特性,允许您重新定义已定义的运算符,以便它们适用于用户自定义类型。通过运算符重载,您可以编写自定义类型的运算符行为,使得用户自定义类型的对象可以像内置类型一样进行运算。

算术运算符重载

  • +:执行加法操作,通常用于数字或字符串的拼接。
  • -:执行减法操作,通常用于数字之间的减法运算。
  • *:执行乘法操作,通常用于数字之间的乘法运算。
  • /:执行除法操作,通常用于数字之间的除法运算。

通过重载这些算术运算符,您可以定义自定义类型对象之间的加减乘除操作。这使得用户自定义类型的对象可以像内置类型一样进行算术运算。

加号运算符

#include <iostream>
using namespace std;class Person {
public:Person() {};Person(int a, int b){this->m_A = a;this->m_B = b;}// 成员函数实现 + 号运算符重载Person operator+(const Person& p) {Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}public:int m_A;int m_B;
};// 全局函数实现 + 号运算符重载
//Person operator+(const Person& p1, const Person& p2) {
//	Person temp(0, 0);
//	temp.m_A = p1.m_A + p2.m_A;
//	temp.m_B = p1.m_B + p2.m_B;
//	return temp;
//}// 运算符重载 可以发生函数重载,就是可以根据参数的不同去分别调用不同的operator运算符重载函数
Person operator+(const Person& p2, int val)
{Person temp;temp.m_A = p2.m_A + val;temp.m_B = p2.m_B + val;return temp;
}void test() {Person p1(10, 10);Person p2(20, 20);// 成员函数方式Person p3 = p2 + p1;  // 相当于 p2.operaor+(p1)cout << "mA:" << p3.m_A << " mB:" << p3.m_B << endl; // 30 30Person p4 = p3 + 10; // 相当于 operator+(p3,10)cout << "mA:" << p4.m_A << " mB:" << p4.m_B << endl; // 40 40}int main() {test();return 0;
}

位运算符重载

  • &:按位与运算符,对两个操作数逐位进行与运算。
  • |:按位或运算符,对两个操作数逐位进行或运算。
  • ^:按位异或运算符,对两个操作数逐位进行异或运算。
  • ~:按位取反运算符,对操作数逐位取反。
  • <<:左移运算符,将操作数的二进制位向左移动指定的位数。
  • >>:右移运算符,将操作数的二进制位向右移动指定的位数。

通过重载位运算符,您可以定义自定义类型对象之间的按位操作。这使得自定义类型的对象可以进行位级运算。

左移运算符

#include <iostream>
using namespace std;
#include <string>/* 作用:可以输出自定义数据类型 */class Person {friend ostream& operator<<(ostream& out, Person& p); // 全局函数做Person类的友元,可以访问类的私有成员public:Person(int a, int b){this->m_A = a;this->m_B = b;}//成员函数 实现不了  p << cout 不是我们想要的效果//void operator<<(Person& p){//}private:int m_A;int m_B;
};//全局函数实现左移重载
//ostream对象只能有一个,故使用 &引用接收,不会创建新对象
ostream& operator<<(ostream& out, Person& p) { out << "a:" << p.m_A << " b:" << p.m_B;return out; // 需返回cout引用,达到链式调用的效果
}void test() {Person p1(10, 20);cout << p1 << "hello world" << endl; // a:10 b:20hello world  链式编程  
}int main() {test(); // 重载左移运算符配合友元可以实现输出自定义数据类型return 0;
}

自增自减运算符重载

通过重载自增和自减运算符,您可以定义自定义类型对象的自增和自减操作。这允许您使用++--来递增或递减对象的值。

前置++自增运算符

前置++,必须返回引用,不能返回值,因为要对同一原对象操作

        解释一下 test01函数中的 cout << ++myInt << endl。首先,递增运算符的重载函数++myInt会被执行,将myInt的值加1,并返回递增后的对象引用。然后,左移运算符的重载函数operator<<会接受递增后的对象作为参数,并将其输出到标准输出流cout中。

#include <iostream>
using namespace std;
#include <string>class MyInteger {friend ostream& operator<<(ostream& out, MyInteger myint);public:MyInteger() {m_Num = 0;}//前置++,必须返回引用,不能返回值,因为要对同一原对象操作MyInteger& operator++() { 		//先++this->m_Num++;//再返回return *this;}private:int m_Num;
};/* 左移重载 */
ostream& operator<<(ostream& out, MyInteger myint) {out << myint.m_Num;return out;
}// 前置++ 先++ 再返回
void test01() {class MyInteger myInt;cout << ++myInt << endl; // 1 递增重载、左移重载cout << myInt << endl; // 1 左移重载
}int main() {test01();return 0;
}

后置++自增运算符

后置++, 只能返回值,因为创建的是临时对象,函数销毁,引用就不对了

#include <iostream>
using namespace std;
#include <string>/*前置递增返回引用,后置递增返回值
*/class MyInteger {friend ostream& operator<<(ostream& out, MyInteger myint);public:MyInteger() {m_Num = 0;}//后置++, 只能返回值,因为创建的是临时对象,函数销毁,引用就不对了MyInteger operator++(int) { // int 代表后置++ //先返回MyInteger temp = *this; // 记录当前本身的值,然后让本身的值加1,但是返回的是以前的值,达到先返回后++;this->m_Num++;return temp;  // 此时返回的还是++前的 0}private:int m_Num;
};/* 左移重载 */
ostream& operator<<(ostream& out, MyInteger myint) {out << myint.m_Num;return out;
}//后置++ 先返回 再++
void test02() {MyInteger myInt;cout << myInt++ << endl; // 0cout << myInt << endl; // 1
}int main() {test02();return 0;
}

赋值运算符重载

  • =:将右操作数的值赋给左操作数。
  • +=:将左操作数与右操作数相加,并将结果赋给左操作数。
  • -=:将左操作数与右操作数相减,并将结果赋给左操作数。
  • *=:将左操作数与右操作数相乘,并将结果赋给左操作数。
  • /=:将左操作数与右操作数相除,并将结果赋给左操作数。

通过重载赋值运算符,您可以定义自定义类型的对象如何进行赋值操作。这使得对象可以通过使用等号和其他赋值运算符来进行赋值。

等号赋值运算符重

c++编译器至少给一个类添加4个函数

  1.  默认构造函数(无参,函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数,对属性进行值拷贝
  4. 赋值运算符 operator=, 对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

如果是浅拷贝,不做赋值重载深拷贝,那么每个类对象都会执行释放,但已释放的空间二次释放就会报错

class Person
{
public:Person(int age){// 将年龄数据开辟到堆区m_Age = new int(age);}// 重载赋值运算符 返回自身引用Person& operator=(Person &p){if (m_Age != NULL){delete m_Age;m_Age = NULL;}// 编译器提供的代码是浅拷贝//m_Age = p.m_Age;// 提供深拷贝 解决浅拷贝的问题m_Age = new int(*p.m_Age); // 各自指向各自的空间//返回自身return *this;}~Person() // 析构函数{if (m_Age != NULL){delete m_Age; // 如果是浅拷贝,不做赋值重载深拷贝,那么每个类对象都会执行释放,同一个堆空间二次释放就会报错m_Age = NULL;}}//年龄的指针int *m_Age;};void test01()
{Person p1(18);Person p2(20);Person p3(30);p3 = p2 = p1; // 连续赋值操作,前提必须返回自身引用cout << "p1的年龄为:" << *p1.m_Age << endl; // 18cout << "p2的年龄为:" << *p2.m_Age << endl; // 18cout << "p3的年龄为:" << *p3.m_Age << endl; // 18
}int main() {test01();//int a = 10;//int b = 20;//int c = 30;//c = b = a;//cout << "a = " << a << endl;//cout << "b = " << b << endl;//cout << "c = " << c << endl;return 0;
}

关系运算符重载

  • ==:检查两个值是否相等,返回布尔值。
  • !=:检查两个值是否不相等,返回布尔值。
  • >:检查左操作数是否大于右操作数,返回布尔值。
  • <:检查左操作数是否小于右操作数,返回布尔值。
  • >=:检查左操作数是否大于或等于右操作数,返回布尔值。
  • <=:检查左操作数是否小于或等于右操作数,返回布尔值。

通过重载这些关系运算符,您可以定义自定义类型对象之间的比较操作。这使得您可以使用关系运算符来比较自定义类型的对象,并根据需要定义其比较规则。

相等 

class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;};bool operator==(Person & p) // 关系重载,相等{if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return true;}else{return false;}}};void test01()
{//int a = 0;//int b = 0;Person a("孙悟空", 18);Person b("孙悟空", 18);if (a == b){cout << "a和b相等" << endl; // a和b相等}else{cout << "a和b不相等" << endl;}}int main() {test01();return 0;
}

不等 

#include <iostream>
using namespace std;
#include <string>/* 重载关系运算符,可以让两个自定义类型对象进行对比操作 */class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;};bool operator!=(Person & p) // 关系重载,不等{if (this->m_Name == p.m_Name && this->m_Age == p.m_Age){return false;}else{return true;}}string m_Name;int m_Age;
};void test01()
{//int a = 0;//int b = 0;Person a("孙悟空", 18);Person b("孙悟空", 18);if (a != b){cout << "a和b不相等" << endl;}else{cout << "a和b相等" << endl; // a和b相等}
}int main() {test01();return 0;
}

函数调用运算符重载

  • 函数调用运算符 ()  也可以重载
  • 由于重载后使用的方式非常像函数的调用,因此称为仿函数
  • 仿函数没有固定写法,非常灵活

class MyPrint
{
public:void operator()(string text) // 函数调用运算符重载函数,注意是在 operator后加 (),当前是打印值{cout << text << endl;}};
void test01()
{//重载的()操作符 也称为仿函数MyPrint myFunc;myFunc("hello world"); // hello world
}class MyAdd
{
public:int operator()(int v1, int v2) // 函数调用运算符重载函数,当前是返回值{return v1 + v2;}
};void test02()
{MyAdd add;int ret = add(10, 10);cout << "ret = " << ret << endl; // 20// 匿名对象调用  MyAdd()是匿名对象,执行完当前行就会销毁,MyAdd()(100, 100)是匿名对象调用重载函数cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}int main() {test01();test02();return 0;
}

总结

需要注意的是,虽然运算符重载提供了强大的功能,但滥用它可能会导致代码难以理解和维护。因此,对于每个运算符,都需要仔细考虑是否有必要进行重载,以及如何遵循最佳的代码设计和风格约定。

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

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

相关文章

数据可视化---柱状图

import matplotlib.pyplot as plt import numpy as npdef plot_bar_chart(data, labels, colorsNone, title"Bar Chart", xlabel"X-Axis", ylabel"Y-Axis"):"""绘制柱状图&#xff0c;并在柱子上显示数量和比例。:param data: 包…

pytest之allure测试报告03:allure动态自定义报告

1、测试用例模块中引入allure&#xff1a;import allure 2、yaml文件中定义添加title、story的值&#xff1a; 3、测试用例中读取调用。eg:allure.dynamic.title() 4、运行报告查看&#xff1a;成功动态展示yaml文件中配置的story、title

共同编辑文档功能实现(websocket)

目录 前言 websocket封装 wangeditor下载 共同编辑文档代码实现 HTML样式部分 JS部分 css部分 前言 功能&#xff1a;实现文档共同编辑功能&#xff0c;可以实时接收到其他人的信息 思路&#xff1a;先调用接口获取相应的数据进行渲染&#xff0c;然后通过webSocket建…

当 Sealos 遇上区块链

当 Sealos 遇上区块链 拿着区块链技术不一定是去发币&#xff0c;很多业务系统也适合用这些技术&#xff0c;比如做个统一支付系统&#xff0c;积分系统等&#xff0c;可以做为一家公司的金融基础设施&#xff0c;或支付中台。拿链的技术去做有很多好处&#xff1a; 高可用&a…

【图的应用一:最小生成树】- 用 C 语言实现普里姆算法

目录 一、最小生成树 二、普里姆算法的构造过程 三、普里姆算法的实现 一、最小生成树 假设要在 n 个城市之间建立通信联络网&#xff0c;则连通 n 个城市只需要 n - 1 条线路。这时&#xff0c;自然会考虑这样一个问题&#xff0c;如何在最节省经费的前提下建立这个通信…

机器学习之无监督学习

聚类&#xff1a;发掘纵向结构的某种模式信息&#xff0c;某些x属于相同的分布或者类别 特征学习&#xff1a;发掘横向结构的某种模式信息&#xff0c;每一行都可以看成是一种属性或特征 密度估计&#xff1a;发掘底层数据分布&#xff0c;x都是从某个未知分布p(x)采出来的&a…

(四)STM32 操作 GPIO 点亮 LED灯 / GPIO工作模式

目录 1. STM32 工程模板中的工程目录介绍 2. GPIO 简介 3. GPIO 框图剖析 1&#xff09;保护二极管及上、下拉电阻 2&#xff09; P-MOS 管和 N-MOS 管 3&#xff09;输出数据寄存器 3.1&#xff09;ODR 端口输出数据寄存器 3.2&#xff09;BSRR 端口位设置/清除寄存器 4&a…

【Java代码审计】目录穿越篇

【Java代码审计】目录穿越篇 1.Java中的目录穿越2.目录穿越漏洞审计3.Java中目录穿越漏洞修复 1.Java中的目录穿越 目录穿越漏洞产生的本质是路径可控&#xff0c;一旦涉及文件的读取问题便会涉及java.io.File类&#xff0c;因此在审计这类漏洞时可以优先查找java.io.File引用…

最强Pose模型RTMO开源 | 基于YOLO架构再设计,9MB+9ms性能完爆YOLO-Pose

实时多人在图像中的姿态估计面临着在速度和精度之间实现平衡的重大挑战。尽管两阶段的上下文方法在图像中人数增加时会减慢速度&#xff0c;但现有的单阶段方法往往无法同时实现高精度和实时性能。 本文介绍了RTMO&#xff0c;这是一个单阶段姿态估计框架&#xff0c;通过在YOL…

超文本传送协议HTTP

目录 HTTP简介&#xff1a; URL的格式&#xff1a; HTTP协议的特点&#xff1a; HTTP/1.0协议&#xff1a; HTTP/1.1协议&#xff1a; HTTP/2: HTTP代理服务器&#xff1a; HTTP的报文结构&#xff1a; 请求报文的特点&#xff1a; 响应报文的特点&#xff1a; Cook…

小 cookie,大作用:探索网站中的隐私追踪器(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

大华 DSS 数字监控系统 itcBulletin SQL 注入漏洞复现

0x01 产品简介 大华 DSS 数字监控系统是大华开发的一款安防视频监控系统,拥有实时监视、云台操作、录像回放、报警处理、设备管理等功能。 0x02 漏洞概述 大华 DSS存在SQL注入漏洞,攻击者 /portal/services/itcBulletin 路由发送特殊构造的数据包,利用报错注入获取数据库…

springboot——定时任务、异步任务

springboot——定时任务、异步任务 一、定时任务1、创建一个定时任务、时间配置文件2、springboot主程序开启定时任务 EnableScheduling3、使用 Scheduled 注解 二、定时任务、异步执行。1、springboot主程序开启异步任务 EnableAsync2、方法上增加 Async 注解&#xff0c;标识…

【JAVA日志框架】JUL,JDK原生日志框架详解。

前言 Java日志体系混乱&#xff1f;Java日志框架系列&#xff0c;清晰简洁整理好整个Java的日志框架体系。第一篇&#xff0c;JDK原生日志框架——JUL。 目录 1.概述 2.日志级别 3.配置 4.继承关系 1.概述 日志框架的核心问题&#xff1a; 日志是用来记录应用的一些运行…

nodejs+vue+微信小程序+python+PHP校园二手交易系统的设计与实现-计算机毕业设计推荐

(2)管理员 进行维护&#xff0c;以及平台的后台管理工作都依靠管理员&#xff0c;其可以对信息进行管理。需具备功能有&#xff1b;首页、个人中心、学生管理、物品分类管理、物品信息管理、心愿贴、系统管理、订单管理等功能。系统分成管理员控制模块和学生模块。 本系统有良好…

maven+spock

pom配置 话说JunitMockito的组合用起来是真难用&#xff0c;还是Spock的简单&#xff0c;尤其是参数化的测试。junit的Parameter是鸡肋&#xff0c;杂恶心&#xff1b;Theories用来也不爽。 <?xml version"1.0" encoding"UTF-8"?><project xm…

Spring容器中scope为prototype类型Bean的回收机制

文章目录 一、背景二、AutowireCapableBeanFactory 方法 autowireBean 分析三、Spring 容器中 scope 为 prototype 类型 Bean 的回收机制四、总结 一、背景 最近做 DDD 实践时&#xff0c;遇到业务对象需要交给 Spring 管理才能做一些职责内事情。假设账号注册邮箱应用层代码流…

Flask学习三:模型操作

ORM flask 通过Model操作数据库&#xff0c;不管你的数据库是MySQL还是Sqlite&#xff0c;flask自动帮你生成相应数据库类型的sql语句&#xff0c;所以不需要关注sql语句和类型&#xff0c;对数据的操作flask帮我们自动完成&#xff0c;只需要会写Model就可以了 flask使用对象关…

第十五章总结

一.输入/输出流 1.输入流 InputStrema类是字节输入流的抽象类&#xff0c;它是所有字节输入流的父类。 该类中所有方法遇到错误都会引发IOException异常。 read()方法&#xff1a;从输入流中读取数据的下一个字节。返回0~255的int字节值。如果因为已经到达流末尾而没有可用的…

【模块化】 js 模块化(CommonJS, AMD, UMD, CMD, ES6)

目录 js 的演变模块化1. CommonJS 规范commonJs伪代码⭐CommonJS优缺点 2. AMD 规范⭐AMD 优缺点 3. UMD 规范⭐UMD AMD CommonJS 4. CMD 规范⭐CMD 优缺点 5. ES6 模块化符号绑定⭐ESM 优缺点 AMD 和 CMD 的区别ES6 模块与 CommonJS 模块的差异参考 将介绍几种 js 模块化的规…