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

黑马程序员C++核心编程笔记--类和对象--运算符重载

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

加号运算符重载

因为C++引入了自定义的类型,所以当两个自定义的类型需要做运算的时候,就需要运算符重载。


class Person
{
public:int m_A;int m_B;}Person P1;P1.m_A = 10;P1.m_B = 20;Person P2;P2.m_A = 20;P2.m_B = 30;Person P3 = P1 + P2  //如果不重载运算符,编译器不知道如何运算自定义的类型

下面是加号运算的重载 编译器统一命名的
1.通过成员函数进行运算符重载(在一个对象内部,所以一个使用this,一个使用(*传的时候使用P->,&传的时候使用P.)
2.通过全局函数进行运算符重载
对象相加返回对象 所以operator前面加void

// 1.通过成员函数进行运算符重载
Person P3 = P1.operator+(P2)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;
}
// 使用的时候可以用 简化
Person P3 = P1+P2;// 2.通过全局函数进行运算符重载
Person operator+ (Person &p1, Person &p2)
{Person temp;temp.m_A = p1->m_A + p2.m_A;temp.m_B = p1->m_B + p2.m_B;return temp;
}Person p3 = operator+(p1,p2);
// 简化为
Person p3 = p1 + p2;// 运算符重载的同时也支持函数重载
Person operator+ (Person &p1, int num)
{Person temp;temp.m_A = p1->m_A + num;temp.m_B = p1->m_B + num;return temp;
}Person P3 = P2+10;

左移运算符重载

没有与这些操作数匹配的 运算符
只能利用全局函数重载左移运算符

int a = 10;
cout << a <<endl;  //左移运算符直接输出整形Person p;
p.m_A = 10;
p.m_B = 10;
cout<< p <<end;  // 左移运算符无法直接输出自定义的类// 通常不会使用成员函数重载左移运算符,因为 operator<<(cout) 简化完为 << cout,想要的是cout<<  成员函数运算符重载规则 【p 运算符 传的参数】
// 只能利用全局函数重载左移运算符 全局函数运算符重载规则 【传的参数1 运算符 传的参数2】
void operator<<(ostream &cout,Person &p)
{cout << p.m_A << p.m_B <<endl;
}cout<<p;  // 重载完之后就可以直接输出p对象的属性// 但是想继续换行,也就是 cout<<p<<endl; 会报错,因为上面定义的重载返回是void// 链式编程思想需要每次返回的都是相同类型的 每次返回都是cout类型才行
//链式调用需要返回对象引用,所以是ostream&,而不是直接ostream
ostream& operator<<(ostream &cout,Person &p)
{cout << p.m_A << p.m_B <<endl;return cout;	
}// 可以链式调用了
cout<<p<<endl; 

递增运算符重载

通过重载递增运算符,实现自己的整形数据

+= 重载

int a = 10;
cout<< ++a<<endl; // 11 前置递增运算符 先运算再表达式
cout<< a <<endl;   // 11int b = 10;
cout<< b++<<endl; // 10 后置递增运算符 先表达式再运算
cout<< b <<endl;   // 11

实现自己的递增运算符,重载

#include <iostream>
using namespace std;class MyInteger {friend ostream& operator<<(ostream& cout, MyInteger myInt) ;public:MyInteger() {m_Num = 0;}// 重载前置++运算符MyInteger& operator++() {   // 返回引用是为了链式调用 //仅返回值的话  链式调用不能对同一个对象进行操作,使得链式调用返回的值是对的,但是对象本身没有进行操作,单独输出对象的值,就是原来的值。// 先进行++运算m_Num++;// 再自身做返回return *this;}// 重载后置++运算符   int为了实现函数重载 是占位参数MyInteger operator++(int) {    // 后置递增返回值,因为temp是局部对象,当前函数执行结束后temp对象就会销毁,所以返回值不能是引用// 后置递增与前置递增不同,先记录当时结果MyInteger temp = *this; // 创建一个局部对象,让这个对象等于 this的解引用 就是自身的值// 但是这里两种自增,多个访问// 后进行递增运算m_Num++;// 最后将记录结果做返回return temp;}
private:int m_Num;
};// 重载 << 运算符
ostream& operator<<(ostream& cout,  MyInteger myInt) {cout << myInt.m_Num;return cout;
}void test01() {MyInteger myInt;cout << ++myInt << endl;cout << myInt << endl;
}void  test02() {MyInteger myInt;cout << myInt++  << endl;cout << myInt << endl;
}int main() {test01();test02();return 0;
}

赋值运算符重载

c++编译器至少给一个类添加4个函数
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性进行值拷贝
赋值运算符 operator=, 对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

(默认浅拷贝)也就是copy后的对象属性中有指向同一个堆区地址,后面手动释放的时候会产生堆区内存重复释放的问题。程序崩了。
需要用深拷贝解决,需要在堆区重新开辟一块内存,放相同的数据。后续释放对象,就不会有冲突了。

class Person {
public:int *m_Age;Person(int age) {// 将年龄数据开辟到堆区m_Age = new int(age);}// 赋值运算符重载Person& operator=(Person& p) {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}
// 当开辟在堆区的数据需要释放时~Person() {if (m_Age != NULL) {delete m_Age;m_Age = NULL;}}};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 age 是Person的构造函数括号法初始化
// 这里operator是重载传入参数,代表运算符前后的类型关系 p=p 所以传person参数
Person &operator=(Person &p)
{// 编译器提供的是浅拷贝如下:// m_Age  = p.m_Age;// 我们要进行深拷贝,所以要开辟一块新的堆内存m_Age = new int(*p.m_Age);  // m_Age 是指针类型 所以要解引用取值 开辟heap内存值m_Age放进堆区return *this;  // this是指针 指向对象本身 *this解引用 相当于返回对象本身// return this; 是返回指针 return *this是返回指向的内容
}
// 之后再进行释放就不会有堆区重复释放的问题了

完善的

Person &operator=(Person &p) // 这里返回值 返回自身 一是符合赋值 二是符合链式调用 返回相同对象的引用
{
// 先判断是否有属性在堆区,如果有就先释放干净,然后进行深拷贝
// 如果不先判断一下,直接就进行赋值 容易出现原内存无法释放的问题等等。
if(m_Age != NULL)
{delete m_Age;m_Age = NULL;
}// 再进行赋值m_Age = new int(*p.m_Age);  // 深拷贝return *this;
}

在这里插入图片描述

关系运算符重载

#include <iostream>
using namespace std;class Person {
public:string m_Name;int m_Age;Person(string name, int age) {this ->m_Age = age;this ->m_Name = name;};bool operator==(Person &p) {if (this ->  m_Age == p.m_Age && this -> m_Name == p.m_Name) {return  true;}else return  false;}bool operator!=(Person &p) {if (this ->  m_Age == p.m_Age && this -> m_Name == p.m_Name) return false;else  return true;}
};void test01() {Person  p1("Tom", 10);Person  p2("Tom", 10);if (p1 == p2) {cout << "p1 和 p2 一样" << endl;}else cout << "p1 和 p2 不一样" << endl;if (p1 != p2) {cout << "p1 和 p2 不一样" << endl;}else  cout << "p1 和 p2 一样" << endl;
}int main() {test01();return 0;
}

函数调用运算符重载

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

#include <iostream>
using namespace std;class MyPrint {
public:void operator()(string text) {cout << text << endl;}};
void test01() {// 重载的()操作符也叫仿函数MyPrint myFunc;myFunc("hello world");
}class MyAdd {
public:int operator()(int v1, int v2) {return v1 + v2;}
};
void test02() {MyAdd myAdd;int ret = myAdd(10, 20);cout <<  ret << endl;// 匿名对象调用cout << MyAdd()(100, 100) <<  endl;
}int main() {test01();test02();return 0;
}

仿函数非常灵活,没有固定的写法

匿名函数对象,当前行执行完,立即被释放。

http://www.lryc.cn/news/601922.html

相关文章:

  • 2116. 判断一个括号字符串是否有效
  • AI技术革命:产业重塑与未来工作范式转型。
  • 2025第15届上海生物发酵展将于8月7号启幕
  • Python训练Day25
  • 入侵检测介绍
  • Linux之shell脚本篇(三)
  • 51核和ARM核单片机OTA实战解析(二)
  • 自然语言处理NLP (1)
  • Java 集合进阶:从 Collection 接口到迭代器的实战指南
  • 基于多智能体的任务管理系统架构设计与实现
  • 【DataWhale】快乐学习大模型 | 202507,Task08笔记
  • 卷积神经网络研讨
  • 墨者:SQL注入漏洞测试(布尔盲注)
  • 零基础,如何入手学习SAP?
  • Ashampoo Background Remover(照片去背景工具) v2.0.2 免费版
  • WSL切换网络模式
  • 持续优化Cypress自动化测试
  • CentOS 镜像源配置与 EOL 后的应对策略
  • MySQL进阶学习与初阶复习第四天
  • 【测试报告】思绪网(Java+Selenium+Jmeter自动化测试)
  • 自动出题与批改系统(数学题生成+OCR识别)
  • ABB机器人多任务详解
  • OpenLayers 综合案例-轨迹回放
  • java的break能加标签,return可以加标签吗
  • 83.删除排序链表中的重复元素
  • DeepCompare文件深度对比软件“.dpcp“工程项目文件功能深度解析
  • vector使用和模拟
  • 网络虚拟化:veth,bridge,network namespace与docker网络
  • ESP32学习-FreeRTOS队列使用指南与实战
  • 使用低级上位画图法理解在对磁盘空间进行容量分配时【低级单位上位至高级单位的换算】