Operator & Cast
答应我不要再考四种 cast 的区别了好吗?好的。
operator
在 C++ 中,我们可以让自定义类型也能够使用运算符。实际上你可能已经使用过类似的机制,例如 std::string 的 + 运算符。
| int main() {
std::string s1 = "Hello, ";
std::cout << s1 + "world!\n";
}
|
注意到我们可以使用 s1 + "world!\n" 这样的语法,这就是运算符重载的效果。
运算符重载并不一定与重载相关
前者的“重载”表示我们能将 C++ 的内置运算符(如 +、- 等)应用在自定义类型上,而后者的“重载”表示同一函数名可以有不同的实现。当然——运算符重载也可以被重载。
接下来我们来关注如何为自定义类型实现运算符重载。考虑下面的复数类:
| struct complex {
const double real;
const double imag;
complex(double r, double i) : real {r}, imag {i} {}
};
|
通常有两种做法。第一种将运算符重载作为全局函数来实现:
| complex operator+(const complex &lhs, const complex &rhs) {
return complex {lhs.real + rhs.real, lhs.imag + rhs.imag};
}
|
对于这种方式,运算符的操作数都需要作为参数传入。如果我们有两个 complex 对象 w 和 z,那么 w + z 会被编译器视作 operator+(w, z) 的函数调用。因此它也遵循函数调用的查找规则(例如在 namespace 中)。作为外部函数,它无法访问类的私有成员变量,但可以通过 friend 声明来实现。
第二种是将运算符重载作为类的成员函数来实现:
| struct complex {
const double real;
const double imag;
complex(double r, double i) : real {r}, imag {i} {}
complex operator+(const complex &other) const {
return complex {real + other.real, imag + other.imag};
}
};
|
对于这种方式,运算符的第一个操作数就是当前对象,第二个操作数(若有)就是参数 other。w + z 会被编译器视作 w.operator+(z) 的成员函数调用。因此,它也受到访问控制的影响。这种方式的优点是可以直接访问类的成员变量,但它仍然会有一些限制…
考虑交换律。我们现在为 complex 类实现与 double 类型的加法:
| struct complex {
const double real;
const double imag;
complex(double r, double i) : real {r}, imag {i} {}
complex operator+(const complex &other) const {
return complex {real + other.real, imag + other.imag};
}
complex operator+(double d) const {
return complex {real + d, imag};
}
};
int main() {
complex w {1, 2};
w + 3; // OK: w.operator+(3)
3 + w; // Error: ?
};
|
这种情况下,3 + w 就无法通过编译了。我们必须单独为其实现一个全局 operator+ 函数:
| complex operator+(double d, const complex &c) {
return complex {d + c.real, c.imag};
// or
return c + d; // OK: c.operator+(d)
}
|
实际上 std::string 的 + 运算符也是这样实现的。
基本上任何现有的运算符都可以被重载,诸如加减乘除、位运算、逻辑运算、关系运算等,甚至 new、delete、解引用运算符 * 和取址运算符 & 等等都可以被重载。不能被重载的运算符有:
.(成员访问)
.*(成员指针访问)
::(作用域解析)
?:(三元运算符)
以及一些关键字,如 sizeof 和 typeid 等。不能通过 operator 定义新的运算符。
自定义后缀运算符
explicit
类型转换
static_cast
dynamic_cast
const_cast
reinterpret_cast