什么是运算符重载
运算符重载是一种允许用户重新定义 C++ 中大多数运算符的功能的机制。在 C++ 中,运算符(如+、-、*、/等)本来有其预定义的操作,比如对于基本数据类型(int、float等)的加法操作。但是,当我们处理自定义的数据类型(如类和结构体)时,通过运算符重载,可以赋予这些运算符新的含义,使其能够像操作基本数据类型一样方便地操作自定义类型。
例如,我们有一个复数类Complex,它包含实部和虚部两个成员变量。如果没有运算符重载,对两个复数进行相加操作就会很麻烦,可能需要定义一个专门的函数来实现加法。但是通过重载+运算符,就可以像Complex c3 = c1 + c2;这样直观地进行复数相加操作,其中c1、c2和c3是Complex类的对象。
如何在 C++ 中进行运算符重载
成员函数方式
加号运算符重载
作用:实现两个自定义数据类型相加的运算
#include<iostream>
using namespace std;
class Box
{
int height;
int width;
int length;
public:
Box() :height(0), width(0), length(0) {}
Box(int l, int w, int h) :height(h), width(w), length(l) {}
Box(const Box &other)
{
this->height = other.height;
this->width = other.width;
this->length = other.length;
}
Box operator+ (const Box & other)//加&避免调用拷贝构造
//返回值不加引用,是因为返回的是局部变量,函数结束会被释放
{
Box c;
c.length = other.length + this->length;
c.width = other.width + this->width;
c.height = other.height + this->height;
return c;
}
void Print()
{
cout << width << " " << length << " " << height << endl;
}
~Box()
{
cout << "析构函数" << endl;
}
};
int main()
{
Box a(1,1,1), b(1,1,1);
Box c = a + b;
c.Print();
}
左移运算符重载
作用:可以输出自定义数据类型
#include<iostream>
#include<string>
using namespace std;
class Box
{
int height;
int width;
int length;
public:
friend ostream& operator<<(ostream &o, Box a);
Box() :height(0), width(0), length(0) {}
Box(int l, int w, int h) :height(h), width(w), length(l) {}
Box(const Box &other)
{
this->height = other.height;
this->width = other.width;
this->length = other.length;
}
void Print()
{
cout << width << " " << length << " " << height << endl;
}
~Box()
{
cout << "析构函数" << endl;
}
};
//左移运算符要在类外实现
ostream& operator<<(ostream &o ,Box a)
{
o << a.height << " " << a.length << " " << a.width << endl;
return o;
}
int main()
{
Box a(1, 1, 1),b;
cout << a << b;
}
+=递增运算符重载
#include<iostream>
using namespace std;
class Box
{
int length;
int width;
int height;
public:
friend ostream& operator<<(ostream& o, Box b);
Box() :length(0), width(0), height(0) {}
Box(int l, int w, int h) :length(l), width(w), height(h) {}
Box(const Box& other)
{
this->length = other.length;
this->height = other.height;
this->width = other.width;
}
Box& operator+=(const Box& other) //可以引用返回*this,因为*this这个对象没在当前函数内创建,在主函数创建的,主函数如果是a+=b,那么*this是a
{
this->height += other.height;
this->length += other.length;
this->width += other.width;
return *this;
}
};
ostream& operator<<(ostream& o,Box b)
{
o << b.length << " " << b.width << " " << b.height << endl;
return o;
}
int main()
{
Box a(1,1,1), b(2,2,2),c(3,3,3);
a += b += c;
cout <<a << b << c << endl;
}
递增运算符重载
class Integer
{
int num;
public:
Integer() :num(0) {}
Integer(int val) :num(val) {}
Integer operator++ ()
{
this->num += 1;
return *this;
}
Integer operator++ (int) //使用参数占位符来区分前++和后++,有参数占位符的为后++
{
Integer other = *this;
this->num += 1;
return other;
}
};
关系运算符
#include<iostream>
using namespace std;
#include<string>
#include<vector>
class A
{
int num;
public:
A() :num(0) {}
A(int num) :num(num) {}
bool operator> (const A& other)
{
if (this->num > other.num) return 1;
return 0;
}
bool operator ==(const A& other)
{
if (this->num == other.num) return 1;
return 0;
}
};
int main() {
A a(2), b;
cout << (a > b);
return 0;
}
赋值运算符
#include<iostream>
using namespace std;
#include<string>
#include<vector>
class A
{
int num;
int *p;
public:
A() :num(0),p(nullptr) {}
A(int num) :num(num),p(new int(num)) {}
~A()
{
if (p) delete p;
}
A& operator= (const A&other)
{
if (p) delete p; //释放原来指向的堆区内存,拷贝构造没有这一步骤
num = other.num;
p = new int(num); //指向新的堆区内存
return *this;
}
};
int &fun()
{
int a = 2;
return a;
}
int main() {
int b = fun(); //行
int &c = fun(); //不行
A a(2), b(3),c;
a = b = c; // 如果类内没有实现赋值运算符,编译器则会提供默认的赋值运算符,每个成员都会被赋值,此时要注意浅拷贝问题
return 0;
}
非成员函数方式(友元函数)
有时候,我们可能需要访问类的私有成员来进行运算符重载,这时可以使用友元函数。例如,对于上面的Vector2D类,如果我们想通过非成员函数来重载+运算符:
class Vector2D {
public:
double x;
double y;
Vector2D(double a = 0, double b = 0) : x(a), y(b) {}
friend Vector2D operator+(const Vector2D& v1, const Vector2D& v2);
};
Vector2D operator+(const Vector2D& v1, const Vector2D& v2) {
return Vector2D(v1.x + v2.x, v1.y + v2.y);
}
运算符重载在面向对象编程中的好处
提高代码的可读性和可维护性
当处理自定义类型时,按照人们习惯的运算符方式来操作对象,会使代码更加直观。例如,对于矩阵类Matrix,如果要实现两个矩阵相加,重载+运算符后,代码Matrix c = a + b;比调用一个名为addMatrices之类的函数(如Matrix c = addMatrices(a, b);)更容易理解。这种直观的表达方式符合人们对数学运算的习惯,使得代码的意图更加清晰,降低了理解代码的难度,从而提高了代码的可维护性。
代码的复用性增强
一旦正确地重载了运算符,这些运算符可以在整个程序中以相同的方式用于自定义类型的对象,就像它们用于基本数据类型一样。例如,我们重载了+和*运算符用于复数类Complex,那么在任何需要对复数进行加法和乘法的地方,都可以直接使用这些运算符,而不需要重新编写专门的函数来实现相同的操作。这减少了代码的冗余,提高了代码的复用性。
更好地体现面向对象编程的封装性和抽象性
通过运算符重载,可以将对自定义类型对象的复杂操作封装在运算符函数内部。例如,对于一个日期类Date,重载+运算符来实现日期的加法(如加上一定天数),用户在使用Date类对象时,不需要了解日期加法的具体实现细节,只需要使用+运算符即可。这使得类的接口更加简洁和抽象,隐藏了内部的实现细节,符合面向对象编程的封装和抽象原则。