当前位置: 首页 > 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.dtcms.com/a/301695.html

相关文章:

  • 机器学习—线性回归
  • 深入解析MySQL索引页结构与B+Tree实现原理
  • ubuntu18.04解压大的tar.gz文件失败
  • 【Java系统接口幂等性解决实操】
  • java--WebSocket简单介绍
  • 2.安装CUDA详细步骤(含安装截图)
  • Dataloader的使用
  • 对抗攻击-知识点
  • HCIE学习之路:MSTP实现负载均衡实验
  • 全方位评测:11款主流指标平台优劣分析
  • [BSidesCF 2019]Kookie
  • 【测试报告】玄机抽奖系统(Java+Selenium+Jmeter自动化测试)
  • MyBatis-Plus 通用 Service(IService)详解与实战
  • Mybatis Plus 多数据源
  • 【LeetCode 热题 100】51. N 皇后——回溯
  • WiFi Mouse PC端 v1.7.2 官方中文版
  • GIF图像格式
  • 【RAG技术权威指南】从原理到企业级应用实践
  • Git Commit 生成与合入 Patch 指南
  • 《关于matplot中绘制图像中文字体乱码问题》
  • AWS免费套餐全面升级:企业降本增效与技术创新解决方案
  • 物联网发展:从概念到应用的演变历程
  • vue3报错:this.$refs.** undefined
  • 【INT范围提取字符串数字为正数】2022-8-29
  • Linux文件系统(三)
  • Java常用日志框架介绍
  • 【笔记】菲克定律与连续性方程详述
  • 【测试报告】博客系统(Java+Selenium+Jmeter自动化测试)
  • 【Milvus合集】1.Milvus 的核心概念(collection、field、index、partition、segment)
  • 【lucene】向量搜索底层文件关系梳理