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

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

加号运算符重载

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

cpp 复制代码
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

cpp 复制代码
// 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;

左移运算符重载

没有与这些操作数匹配的 运算符

只能利用全局函数重载左移运算符

cpp 复制代码
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; 

递增运算符重载

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

+= 重载

cpp 复制代码
int a = 10;
cout<< ++a<<endl; // 11 前置递增运算符 先运算再表达式
cout<< a <<endl;   // 11

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

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

cpp 复制代码
#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后的对象属性中有指向同一个堆区地址,后面手动释放的时候会产生堆区内存重复释放的问题。程序崩了。
需要用深拷贝解决,需要在堆区重新开辟一块内存,放相同的数据。后续释放对象,就不会有冲突了。
cpp 复制代码
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;
cpp 复制代码
// 重载赋值运算符 这里上面传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是返回指向的内容
}
// 之后再进行释放就不会有堆区重复释放的问题了

完善的

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

关系运算符重载

cpp 复制代码
#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;
}

函数调用运算符重载

函数调用运算符()也可以重载。

由于重载后使用的方式非常像函数的调用,因此成为仿函数。

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

cpp 复制代码
#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;
}

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

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

相关推荐
UQWRJ1 小时前
菜鸟教程R语言一二章阅读笔记
开发语言·笔记·r语言
朝朝又沐沐4 小时前
算法竞赛阶段二-数据结构(36)数据结构双向链表模拟实现
开发语言·数据结构·c++·算法·链表
YGY Webgis糕手之路4 小时前
OpenLayers 综合案例-轨迹回放
前端·经验分享·笔记·vue·web
逝雪Yuki4 小时前
Leetcode——287. 寻找重复数
c++·leetcode·二分查找·双指针·环形链表
遇见尚硅谷5 小时前
C语言:*p++与p++有何区别
c语言·开发语言·笔记·学习·算法
李永奉6 小时前
C语言-流程控制语句:for循环语句、while和do…while循环语句;
c语言·开发语言·c++·算法
老虎06276 小时前
JavaWeb(苍穹外卖)--学习笔记13(微信小程序开发,缓存菜品,Spring Cache)
笔记·学习·微信小程序
打码农的篮球6 小时前
STL——list
开发语言·c++·list
C++ 老炮儿的技术栈7 小时前
在 Scintilla 中为 Squirrel 语言设置语法解析器的方法
linux·运维·c++·git·ubuntu·github·visual studio
@蓝莓果粒茶8 小时前
LeetCode第350题_两个数组的交集II
c++·python·学习·算法·leetcode·职场和发展·c#