C++第十二篇:运算符重载
C++ 中的运算符重载是一种允许程序员为自定义类型(如类或结构体)重新定义已有运算符行为的机制。通过运算符重载,你可以像使用内置类型(如 int、double)一样,对自定义对象使用常见的运算符(如 +、-、==、<< 等)。
简单说就是你可以改变一个符号的作用,自己定义它的功能,甚至将 '+' 改为 ' - ' 的作用。
一、基本概念
- 运算符重载本质上是函数重载的一种形式。
- 每个被重载的运算符对应一个运算符函数,其函数名为
operator
后跟要重载的运算符符号,例如:operator+
、operator==
、operator<<
等。
二、重载方式
1. 成员函数
1.1 示例解析
在没有进行运算符重载之前,如果我们将两个对象相加,那结果是什么呢?或者说这样做真的可以吗?来看下面的代码:
class Complex {
public:double real;Complex(double r = 0) : real(r){}
};int main()
{Complex A(2.0);Complex B(1.0);//错误,没有进行运算符的重载;A+B;return 0;
}
编译结果:
毫无疑问,这样是不可以的,并且提示没有匹配的运算符重载函数。
那我们再来看看有了运算符重载会发生什么,如下:
#include <iostream>class Complex {
public:double real;Complex(double r = 0) : real(r){}// 成员函数重载 +Complex operator+(const Complex& other) const {return Complex(this->real + other.real);}
};int main()
{Complex A(2.0);Complex B(1.0);Complex C = A+B;std::cout << "num : " << C.real << std::endl;return 0;
}
结果如下:
我们发现,编译并没报错,并且还打印出了对象成员变量的值。
来分析一下这个运算符重载函数:
// 成员函数重载 +Complex operator+(const Complex& other) const {return Complex(this->real + other.real);}
它就是一个运算符重载函数,返回值是一个Complex 对象,operator+代表对‘ + ’做重载,参数是一个引用,这个参数实际上是' + '后面的那个对象B,类型也是 Complex ,而加号前面的就是当前对象this(即A),所以我们可以看到,这个运算符重载函数中内容是this->real+ other.real,实际就是加号前后两个的成员变量的运算,最后返回了一个新的Complex 对象。
这样大家应该都懂了原理了,实际返回什么完全由自己定。需要注意的是,这个运算符重载函数是Complex 类中的,一定要是该类型的才可以调用,并且参数和返回值也要对应。
1.2 注意点
- 左侧操作数是当前类类型
- 隐含的第一个参数是
*this
。
一定要注意,左操作数这个点:
Complex e = 5 + c; // 编译错误!
那这时候大家可能就会有疑问了,难道5+c就没办法了吗?有的兄弟有的,来看下面的非成员函数。
2. 非成员函数
1.1 示例解析
刚才的成员函数说到,加号左侧的对象在运算重载函数中就是this,但在非成员函数这里,就没有这个隐藏的this了,运算符左右的内容,全部作为参数。
直接看示例:
#include <iostream>
class Complex {public:double real;//这里写为公共的是方便后面的打印//但因为我将变量声明为公共的,所以下面不声明为友元也没有问题。Complex(double r = 0) : real(r){}// 友元非成员函数friend Complex operator+(const Complex& a, const Complex& b);friend Complex operator+(int x, const Complex& c);friend Complex operator+(const Complex& c, int x);
};// 定义
Complex operator+(const Complex& a, const Complex& b){return Complex(a.real + b.real);//友元函数可以直接调用对象的私有成员变量
}Complex operator+(int x, const Complex& c) {return Complex(x + c.real);
}Complex operator+(const Complex& c, int x) {return Complex(c.real + x);
}int main()
{Complex A(2.0);Complex B(4.0);Complex C = A + 5;Complex D = 5 + B;Complex E = A + B;std::cout << " C = " << C.real << std::endl;std::cout << " D = " << D.real << std::endl;std::cout << " E = " << E.real << std::endl;return 0;
}
结果如下:
没有问题。
实际就是将成员函数变成非成员函数,然后加上友元,这样当写出
Complex C = A + 5;
编译器就会查找所有名为 operator+
的函数(包括成员函数和非成员函数),当发现有参数类型匹配成功后,就会直接调用这个全局函数。
我们只需要将对应的功能实现,它遇到就会按照我们设置好的去运算。
1.2 注意点
- 适用于需要对称处理两个操作数(尤其是当左侧不是当前类对象时)。
- 所有参数都显式写出。
三.补充
这些运算符是不能够进行重载的:
运算符 | 名称 | 说明 |
---|---|---|
. | 成员访问(点运算符) | 用于访问对象成员,如 obj.x |
.* | 指向成员的指针(点星) | 如 obj.*ptr |
:: | 作用域解析运算符 | 如 std::cout 、ClassName::member |
?: | 三目条件运算符 | 如 a ? b : c |
sizeof | 大小运算符 | 编译期计算类型/对象大小 |
下面的运算符是无法为全局函数,必须是成员函数,如:
运算符 | 是否必须为成员 | 常见用途 |
---|---|---|
= | 是 | 赋值 |
() | 是 | 仿函数(如 std::function ) |
[] | 是 | 容器下标访问(如 vector[i] ) |
-> | 是 | 智能指针、迭代器 |
->* | 是 | 成员指针 |
内部原因较为复杂,这里不展开详细介绍,有兴趣可以自己查找资料或者问AI,这里只简单说一下'='的重载:
#include <iostream>class Complex {public:double real;Complex(double r = 0) : real(r){}Complex(const Complex &com):real(com.real){ //参数需要为conststd::cout << "拷贝构造函数\n";}// '=' 运算符的重载,必须为成员函数Complex& operator=(const Complex& b){std::cout << "运算符重载函数\n";this->real = b.real;return *this;}
};int main()
{Complex A(2.0);Complex B(4.0);Complex C = A; //拷贝构造函数A = B; //运算符重载函数std::cout << " A = " << A.real << std::endl;std::cout << " B = " << B.real << std::endl;std::cout << " C = " << C.real << std::endl;return 0;
}
结果如下:
' = '必须为成员函数,避免和默认的' = '发生冲突。
运算符的重载是一个很大的内容,里面的不同的运算符重载都有需要注意的细节,这个博客知识大概讲述了一下基本和简单的。