知识图谱
一.需要重载的原因
正常情况下,C++ 的运算符( +、-、*、/ 等)只能用于对基本类型的常量或变量进行运算,而不能用于类对象之间的运算。
类的对象直接的运算虽然可以通过成员函数或全局函数去实现,如date.Add(1),但这样的写法有时不易理解。
C++的提供了重载运算符的特性,允许将一些常见的运算符根据自定义类型的需要去重新定义,尽量让对象之间的运算可以像基本类型的运算一样简单、好理解。
二.声明、定义重载运算符函数
1.定义
返回值类型 operator 运算符(形参列表) {... }
返回值一般是一个类
- 一元运算符重载:一元运算符只有一个操作数。在重载一元运算符时,通常将其定义为类的成员函数,没有参数(除了隐式的this指针)。
- 二元运算符重载:二元运算符有两个操作数。在重载二元运算符时,可以选择将其定义为类的成员函数或非成员函数。
- 如果将二元运算符定义为类的成员函数,参数列表将包括一个额外的参数,表示右侧操作数。左侧操作数则是隐式的this指针。
- 如果将二元运算符定义为非成员函数(全局函数或友元函数),参数列表将包括两个参数,分别表示左侧和右侧操作数。
2.重载运算符的例子
(1)单目运算符
#include <iostream> using namespace std;class A { private:int x, y; public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}A &operator++()//++i 前置++实现{++x; //先自增++y;return *this; //后引用}A operator++(int) //i++ 后置++实现{//int参数没有任何意义,只是为了区分是前置还是后置形式A a = *this; //保存对象引用++(*this); //自增,调用前面实现的前置++return a; //返回先前保存的对象}void show(){cout << "x=" << x << "," << "y=" << y << endl;} }; int main() {A a1(1, 2), a2(3, 4);(a1++).show();(++a2).show();return 0; }
输出:
x=1,y=2x=4,y=5
前置运算符:++a,先++,后赋值(返回引用)
后置运算符:--a,先赋值(赋值的方法,定义一个类来保存先前对象的引用),然后调用前面的前置++运算符实现自增,最后返回先前保存的对象。注意:后置的++要带上参数int用来区别前置运算符
#include <iostream>class MyClass { private:int value; public:MyClass() : value(0) {} // 构造函数MyClass(int val) : value(val) {} // 带参数的构造函数// 后置自增运算符的重载MyClass operator++(int) {MyClass temp = *this; // 保存当前对象的一个副本value++; // 自增成员变量return temp; // 返回副本(未自增的状态)}int getValue() const {return value;} };int main() {MyClass obj(10);std::cout << "Before increment: " << obj.getValue() << std::endl;MyClass obj2 = obj++; // 使用后置自增std::cout << "After increment: " << obj.getValue() << std::endl;std::cout << "Copy before increment: " << obj2.getValue() << std::endl;return 0; }
若没有前置++的版本,后置的++需要自增成员变量
(2)双目运算符
加法重载运算符
成员函数
#include <iostream> using namespace std;class A { private:int x, y; public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}A operator+(const A& a)const{A t;t.x = this->x + a.x;t.y = this->y + a.y;return t;}void show(){cout << "x=" << x << "," << "y=" << y << endl;} }; int main() {A a1(1, 2);A a2(3, 4);A a;a = a1 + a2;a.show(); }
左值无需用参数,直接就用this传
全局函数
#include <iostream> using namespace std;class A { private:int x, y; public:A(int x1 = 0, int y1 = 0){x = x1;y = y1;}friend A operator+(const A& a, const A& b);void show(){cout << "x=" << x << "," << "y=" << y << endl;} }; A operator+(const A& a, const A& b) {return A(a.x + b.x, a.y + b.y); } int main() {A a1(1, 2), a2(3, 4);A c;c = a1 + a2;c.show();return 0; }
注意其书写形式
`const A& a`是一种参数传递的方式,表示`a`是一个对类型`A`的常量引用。
(3) 关系运算符重载
class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//private:int _year;int _month;int _day; };//operator运算符 做函数名 bool operator == (const Date& x, const Date& y) {return x._year == y._year&& x._month == y._month&& x._day == y._day; }bool operator < (const Date& x, const Date& y) {if (x._year < y._year){return true;}else if (x._year == y._year){if (x._month < y._month){return true;}else if (x._month == y._month){return x._day < y._day;}}return false; } int main() {Date d1(2024, 1, 28);Date d2(2024, 1, 29);cout << operator == (d1, d2) << endl;cout << operator < (d1, d2) << endl;cout << (d1 == d2) << endl;cout << (d1 < d2) << endl;return 0; }
注意其书写形式
赋值运算符
class Date { public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print(){cout << _year << '/' << _month << '/' << _day << endl;}Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}private:int _year;int _month;int _day; };int main() {Date d1(2024, 2, 01);Date d2(2024, 2, 02);Date d3(2024, 2, 03);d1 = d2 = d3;d1.Print();d2.Print();d3.Print();return 0; }
在赋值运算符重载函数中,通常需要执行以下操作:
检查是否是自我赋值,即当前对象和要赋值的对象是否是同一个对象。如果是同一个对象,则直接返回当前对象,避免不必要的操作。
进行属性的深拷贝,将要赋值的对象的属性逐个复制给当前对象的属性。
返回当前对象的引用。
为何使用引用
- 引用参数:赋值运算符需要修改当前对象的值,而不是创建一个新的对象。因此,使用引用参数,可以直接修改当前对象而不是在函数内部创建一个副本。
- 返回this指针:赋值运算符一般返回当前对象的引用,即*this。这样可以实现连续赋值操作,例如 a = b = c。通过返回this指针,可以链式调用赋值运算符。
(4)流输入与流输出运算符重载(以复数为例)
流输出
#include <iostream>using namespace std;class Complex { private:double real;double imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}friend ostream& operator<<(ostream& out, const Complex& c); };ostream& operator<<(ostream& out, const Complex& c) {out << "(" << c.real << ", " << c.imag << ")";return out; }int main() {Complex c1(3.0, 4.5);cout << "The complex number is: " << c1 << endl;return 0; }
operator<<
被定义为一个友元函数。- 函数参数包括输出流对象(通常是
ostream&
)和常量引用的复数对象。- 函数返回输出流对象的引用以便支持链式调用(如
cout << c1 << c2
)。- 注意:里面的<<是ostream的成员函数
流输入
#include <iostream>using namespace std; class Complex { private:double real;double imag;public:Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}friend istream& operator>>(istream& in, Complex& c); };istream& operator>>(istream& in, Complex& c) {in >> c.real >> c.imag;return in; }int main() {Complex c2;cout << "Enter a complex number (real and imaginary parts): ";cin >> c2;cout << "You entered: " << c2 << endl;return 0; }
operator>>
被定义为一个友元函数。- 函数参数包括输入流对象(通常是
std::istream&
)和非常量引用的复数对象。- 函数返回输入流对象的引用以便支持链式调用(如
std::cin >> c1 >> c2
)。