C++编程之运算符重载,内含大量示例代码和相关脑图

  • Post author:
  • Post category:其他


那天,大佬指着review代码,对我大吼:“那个谁谁谁,两点位之间相对关系咋这样计算??GIS是矢量运算,你不知道吗??按矢量关系,重载运算符,把这些代码全部重新修改update…”。就这样,我哼哧哼哧加班,又把运算符重载搞了一遍。这就是本篇文章的灵感来源。苦涩的沙吹痛脸庞的感觉~水手响起

系列文章


C++编程之引用的详细总结



C++编程之命名空间、const常量




1、为什么要运算符重载

何为运算符重载?在C/C++或其他编程语言中,都会涉及运算,比如加(+)、 减(-)、乘(*)、除(/),不同的运算符在不同数据类型中计算结果不同。比如两数相加:

int Add(const int a, const int b)
{
	return a + b;
}
// 此函数返回两个整数的和,使用的是加(+)法运算符。

在C++中,运算符都是需要和相同类型或有父子类关系类型的变量之间运算。

string Add(const int a)
{
	string strName = "Marble";
    string c = strName + a;
    return c;
}
// 以上代码,在C++编译器中必定会报错,编译器不知道你这是在搞什么!!!

以上讲述,初步引入了运算符重载的需求。即自定义运算符运算规则,让编译器知道你这是在干什么。如下示例:

class Point
{
public:
	int x;
    int y;
}
// 需求计算两个点Point之和。

Point Add(Point p1, Point p2)
{
	return p1 + p2;
}
// 上述代码,编译器也是一脸懵逼,不知如何处理,只能给你报错。

在这里插入图片描述

以上需求,引入今天的话题“运算符重载”。



2、运算符重载语法格式


类型


类名




::


operator


函数名(参数)



类型



::



operator


函数名(参数)

特别注意:此处的函数名,其实是

运算符

,指出后续需要计算的符号。运算符重载一般有

两种方式

,如上格式,贴出一段简短的代码。

Point operator+(const Point &p1, const Point &p2)
{
	Point temp;
	temp.x = p1.x + p2.x;
	temp.y = p1.y + p2.y;
	return temp;
}




3、运算符重载的方式


3.1、成员函数

让重载运算符成为该类的成员函数。这允许运算符函数访问类的私有成员。它也允许函数使用隐式的this指针来访问调用对象,这种类型参数个数小于等于1。其语法格式如下:


类型


类名




::


operator


函数名(参数)

// 点类型
class Point
{
public:
	//Point 类的成员函数
	Point operator+(const Point& p1);

private:
	int x;
	int y;
};

Point Point::operator+(const Point &p1)
{
	// 使用隐式的this指针形参来访问调用对象
	this->x = this->x + p1.x;
	this->y = this->y + p1.y;
	return *this;
}


3.2、友元函数

让重载的成员函数成为独立分开的函数。当以这种方式重载时,运算符函数必须声明为类的友元才能访问类的私有成员。


class Point
{ 
	// 重载友元函数 全局函数 + 操作符重载
	friend Point operator+(const Point& p1, const Point& p2);

private:
	int x;
	int y;
};
 
 // 全局函数法 实现 + 运算符重载
Point operator+(const Point& p1, const Point& p2)
{
	Point p;
	p.x = p1.x + p2.x;
	p.y = p1.y + p2.y;
	return p;
}



4、哪些运算符可以重载

如下脑图所示,是可以重载的运算符,后面章节将进行所有可重载运算符实例演示。

在这里插入图片描述



5、哪些运算符不可以重载

如下脑图所示,是不可以重载的运算符,这些运算符是系统预定义的运算符,只有唯一性,不可以重载。

在这里插入图片描述



5、运算符重载实例


5.1、双目运算符重载
class Point
{
public:
	//Point 类的成员函数
    // 加法运算符重载
	Point operator+(const Point& p);
    // 减法运算符重载
	Point operator-(const Point& p);
    // 乘法运算符重载
	Point operator*(const Point& p);
    // 除法运算符重载
	Point operator/(const Point& p);
    // 取模运算符重载
	Point operator%(const Point& p);

private:
	int x;
	int y;
};

// 加法运算符重载
Point Point::operator+(const Point &p1)
{
	this->x = this->x + p1.x;
	this->y = this->y + p1.y;
	return *this;
}
 // 减法运算符重载
Point Point:: operator-(const Point& p)
{
	this->x -= p.x;
	this->y -= p.y;
	return *this;
}

 // 乘法运算符重载
Point Point:: operator*(const Point& p)
{
	this->x *= p.x;
	this->y *= p.y;
	return *this;
}

// 除法运算符重载
Point Point:: operator/(const Point& p)
{
	this->x /= p.x;
	this->y /= p.y;
	return *this;
}

 // 取模运算符重载
Point Point:: operator%(const Point& p)
{
	this->x %= p.x;
	this->y %= p.y;
}


5.2、关系运算符重载


5.2.1、成员函数重载

// 构造点类
class Point
{
public:
     bool operator==(const Point& p);
     bool operator!=(const Point& p);
     bool operator<(const Point& p);
     bool operator>(const Point& p);
     bool operator<=(const Point& p);
     bool operator>=(const Point& p);

private:
     int x;
     int y;
};


bool Point::operator==(const Point& p)
{ 
 	return this->x == p.x && this->y == p.y;
}

bool Point:: operator!=(const Point& p)
{
 	return this->x != p.x && this->y != p.y;
}

bool Point:: operator<(const Point& p)
{
 	return this->x < p.x && this->y < p.y;
}
bool Point:: operator>(const Point& p)
{	
 return this->x > p.x&& this->y > p.y;
}

bool Point:: operator<=(const Point& p)
{
 	return this->x <= p.x && this->y <= p.y;
}

bool Point:: operator>=(const Point& p)
{
     return this->x >= p.x && this->y >= p.y;
}


5.2.2、非成员函数重载

非成员函数重载,如果要访问类的私有变量,必须为类的友元,才可以访问。类似你想获取别人隐私,一般要先认识成为朋友才可以。如下图所示:

在这里插入图片描述



5.3、逻辑运算符重载

不建议重载逻辑运算符 &&和 ||,原因如下:

#include<iostream>
using namespace std;
class Person
{
public:
	Person(const int age)
	{
		this->age = age;
	}
	int getName()const
	{
		cout << "age is :" << this->age << endl;
		return this->age;
	}
	 
private:
	int age;
};

// 重载&&
bool operator && (const Person& p1, const Person& p2)
{
	return p1.getName() && p2.getName();
}

// 重载||
bool operator || (const Person& p1, const Person& p2)
{
	return p1.getName() || p2.getName();
}
 

int main()
{
	Person p1(-10);
	Person p2(20);
	if (p1 && p2)
	{
		cout << "true" << endl;
	}
	else
	{
		cout << "false" << endl;
	}
	system("pause");
	return 0;
}

运行结果

在这里插入图片描述

从结果可以看出,并没有达到我们需要的结果,预期的结果是,如果两者都为正数,则为true,如果有一个为负数,则为false。但是运行结果并不是这样,这就破坏了逻辑运算符原有的


短路规则


。因此建议不要轻易重载逻辑运算符。



5.4、单目运算符重载


5.4.1 、 +(正)、-(负)运算符重载
class  Point
{
public:
	Point(const int x,const int y)
	{
		this->x = x;
		this->y = y;
	}
	
    // 负值仍然是负值,正值则不变
	Point operator +()
	{
		this->x = +this->x;
		this->y = +this->y;
		return *this;
	}

	// 正值变为负值,负值变为正值
	Point operator -()
	{
		this->x = -this->x;
		this->y = -this->y;
		return *this;
	}
	 
private:
	int x;
	int y;

}; 

//测试代码
void Test()
{
	Point p1(10, 20);
	Point p2 = -p1;
	Point p3 = -p2;
}


5.4.2 、*(指针),&(取地址)重载

模拟智能指针

class  Person
{
public:
	Person(const int age)
	{
		cout << "Person 构造" << endl;
		this->m_age = age;
	}
	~Person()
	{
		cout << "Person析构" << endl;
	}

	int getAge()const
	{
		return this->m_age;
	}

private:
	int m_age;
}; 

class SmartPointor
{
public:
	SmartPointor(const Person* person)
	{
		cout << "Smartpointor 构造" << endl;
		this->p = person;
	}

	~SmartPointor()
	{
		cout << "SmartPointor 析构" << endl;
		if (this->p != NULL)
		{
			delete this->p;
			this->p = NULL;
		}
	}

	Person operator *()
	{
		return *p;
	}

	Person* operator &()
	{
		return p;
	}
private:
	Person* p;
};

void test()
{
	SmartPointor sp = SmartPointor(new Person(10));
    // 下面两种调用属于* 和& 重载后的调用结果,感兴趣的可以体验一下。
	(*sp).getAge();
	(&sp)->getAge();
}

int main()
{
	// 普通情况下,new对象后,还需要手动释放,每次都需要
	/*Person *person = new Person(10);
	delete person;*/

	// 使用智能指针后,就可以避免手动调用delete,避免遗漏导致的内存泄漏
	test();
	
	system("pause");
	return 0;
}


5.5、自增++、自减–运算符重载
class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 前置++ 即 ++a
	MyInt operator ++()
	{
		this->m_int++;
		return *this;
	}

	// 后置++ 即 a++,采用占位符,请体验此处的妙用!!!!
	MyInt operator ++(int)
	{
		MyInt temp = *this;
		this->m_int++;
		return temp;
	}

	// 前置-- 即 --a
	MyInt operator --()
	{
		this->m_int--;
		return *this;
	}

	// 后置-- 即 a--
	MyInt operator --(int)
	{
		MyInt temp = *this;
		this->m_int--; // 延后--
		return temp;
	}

private:
	int m_int;
};


5.6、位运算运算符重载
class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 重载|
	MyInt operator |(const MyInt& a)
	{
		return this->m_int | a.m_int;
	}

	// 重载&
	MyInt operator &(const MyInt& a)
	{
		return this->m_int & a.m_int;
	}

	// 重载~
	MyInt operator ~()
	{
		return ~this->m_int;
	}

	// 重载^
	MyInt operator ^(const MyInt& a)
	{
		return this->m_int ^ a.m_int;
	}

	// 重载<<
	MyInt operator <<(const int len)
	{
		return this->m_int << len;
	}

	MyInt operator >>(const int len)
	{
		return this->m_int >> len;
	}

	void print()
	{
		cout << this->m_int << endl;
	}

private:
	int m_int;
};
 
int main()
{
	MyInt p(8);
	MyInt q(5);

	MyInt r1 = p | q;
	r1.print();

	MyInt r2 = p^q;
	r2.print();

	MyInt r3 = p&q;
	r3.print();

	MyInt r4 = ~p;
	r4.print();

	MyInt r5 = p << 3;
	r5.print();

	MyInt r6 = p >> 3;
	r6.print();

	system("pause");
	return 0;
}

运行结果如图所示:

在这里插入图片描述



5.7、赋值运算符重载

class  MyInt
{
public:
	MyInt(const int a)
	{
		this->m_int = a;
	}

	// 重载=,如何实现 a=b=c=d,链式编程呢?这个问题留在后面讲解。
	MyInt operator =(const MyInt& a)
	{
		return this->m_int = a.m_int;
	}

	// 重载+=
	MyInt operator +=(const MyInt& a)
	{
		return this->m_int += a.m_int;
	}

	// 重载-=
	MyInt operator -=(const MyInt& a)
	{
		return this->m_int -= a.m_int;
	}

	// 重载*=
	MyInt operator *=(const MyInt& a)
	{
		return this->m_int *= a.m_int;
	}

	// 重载/=
	MyInt operator /=(const int len)
	{
		return this->m_int /= len;
	}

	// 重载%=
	MyInt operator %=(const int len)
	{
		return this->m_int %= len;
	}

	// 重载&=
	MyInt operator &=(const int len)
	{
		return this->m_int &= len;
	}

	// 重载|=
	MyInt operator |=(const int len)
	{
		return this->m_int |= len;
	}

	// 重载^=
	MyInt operator ^=(const int len)
	{
		return this->m_int ^= len;
	}
	// 重载<<=
	MyInt operator <<=(const int len)
	{
		return this->m_int = this->m_int << len;
	}
	// 重载>>=
	MyInt operator >>=(const int len)
	{
		return this->m_int = this->m_int >> len;
	}

	void print()
	{
		cout << this->m_int << endl;
	}

private:
	int m_int;
};


5.8、空间申请与释放运算符重载

重载new,delete运算符,new,delete在c++中也被归为运算符,所以可以重载它们。

new的行为:

  1. 先开辟内存空间
  2. 再调用类的构造函数,开辟内存空间的部分,可以被重载。

delete的行为:

  1. 先调用类的析构函数
  2. 再释放内存空间释放内存空间的部分,可以被重载。

那么为什么要要重载这两个运算符呢?

比如频繁的new和delete对象,会造成内存碎片,内存不足等问题,影响程序的正常执行,所以一次开辟一个适当大的空间,作为内存池,每次需要对象的时候,不再需要去开辟内存空间,只需要调用构造函数(使用placement new)即可。

new,delete的重载函数,可以是全局函数,也可以是类内部的公有重载函数;当既有全局的重载函数,也有类内部的公有重载函数时,

实际调用的是类内部的公有重载函数。

new,delete可以有多种重载方式,但是,new函数的第一个参数一定要是

size_t

类型

// new 单个对象
void* operator new(size_t sz)
{
  void* o = malloc(sz);
  return o;
}
void operator delete(void *o)
{
  free(o);
}
// new对象的数组
void* operator new[](size_t sz)
{
  void* o = malloc(sz);
  return o;
}
void operator delete[](void *o)
{
  free(o);
}
 // 不开辟空间,只是调用给定对象(用地址识别)的构造方法,也叫placement new 
void* operator new(size_t sz, String* s, int pos)
{
  return s + pos;
}


5.9、其他运算符重载

括号运算符一般用于仿函数中

void operator()(const string str)
{
   cout << str ;
}

成员访问符(->)重载

class  Person
{
public:
	Person(const int age)
	{
		cout << "Person 构造" << endl;
		this->m_age = age;
	}
	~Person()
	{
		cout << "Person析构" << endl;
	}

	int getAge()const
	{
		return this->m_age;
	}

private:
	int m_age;
}; 

class SmartPointor
{
public:
	SmartPointor(const Person* person)
	{
		cout << "Smartpointor 构造" << endl;
		this->p = person;
	}

	~SmartPointor()
	{
		cout << "SmartPointor 析构" << endl;
		if (this->p != NULL)
		{
			delete this->p;
			this->p = NULL;
		}
	}

	// 模拟智能指针,重载->运算符
	Person* operator ->()
	{
		return p;
	}

private:
	Person* p;
};

逗号运算符和[]下标运算符重载,请各位小伙伴们自行实现,具体实现逻辑和前面差不多。找一个应用场景,即可实现。



6、写在结尾

本文是博主被大佬暴击后,review学过的知识点,总结的笔记,文中的示例代码都是经过调试测试,无问题后才贴上去的。由于C++运算符重载应用比较广泛,因此做笔记于此。博主切身体会到,编程基础的重要性,以及能灵活使用,更是重中之重。希望本篇文章,对你有帮助。若有助于你,请点赞支持我继续写下去;如果您在阅读中发现有错误,请留言或随时私信我,我第一时间更新修复,万分感谢。



版权声明:本文为Marble_ccp原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。