运算符重载与友元:定制类行为
一、基础运算符重载
1.1 成员函数重载
class Vector3D {
float x, y, z;
public:
Vector3D operator+(const Vector3D& rhs) const {
return {x+rhs.x, y+rhs.y, z+rhs.z};
}
Vector3D& operator+=(const Vector3D& rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
};
1.2 友元函数重载
class Complex {
double real, imag;
public:
friend Complex operator+(const Complex& a, const Complex& b);
};
Complex operator+(const Complex& a, const Complex& b) {
return {a.real+b.real, a.imag+b.imag};
}
1.3 输入输出重载
class Student {
string name;
int id;
public:
friend ostream& operator<<(ostream& os, const Student& s);
friend istream& operator>>(istream& is, Student& s);
};
ostream& operator<<(ostream& os, const Student& s) {
return os << "ID:" << s.id << " Name:" << s.name;
}
istream& operator>>(istream& is, Student& s) {
return is >> s.id >> s.name;
}
二、友元机制详解
2.1 友元函数特性
class Matrix {
int data[4][4];
public:
friend Matrix multiply(const Matrix& a, const Matrix& b);
};
Matrix multiply(const Matrix& a, const Matrix& b) {
Matrix result;
// 访问私有数据进行矩阵乘法
return result;
}
2.2 友元类关系
class Engine { // 被访问类
friend class Car; // 声明友元类
int rpm;
};
class Car { // 友元类
public:
void adjustEngine(Engine& e) {
e.rpm = 2000; // 访问Engine私有成员
}
};
🔍 友元使用场景对比表:
| 场景类型 | 适用方案 | 典型示例 |
|---|---|---|
| 运算符需要对称性 | 友元函数 | operator<< |
| 跨类数据访问 | 友元类 | 汽车-引擎关系 |
| 工具函数 | 普通友元函数 | 矩阵乘法工具 |
三、重载规则与限制
3.1 可重载运算符列表
+ - * / % ^ & | ~
! = < > += -= *= /=
% = ^= &= |= << >> >>=
<<= == != <= >= && || ++
-- ->* , -> [] () new delete
3.2 不可重载运算符
. .* :: ?: sizeof
typeid alignof noexcept
3.3 特殊运算符规则
下标运算符[] :
class StringArray {
string* arr;
public:
string& operator[](size_t index) {
return arr[index];
}
const string& operator[](size_t index) const {
return arr[index];
}
};
函数调用运算符() :
class Adder {
int base;
public:
Adder(int b) : base(b) {}
int operator()(int x) const {
return base + x;
}
};
Adder add5(5);
cout << add5(3); // 输出8
四、综合应用案例
4.1 智能指针实现
template<typename T>
class SmartPtr {
T* ptr;
public:
explicit SmartPtr(T* p = nullptr) : ptr(p) {}
~SmartPtr() { delete ptr; }
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
// 禁止拷贝(示例)
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
};
4.2 矩阵运算系统
class Matrix {
vector<vector<double>> data;
public:
// 矩阵加法
Matrix operator+(const Matrix& rhs) const;
// 矩阵乘法
friend Matrix operator*(const Matrix& a, const Matrix& b);
// 标量乘法
Matrix operator*(double scalar) const;
// 输出格式化
friend ostream& operator<<(ostream& os, const Matrix& m);
};
五、最佳实践与陷阱
5.1 运算符重载原则
- 保持语义一致性:
operator+不应修改操作数 - 返回值优化:算术运算符返回新对象而非引用
- 关系运算符成对实现:实现
==时同时实现!= - 流操作符使用友元:保证访问权限
5.2 典型错误示例
// 错误:修改左操作数
Vector3D operator+(Vector3D& lhs, const Vector3D& rhs) {
lhs.x += rhs.x; // 错误!改变了左操作数
return lhs;
}
// 危险:返回局部变量引用
const string& operator+(const string& a, const string& b) {
string tmp = a + b;
return tmp; // 返回局部对象引用!
}
六、总结与提升
课后练习:
- 实现分数类Fraction,支持四则运算和约分
- 为自定义字符串类添加[]运算符和+运算符
- 设计支持链式调用的日志类,重载<<运算符
进阶话题:
- 类型转换运算符重载(operator int()等)
- new/delete运算符重载实现内存池
- C++20的三路比较运算符(<=>)
设计准则:
- 优先使用成员函数形式重载
+=、-=等修改型运算符 - 对需要对称性的运算符(如+、*)使用友元函数
- 避免重载逻辑运算符(&&、||)以免失去短路特性
- 保持运算符的数学含义,不要赋予反直觉的行为
通过合理运用运算符重载和友元机制,可以创建出表现力强、接口直观的自定义类型。建议结合单元测试验证运算符行为,并参考标准库(如string、complex等)的实现方式。记住:优秀的运算符重载应让使用者感觉在使用内置类型。