那天,大佬指着review代码,对我大吼:“那个谁谁谁,两点位之间相对关系咋这样计算??GIS是矢量运算,你不知道吗??按矢量关系,重载运算符,把这些代码全部重新修改update…”。就这样,我哼哧哼哧加班,又把运算符重载搞了一遍。这就是本篇文章的灵感来源。苦涩的沙吹痛脸庞的感觉~水手响起
文章目录
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的行为:
- 先开辟内存空间
- 再调用类的构造函数,开辟内存空间的部分,可以被重载。
delete的行为:
- 先调用类的析构函数
- 再释放内存空间释放内存空间的部分,可以被重载。
那么为什么要要重载这两个运算符呢?
比如频繁的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++运算符重载应用比较广泛,因此做笔记于此。博主切身体会到,编程基础的重要性,以及能灵活使用,更是重中之重。希望本篇文章,对你有帮助。若有助于你,请点赞支持我继续写下去;如果您在阅读中发现有错误,请留言或随时私信我,我第一时间更新修复,万分感谢。