一文详解C++操作符重载

308 篇文章 0 订阅
68 篇文章 4 订阅

一、运算符重载的基本概念

 运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型(运算符重载不能改变原来的寓意。不能改变基础类型寓意)

运算符重载只是一种语法上的方便,也就是它只是另一种函数调用方式

 在C++中可以定义一个处理类的新运算符,这种定义很像一个普通的函数定义,只是函数的名字由关键字operator及其紧跟的运算符组成,差别仅此而已,它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数

语法:定义重载的运算符就像定义函数,只是该函数的名字是operator@这里的@代表了被重载的运算符,函数的参数中参数个数取决于两个因素

二、加号运算符重载

2.1 写成全局函数

重载函数如果写成全局的,那么双目运算符左边的是第一个参数,右边是第二个参数

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

private:
	int id;
	int age;
};

// 全局重载
Maker operator+(Maker &p1, Maker &p2)
{
	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
	return t;
}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// 编译器看到两个对象相加  那么编译器会去找有没有叫operator+的函数
	Maker m3 = m + n;// 运算符重载

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{

	test01();
	return EXIT_SUCCESS;
}

2.2 写成成员函数

重载函数如果写成成员函数,那么双目运算符的左边就是this,右边是第一个参数

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator+(Maker& m)
	{
		Maker t(this->id + m.id,this->age + m.age);
		return t;
	}

private:
	int id;
	int age;
};

 全局重载
//Maker operator+(Maker &p1, Maker &p2)
//{
//	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
//	return t;
//}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// 编译器看到两个对象相加  那么编译器会去找有没有叫operator+的函数
	Maker m3 = m + n;// 运算符重载

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{

	test01();
	return EXIT_SUCCESS;
}

2.3 不同类的对象相加

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator+(Maker& m)
	{
		Maker t(this->id + m.id,this->age + m.age);
		return t;
	}

private:
	int id;
	int age;
};


class Student
{
public:
	Student()
	{
		this->mid = 0;
	}

	Student(int id)
	{
		this->mid = id;
	}


	int getID()
	{
		return this->mid;
	}
private:
	int mid;
};

 全局重载
//Maker operator+(Maker &p1, Maker &p2)
//{
//	Maker t(p1.getId() + p2.getId(), p1.getAge() + p2.getAge());
//	return t;
//}

void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// 编译器看到两个对象相加  那么编译器会去找有没有叫operator+的函数
	Maker m3 = m + n;// 运算符重载

	cout << m3.getId() << m3.getAge() << endl;
}

// 不同类的对象相加  全局重载
Maker operator+(Maker &m, Student &s)
{
	Maker t(m.getId() + s.getID(),20);
	return t;
}

void test02()
{
	Maker m(1, 9);
	Student n(2);
	Maker s = m + n;
	cout << s.getId() << s.getAge() << endl;
}

int main()
{
	test02();
	return EXIT_SUCCESS;
}

 几乎C中所有的运算符都可以重载,但是运算符重载的使用还是相当受限制,特别是不能使用C中当前没有意义的运算符,不能改变运算符优先级,不能改变运算符的参数个数

在这里插入图片描述

三、减号运算符重载

3.1 成员函数的形式

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// 编译器看到两个对象相加  那么编译器会去找有没有叫operator+的函数
	Maker m3 = m - n;// 运算符重载

	cout << m3.getId() << m3.getAge() << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

全局重载

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};


int operator-(Maker& m, int b)
{
	return m.getId() - b;
}


void test01()
{
	Maker m(1,2);
	Maker n(2,3);

	// 编译器看到两个对象相加  那么编译器会去找有没有叫operator+的函数
	int m3 = m - 5;// 运算符重载

	cout << m3 << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

四、左移运算符重载

4.1 全局函数

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};

// 如果要和endl一起使用 那么必须返回ostream对象
opstream& operator<<(ostream& out, Maker& m)
{
	cout << m.getId() << " " << m.getAge() << endl;
    return out;
}


void test01()
{
	Maker m(1,2);
	cout << m;// 重载左移运算符
}


int main()
{
	test01();
	return EXIT_SUCCESS;
}

注意:

  • cout是对象,<<是左移运算符
  • 重载左移运算符是为了直接打印对象
  • 形参和实参是一个对象
  • 不能改变库类中的代码
  • oatream中把拷贝构造函数私有化
  • 如果要把endl一起使用,那么必须返回ostream的对象

五、右移运算符重载

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {
	friend void operator>>(istream& in, Maker& m);
public:
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}

	// 写成成员函数 那么只需要一个参数  这个参数是加号的右边
	Maker operator-(Maker& m)
	{
		Maker t(this->id - m.id,this->age - m.age);
		return t;
	}

private:
	int id;
	int age;
};

// 将全局函数声明为友元函数 可以访问私有成员
void operator>>(istream& in, Maker& m)
{
	in >> m.id;
	in >> m.age;
}

void test01()
{
	Maker m(1,2);
	cin >> m;
	cout << m.getAge() << " " << m.getId() << endl;
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

六、赋值运算符重载

6.1 默认的赋值运算符重载函数进行了简单的赋值操作

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker()
	{
		this->id = 0;
		this->age = 0;

	}
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}


private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker m1;

	m1 = m;// 默认的赋值运算符重载函数进行了简单的赋值操作
	cout << m1.getId() << m1.getAge() << endl;
}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

6.2 当类有成员指针时,然后在构造函数中申请堆区空间,在析构函数中释放堆区空间

当类有成员指针时,然后在构造函数中申请堆区空间,在析构函数中释放堆区空间,会出现同一块空间释放两次,然后内存泄漏,所以要重写赋值运算符重载函数。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Student {


public:
	Student(const char* name)
	{
		pName = new char[strlen(name) + 1];// 申请堆区空间
		strcpy(pName,name);// 复制内容
	}

	// 重写赋值运算符重载函数
	Student& operator=(const Student& stu)
	{
		// 不确定this->pName 指向的空间是否能装下stu中的数据  所以先释放this->pNmae所指向的空间
		if (this->pName != NULL)
		{
			delete this->pName;
			this->pName = NULL;
		}

		// 申请空间 大小由stu进行决定
		this->pName = new char[strlen(stu.pName) + 1];
		strcpy(this->pName,stu.pName);

		// 返回对象本身
		return *this;
	}

	~Student()
	{
		if (pName != NULL)
		{
			delete[] pName;
			pName = NULL;
		}
	}

	void printName()
	{
		cout << pName << endl;
	}


private:
	char* pName;
};

void test01()
{
	Student s1("孙悟空");
	Student s2("hhhh");

	s1.printName();
	s2.printName();

	s1 = s2;// 需要重载赋值运算符  否则发生内存泄露

	s1.printName();
	s2.printName();


}

int main()
{
	test01();
	return EXIT_SUCCESS;
}

七、关系运算符


#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

public:
	Maker()
	{
		this->id = 0;
		this->age = 0;

	}
	Maker(int id,int age)
	{
		this->id = id;
		this->age = age;
	}

	// 关系运算符进行重载
	bool operator==(Maker& m)
	{
		if (this->id == m.getId() && this->age == m.getAge())
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	int getId()
	{
		return this->id;
	}

	int getAge()
	{
		return this->age;
	}


private:
	int id;
	int age;
};


void test01()
{
	Maker m(1,2);
	Maker m1(2,3);

	if (m1 == m)
	{
		cout << "相同" << endl;
	}
	else
	{
		cout << "不相同" << endl;
	}
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

八、前置加加运算符与后置加加运算符

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Maker {

	friend ostream& operator<<(ostream& out, Maker& m);

public:

	// 默认的构造函数
	Maker()
	{
		this->a = 0;
	}

	Maker(int a)
	{
		this->a = a;
	}

	// 重载前置加加
	Maker& operator++()
	{
		// 需要返回引用  不然返回的是一个新的Maker 对象
		++this->a;
		return *this;
	}

	// 重载后置加加
	Maker operator++(int)
	{	
		// 后置加加  先返回 后加加
		Maker tmp(*this);// *this 里面的值a等于2  调用拷贝构造函数 
		++this->a;// 后加加
		return tmp;
	}

	int getA()
	{
		return this->a;
	}

private:
	int a;

};

ostream& operator<<(ostream& out, Maker& m)
{
	//cout << m.getA() << endl;
	cout << m.a<< endl;// 声明为友元函数 才可以访问私有成员变量

	return out;
}

void test01()
{
	Maker m1(1);
	cout << m1 << endl;
	cout << ++m1 << endl;

	cout << m1++ << endl;// 返回的是temp对象  局部对象
	cout << m1 << endl;
}



int main()
{
	test01();
	return EXIT_SUCCESS;
}

九、数组下标重载

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include<string>
#include"MyArray.h";

void printMyArray(MyArray& arr)
{
	for (int i = 0; i < arr.Size(); i++)
	{
		cout << arr.Get(i) << " ";
	}
	cout << endl;
}

void test01()
{
	MyArray arr;

	for (int i = 0; i < 20; i++)
	{

		arr[i] = i + 10;
	}

	MyArray arr2;
	arr2 = arr;

	for (int i = 0; i < 20; i++)
	{

		cout<<arr2[i] << endl;
	} 
}


int main()
{

	test01();

	return EXIT_SUCCESS;
}

MyArray.h

#pragma once
class MyArray
{
public:
	MyArray();// 无参构造函数声明
	MyArray(const MyArray& arr);// 拷贝构造声明
	MyArray(int capacity, int val = 0);// 有参构造函数

	// 重写赋值运算符重载函数
	MyArray& operator=(const MyArray& m);

	// 重载数组 下标
	int& operator[](int index);


	~MyArray();// 析构函数

	//头插
	void PushFront(int val);

	//尾插
	void PushBack(int val);

	// 头部删除
	void PopFront();

	// 尾部删除
	void PopBack();

	// 获取数组元素个数
	int Size();

	// 获取数组容量
	int Capacity();

	// 指定位置插入元素
	void Insert(int pos, int val);

	// 获取指定位置的值
	int& Get(int pos);

	// 在指定位置修改值
	void Set(int pos, int val);

private:
	int* pArray;// 指向堆区空间,存储数据 = new int[this->mCapacity]; 创建堆区数组
	int mSize;// 元素个数
	int mCapacity;// 容量
};

MyArray.cpp


#include "MyArray.h"
#include<iostream>
using namespace std;

MyArray::MyArray()
{
	this->mCapacity = 20;
	this->mSize = 0;

	// 创建堆区数组
	this->pArray = new int[this->mCapacity];

	// 数组元素全部初始化为0
	for (int i = 0; i < this->mCapacity; i++)
	{
		this->pArray[i] = 0;
	}
}

MyArray::~MyArray()
{
	if (this->pArray == NULL)
	{
		delete[] this->pArray;// 释放数组空间
		this->pArray = NULL;
	}
}

// 重写赋值运算符重载
MyArray& MyArray::operator=(const MyArray& m)
{
	// 首先释放原来的空间
	if (this->pArray != NULL)
	{
		delete[] this->pArray;
		this->pArray = NULL;
	}

	this->mCapacity = m.mCapacity;
	this->mSize = m.mSize;

	// 申请空间  
	this->pArray = new int[m.mCapacity];

	// 拷贝数据
	for (int i = 0; i < this->mCapacity; i++)
	{
		this->pArray[i] = m.pArray[i];
	}

	return *this;
}


// 重载数组 下标
int& MyArray::operator[](int index)
{
	// 赋值时 加加
	this->mSize++;
	return this->pArray[index];
}



// 拷贝构造函数的实现
MyArray::MyArray(const MyArray& arr)
{
	this->mCapacity = arr.mCapacity;
	this->mSize = arr.mSize;

	// 申请空间
	this->pArray = new int[arr.mCapacity];

	// 拷贝数据
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = arr.pArray[i];
	}
}

// 实现不能有默认参数
MyArray::MyArray(int capacity, int val)
{
	this->mCapacity = capacity;
	this->mSize = capacity;
	this->pArray = new int[capacity];
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = val;
	}
}

//头插
void MyArray::PushFront(int val)
{
	if (this->mSize == this->mCapacity)
	{
		return;// 数组满了
	}

	// 如果没有满 从数组最后一个元素开始向后移动元素
	for (int i = this->mSize - 1; i >= 0; i--)
	{
		this->pArray[i + 1] = this->pArray[i];
	}

	// 空出第一个位置
	this->pArray[0] = val;

	// 数组长度加一
	this->mSize++;
}

//尾插
void MyArray::PushBack(int val)
{
	if (this->mSize == this->mCapacity)
	{
		return;// 数组元素满
	}

	this->pArray[this->mSize] = val;
	this->mSize++;// 数组长度加一
}

// 头部删除
void MyArray::PopFront()
{
	if (this->mSize == 0)
	{
		return;// 数组元素为0
	}

	// 后面的数往前面移动来覆盖第一个元素
	for (int i = 0; i < this->mSize; i++)
	{
		this->pArray[i] = this->pArray[i + 1];
	}

	this->mSize--;
}

// 尾部删除
void MyArray::PopBack()
{
	// 这个只能在逻辑上进行删除
	if (this->mSize == 0)
	{
		return;
	}
	this->mSize--;
}

// 获取数组元素个数
int MyArray::Size()
{
	return this->mSize;
}

// 获取数组容量
int MyArray::Capacity()
{
	return this->mCapacity;
}


// 指定位置插入元素
void MyArray::Insert(int pos, int val)
{
	// 先判断容量是否已经满了
	if (this->mSize == this->mCapacity)
	{
		return;
	}

	// 如果位置不对  就插入尾部
	if (pos < 0 || pos > this->mSize - 1)
	{
		pos = this->mSize;
	}

	for (int i = this->mSize - 1; i >= pos; i--)
	{
		this->pArray[i + 1] = this->pArray[i];
	}

	this->pArray[pos] = val;
	this->mSize++;
}

// 获取指定位置的值
int& MyArray::Get(int pos)
{
	return this->pArray[pos];
}

// 在指定位置修改值
void MyArray::Set(int pos, int val) {

	if (pos < 0 || pos > this->mCapacity - 1)
	{
		return;
	}

	this->pArray[pos] = val;
}

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少写代码少看论文多多睡觉

求打赏,求关注,求点赞

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值