在C++中,运算符重载可以让我们自定义类型表现出类似内置类型的行为。箭头运算符(->)和函数调用运算符(())属于比较特殊且实用的可重载运算符,常用于智能指针、代理模式和函数对象等场景。理解它们的重载方式,是掌握C++高级编程技巧的重要一步。

箭头运算符的重载
箭头运算符只能以成员函数的形式重载,且必须返回一个指针,或者返回一个重载了箭头运算符的对象。编译器会持续对返回结果应用箭头运算符,直到得到原生指针为止。
基本语法
返回类型通常为类所管理的对象指针类型,语法形式如下:
#include <iostream>
#include <string>
class Data {
public:
void show() {
std::cout << "Data::show called" << std::endl;
}
};
class SmartPtr {
Data* ptr;
public:
SmartPtr(Data* p) : ptr(p) {}
// 重载箭头运算符,返回原生指针
Data* operator->() {
return ptr;
}
~SmartPtr() { delete ptr; }
};
int main() {
SmartPtr sp(new Data());
sp->show(); // 实际调用 operator->() 返回的指针的 show
return 0;
}
链式箭头重载
如果返回的是另一个重载了箭头的对象,编译器会递归解析,直到获得指针。这种特性常用于代理类设计。
函数调用运算符的重载
函数调用运算符同样只能作为成员函数重载,它让对象可以像函数一样使用,这样的对象被称为仿函数(functor)。
基本语法
#include <iostream>
class Adder {
int base;
public:
Adder(int b) : base(b) {}
// 重载函数调用运算符
int operator()(int x) const {
return base + x;
}
};
int main() {
Adder add5(5);
std::cout << add5(10) << std::endl; // 输出 15
return 0;
}
支持多个参数的调用
函数调用运算符可以接受任意数量和类型的参数,也可以有默认参数,非常灵活。
#include <iostream>
class Multiplier {
public:
int operator()(int a, int b) const {
return a * b;
}
};
int main() {
Multiplier mul;
std::cout << mul(3, 4) << std::endl; // 输出 12
return 0;
}
注意事项与对比
这两种运算符重载时都需要注意以下几点:
- 二者都只能定义为成员函数,不能定义为全局函数。
- 箭头运算符应保证返回有效指针或同样重载箭头的对象,避免空指针解引用。
- 函数调用运算符通常应声明为 const 成员,如果不修改对象状态,以提升可用性。
| 运算符 | 典型用途 | 返回要求 |
|---|---|---|
| operator->() | 智能指针、代理类 | 指针或重载箭头对象 |
| operator()() | 仿函数、回调对象 | 任意类型 |
总结
重载箭头运算符与函数调用运算符是C++运算符重载中的高级技巧。箭头运算符让我们自定义类型拥有指针般的访问体验,函数调用运算符则赋予对象函数行为。合理运用它们,可以写出表达力强且易于复用的C++代码。