当前位置: 首页 > news >正文

C++ :运算符重载

运算符重载:

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

 

运算符的重载实际是一种特殊的函数重载,必须定义一个函数,并告诉C++编译器,当遇到该重载的运算符时调用此函数。这个函数叫做运算符重载函数,通常为类的成员函数

定义运算符重载函数的一般格式
返回值类型 类名::operator重载的运算符(参数表)  {.......}

operator是关键字,它与重载的运算符一起构成函数名。因函数名的特殊性,C++编译器可以将这类函数识别出来

加号运算符重载: 

作用:实现两个自定义数据类型相加的运算

1.成员函数实现 + 号运算符重载

 

#include <iostream>
using namespace std;//加号运算符重载class Person
{
public://1 成员函数实现 + 号运算符重载Person operator+(Person& p){Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}int m_A;int m_B;
};void test01() {Person p1;p1.m_A = 10;p1.m_B = 10;Person p2;p2.m_A = 10;p2.m_B = 10;Person p3 = p1 + p2;cout <<  "p3.m_A = " << p3.m_A<< endl;cout <<  "p3.m_B = " << p3.m_B<< endl;
}int main() {test01();system("pause");return 0;
}

(2).全局函数重载:

class Person
{
public://1 成员函数实现 + 号运算符重载/*Person operator+(Person& p){Person temp;temp.m_A = this->m_A + p.m_A;temp.m_B = this->m_B + p.m_B;return temp;}*/int m_A;int m_B;
};//2.全局函数重载+号
Person operator+(Person& p1, const Person& p2)
{Person temp;temp.m_A = p1.m_A + p2.m_A;temp.m_B = p1.m_B + p2.m_B;return temp;
}//3 运算符重载 可以发生函数重载
Person operator+(Person& p1, int num)
{Person temp;temp.m_A = p1.m_A + num;temp.m_B = p1.m_B + num;return temp;
}void test01() {Person p1;p1.m_A = 10;p1.m_B = 10;Person p2;p2.m_A = 10;p2.m_B = 10;//成员函数方式Person p3 = p2 + p1; //相当于 p2.operaor+(p1)Person p3 = p1 + p2;//全局函数重载本质调用:Person p3 = operator+(p1, p2);Person p3 = p1 + p2;//cout <<  "p3.m_A = " << p3.m_A<< endl;cout <<  "p3.m_B = " << p3.m_B<< endl;
}int main() 
{test01();system("pause");return 0;
}

2.左移运算符重载

作用:可以输出自定义数据类型
#include <iostream>
using namespace std;//左移运算符重载class Person
{friend ostream& operator<<(ostream& cout, Person& p);public:Person(int a, int b){this->m_A = a;this->m_B = b;}
private:
//成员函数 实现不了  p << cout 不是我们想要的效果//void operator<<(Person& p){//}int m_A;int m_B;
};//全局函数实现左移重载
ostream& operator<<(ostream& cout, Person &p)  //本质 operator<<(cout,p) 简化cout<<p
{cout << "m_A = " << p.m_A << " m_B = " << p.m_B;return cout;
}void test01()  
{Person p(10,10);//p.m_A = 10;//p.m_B = 10;cout << p<<"hello world"<<endl; //链式编程   输出一个对象就能打印他的属性
} int main() 
{test01();system("pause");return 0;
}

总结:重载左移运算符配合友元可以实现输出自定义数据类型

3.递增运算符重载

作用: 通过重载递增运算符,实现自己的整型数据
总结:前置递增返回引用,后置递增返回值
先写一个输出整形数据的:

class MyInteger 
{friend ostream& operator<<(ostream& cout, MyInteger myint);
public:MyInteger() {m_Num = 0;}private:int m_Num;
};//重载左移运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{cout << myint.m_Num << endl;    //全局函数想要访问私有属性return cout;
}
void test01()
{MyInteger myint;cout << myint << endl;}int main() 
{test01();system("pause");return 0;
}

输出结果: 0

 

class MyInteger 
{friend ostream& operator<<(ostream& cout, MyInteger myint);
public:MyInteger() {m_Num = 0;}//重载前置++运算符 返回引用为了一直对一个数据进行递增操作MyInteger& operator++(){//先++m_Num++;//再返回return *this;}//重载后置++运算符  //int代表占位参数,可以用于区分前置和后置递增MyInteger operator++(int){//先记录当时结果MyInteger temp = *this;//后递增m_Num++;//最后将记录结果做返回return temp;}private:int m_Num;
};//重载左移运算符
ostream& operator<<(ostream& cout, MyInteger myint)
{cout << myint.m_Num << endl;    //全局函数想要访问私有属性return cout;
}
void test01()
{MyInteger myint;cout << ++myint << endl;}void test02()
{MyInteger myint;cout << myint++ << endl;cout << myint << endl;
}int main() 
{// test01();test02();system("pause");return 0;
}

4.赋值运算符重载: 

c++ 编译器至少给一个类添加 4 个函数
1. 默认构造函数 ( 无参,函数体为空 )
2. 默认析构函数 ( 无参,函数体为空 )
3. 默认拷贝构造函数,对属性进行值拷贝
4. 赋值运算符 operator=, 对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
class Person
{
public:Person(int age){m_Age = new int(age);  //把数据创建在堆区 }~Person(){if (m_Age != NULL){delete m_Age;m_Age = NULL;}}                           //释放堆区内存  int* m_Age;};void test01()
{Person p1(18);Person p2(20);p2 = p1;    //赋值操作cout << "p1的年龄为:" << *p1.m_Age << endl;cout << "p2的年龄为:" << *p2.m_Age << endl;
}int main()
{test01();system("pause");return 0;
}

上示代码会崩;

 崩溃原因:堆区内存重复释放

 

解决方法: 

 示例如下:

class Person
{
public:Person(int age){m_Age = new int(age);  //把数据创建在堆区 }~Person(){if (m_Age != NULL){delete m_Age;m_Age = NULL;}}                           //释放堆区内存  //重载 赋值运算符    (编译器默认提供) 是一个浅拷贝的操作,创建在堆区d的属性,就会出现堆区内存重复释放Person& operator=(Person &p)  //返回引用{//编译器提供的代码是浅拷贝//m_Age = p.m_Age;//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝if (m_Age != NULL){delete m_Age;m_Age = NULL;}//提供深拷贝 解决浅拷贝的问题m_Age = new int(*p.m_Age);//返回自身  为了实现连等return *this;}int* m_Age;        //创建在堆区};void test01()
{Person p1(18);Person p2(20);Person p3(30);p3 = p2 = p1; //赋值操作cout << "p1的年龄为:" << *p1.m_Age << endl;cout << "p2的年龄为:" << *p2.m_Age << endl;cout << "p3的年龄为:" << *p3.m_Age << endl;}int main()
{test01();system("pause");return 0;
}

拷贝构造函数 与赋值重载函数的区别。
总结: 浅赋值与深复制。
什么时候使用深拷贝和深赋值。
在类型设计中,使用动态内存或使用内核对象时,必须重新实现拷贝构造函数和赋值重载 

 5.关系运算符重载:

作用: 重载关系运算符,可以让两个自定义类型对象进行对比操作
如下图,对于内置类型来说,编译器可以正常运行,但是对于自定义类型来说,程序会出现崩溃现象。
class Person
{
public:Person(string name, int age)   //用构造函数进行赋初值{m_Name = name;m_Age = age;}string m_Name;int m_Age;};void test01()
{Person p1("Tom", 18);Person p2("Tom", 18);if (p1 == p2)  、//err{cout << "a和b相等" << endl;}else{cout << "a和b不相等" << endl;}
}int main()
{test01();system("pause");return 0;
}

对于== 

重载关系运算符,可以让两个自定义类型对象进行对比操作 

class Person
{
public:Person(string name, int age)   //用构造函数进行赋初值{m_Name = name;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;}}string m_Name;int m_Age;};void test01()
{Person p1("Tom", 18);Person p2("Tom", 18);if (p1 == p2)   //err 需要重载=={cout << "p1和p2相等" << endl;}else{cout << "p1和p2不相等" << endl;}
}int main()
{test01();system("pause");return 0;
}

 对于!= 

class Person
{
public:Person(string name, int age)   //用构造函数进行赋初值{m_Name = name;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;}}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()
{Person p1("Tom", 18);Person p2("Jerry", 18);if (p1 == p2)   //err 需要重载=={cout << "p1和p2相等" << endl;}else{cout << "p1和p2不相等" << endl;}if (p1 != p2)   //err 需要重载=={cout << "p1和p2是不相等" << endl;}else{cout << "p1和p2相等" << endl;}
}int main()
{test01();system("pause");return 0;
}

运行结果:

 

6.函数调用运算符重载

函数调用运算符 () 也可以重载
由于重载后使用的方式非常像函数的调用,因此称为仿函数
仿函数没有固定写法,非常灵活
//打印输出类
class MyPrint
{
public://重载函数调用运算符 也称为仿函数void operator()(string test)  //()代表了函数名 (string test) 形参列表{cout << test << endl;}
};void MyPrint02(string test)
{cout << test << endl;
}void test01()
{MyPrint myPrint;myPrint("hello world");//由于使用起来非常类似于函数调用,因此称为仿函数MyPrint02("hello world");      //函数调用
}//仿函数非常灵活 没有固定的写法  依照需求 写对应的仿函数
//加法类class MyAdd  //实现两数相加
{
public:int operator()(int num1,int num2){return num1 + num2;}
};void test02()
{MyAdd myadd;   //先创建对象int ret = myadd(100, 100);cout << "ret = " << ret << endl;//匿名对象调用  类型加小括号cout << "MyAdd()(100,100) = " << MyAdd()(100, 100) << endl;
}int main() 
{test01();//test02();system("pause");return 0;
}

运算符重载函数的总结:


1、运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。
2、当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。
3、单目运算符“++”和“"存在前置与后置问题.

前置“++"格式为:
返回类型 类名::operator++(){......}
而后置“++"格式为:
返回类型 类名::operator++(int){......}
后置“++”中的参数int仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。

4、C++中只有极少数的运算符不允许重载

重载运算符有以下几种限制:
不可臆造新的运算符.
不能改变运算符原有的优先级、结合性和语法结构,不能改变运算符操作数的个数.
运算符重载不宜使用过多
重载运算符含义必须清楚,不能有二义性 

相关文章:

C++ :运算符重载

运算符重载&#xff1a; 运算符重载概念&#xff1a;对已有的运算符重新进行定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型 运算符的重载实际是一种特殊的函数重载&#xff0c;必须定义一个函数&#xff0c;并告诉C编译器&#xff0c;当遇到该重载的运算符…...

如何跑通跨窗口渲染:multipleWindow3dScene

New 这是一个跨窗口渲染的示例&#xff0c;用 Three.js 和 localStorage 在同一源&#xff08;同产品窗口&#xff09;上跨窗口设置 3D 场景。而这也是本周推特和前端圈的一个热点&#xff0c;有不少人在争相模仿它的实现&#xff0c;如果你对跨窗口的渲染有兴趣&#xff0c;可…...

flutter-web中使用js工具类

文章目录 为什么要调用js1. flutter-web1. 引入js web/index.html2. 创建工具js web/CryptoEnc.js3. 创建对应的lib/js/js_interop.dart4. 由于引入的js是针对web平台的&#xff0c;所以引入需要做引入处理5. 使用 2. Android1. 引入依赖2. index.html3. dart引用 为什么要调用…...

@ResponseBody详解:用于响应体响应数据

RestFul风格或者是web阶段接触过的异步请求&#xff0c;都需要把数据转换成Json放入响应体中。 ResponseBody的作用其实是将java对象转为json格式的相应内容 使用 RequestMapping注解时&#xff0c;Spring会将返回值解析为视图路径&#xff0c;然后跳转路径返回对应的视图页面…...

【Python百练——第3练】矩形类及操作

&#x1f490;作者&#xff1a;insist-- &#x1f490;个人主页&#xff1a;insist-- 的个人主页 理想主义的花&#xff0c;最终会盛开在浪漫主义的土壤里&#xff0c;我们的热情永远不会熄灭&#xff0c;在现实平凡中&#xff0c;我们终将上岸&#xff0c;阳光万里 ❤️欢迎点…...

【C语言学习疑难杂症】C语言中数组存储时为什么从低地址到高地址

在C语言中&#xff0c;数组的存储从低地址到高地址是有其历史原因的。这种设计主要是为了与计算机系统的内存组织方式相一致。 在计算机系统中&#xff0c;内存通常按照字节进行编址&#xff0c;地址从低到高递增。数组在内存中是连续存储的&#xff0c;因此数组的第一个元素&…...

Linux:查看端口占用的进程

命令 netstat -tunlp可以从图中看到&#xff0c;端口被那个进程占用&#xff0c;对应进程的pid是多少。...

医美店会员管理系统预约小程序作用是什么

医美在美业中占据着一定地位&#xff0c;爱美使然和经济独立、悦己消费下&#xff0c;不少女性会前往医美机构做脸部整容、嫩肤补水等服务&#xff0c;如美容院一样都是具备本地外地属性的&#xff0c;因此在如今互联网盛行下&#xff0c;商家需要借势线上破解难题及增强生意效…...

Linux 正则表达式

内容概述 1 文本编辑工具之神 VIM 1.1 vi 和 vim 简介 在 Linux 中我们经常编辑修改文本文件&#xff0c;即由 ASCII Unicode 或者其他编码的纯文字的文件。之前介绍过的 nano&#xff0c;实际工具中我们使用更为专业&#xff0c;功能强大的工具 文本编辑种类&#xff1a; vi…...

C语言面试之数组指针上篇

C语言数组是C语言中重要的数据结构之一&#xff0c;它用于存储一组相同类型的数据。数组在C语言中是以连续的内存空间来存储的&#xff0c;每个数组元素都是一个变量&#xff0c;占据一定的内存空间&#xff0c;数组元素之间是紧密相邻的。 一、数组的定义 在C语言中&#xff0…...

LinkWeChat,唯一以开源为核心的SCRM

LinkWeChat是国内首个基于企业微信的开源SCRM&#xff0c;在集成了企微强大的开放能力的基础上&#xff0c;进一步升级拓展灵活高效的客户运营能力及多元化精准营销能力&#xff0c;让客户与企业之间建立强链接&#xff0c;帮助企业提高客户运营效率&#xff0c;强化营销能力&a…...

Android textView 显示: STRING_TOO_LARGE

在Android中&#xff0c;字符串资源的长度限制是32KB&#xff0c;getString()方法返回的字符串资源的大小超过这个限制&#xff0c;就会抛出STRING_TOO_LARGE 这个错误。 我本地的临界值是&#xff1a;32.3 KB (33,090 字节) 小于等于33090时&#xff0c;能正常显示&#xff…...

用HeidiSQL在MySQL中创建新的数据库

用有权限的用户登录&#xff1a; 右键单击&#xff0c;选择&#xff1a; 输入要创建的数据库名称&#xff0c;然后点击“确定”&#xff1a; 刷新下&#xff0c;就看到新创建的数据库了&#xff1a; 在新创建的数据库中&#xff0c;就可以做其它操作了&#xff0c;例如…...

纯前端实现导入excel数据

准备工作 - 下载 xlsx npm install xlsx下面直接上代码&#x1f447; <template><div><input type"file" accept".xlsx, .xls" change"handleClick"></div> </template><script langts setup> import * a…...

Matlab下载许可证文件 教程(在账号有许可证的前提下)

文章目录 Part.I IntroductionPart.II 许可证文件过期解决方案Chap.I 使用 Internet 自动激活Chap.II 在不使用 Internet 的情况下手动激活 Part.I Introduction 本文主要介绍&#xff0c;在 Mathwork 账号有许可证的前提下&#xff0c;下载许可证的操作流程。 好久没有用 Mat…...

AI 绘画 | Stable Diffusion 电商模特

前言 上一篇文章讲到如何给人物更换背景和服装。今天主要讲电商模特,就是服装电商们的固定服装产品图片如何变成真人模特穿上的固定服装产品效果图。如果你掌握了 《AI 绘画 | Stable Diffusion 人物 换背景|换服装》,这篇文章对你来说,上手会更轻松。 教程 提取服装蒙版…...

vue面试题整理(1.0)

一、对MVC&#xff0c;MVP&#xff0c;MVVM的理解 三者都是项目的架构模式&#xff08;不是类的设计模式&#xff09;&#xff0c;即&#xff1a;一个项目的结构&#xff0c;如何分层&#xff0c;不同层负责不同的职责。 1.MVC MVC的出现是用在后端&#xff08;全栈时代&…...

PyQt基础_007_ 按钮类控件QCombox

import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import *class ComboxDemo(QWidget):def __init__(self, parentNone):super(ComboxDemo, self).__init__(parent)self.setWindowTitle("combox 例子") self.resize(300, 90) …...

Android Wifi的扫描机制

Android Wifi 的扫描场景分为下面四种情况&#xff1a; 1.亮屏情况下&#xff0c;在Wifi settings界面&#xff0c;固定扫描&#xff0c;扫描时间为10s。 2.亮屏情况下&#xff0c;在非Wifi settings界面&#xff0c;二进制指数退避扫描&#xff0c;退避&#xff1a;interval…...

类 —— 友元、常/静态成员函数

类 类的大小 和结构体大小求法一致。但需注意&#xff0c;普通空类也会占用 1 字节大小&#xff0c;因为普通空类可以实例化对象。 而 抽象空类占 4 字节&#xff08;32 位机中&#xff09;&#xff0c;因为抽象空类中含有虚指针&#xff08;含有虚函数的非抽象空类同理&am…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

内存分配函数malloc kmalloc vmalloc

内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销&#xff0c;平衡网络负载&#xff0c;延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...