C++运算符重载的一些小细节
一个通用的运算符重载助记公式
// 对于前置的一元运算符@A:
type Foo::operator@(); // 成员函数
type operator@(Foo& ); // 非成员函数
// 对于后置的一元运算符A@
// 另外这里还要返回自加之前的"旧"值,所以返回一份拷贝
type Foo::operator@(int); // 成员函数
type operator@(const Foo&, int); // 非成员函数
// 对于后置的二元运算符A@
type Foo::operator@(const Foo&); //成员函数
type operator@(const Foo&, const Foo&); // 非成员函数
哑参数
在后置一元运算符的重载函数声明中,int只起到区分前置后置的作用,没有什么实际效果,称为“哑参数”
操作符重载的返回值
所有的操作符的重载函数的返回值都没有指定,编译器会根据实际的代码来进行上面所声明的运算符的匹配调用,如:
@a => (a).operator@() // 成员函数的情况
或者
@a => operator@(a) // 非成员函数的情况
a@b => (a).operator@(b) // 成员函数的情况
或者
a@b => operator@(a,b) // 非成员函数的情况
所以这里的操作符重载的返回值可以根据语义自行设计。考虑设计Matrix的operator*(),我们都知道矩阵和矩阵之间是可以相乘的,例如A = B * C这样的语句,被编译器这样调用:
A = B.operator*(C);
如果operator()的返回类型是Matrix值类型,那么就意味着在operator*()内部的实现中需要进行内存的额外拷贝,但这样的好处是无论做什么样的运算,都不会影响到B的内存值。就像这样:
Matrix Matrix::operator*(const Matrix& another)
{
Matrix ret;
// some logic...
return ret;
}
Matrix A = B.operator*(C);
为了避免函数返回ret的额外开销,这里在实际工程中可以使用C++11的移动语义完成。
当然,为了避免额外的内存拷贝,也可以返回Matrix&类型。但是问题在于,operator*()会创造一个额外的变量A,不额外分配的话A的内存是从哪儿来?当编译器进行如下调用时:
Matrix& Matrix::operator*(const Matrix& another)
{
// 一些在原地(in place)计算的操作
this.resouce = this.resouce * another.resouce;
return *this;
}
Matrix &A = B.operator*(C);
可以发现,这样的操作影响到了B本身。
以上根据矩阵类本身的语义对operator@返回值的设计进行了简单的示例。operator@的返回值并没有明确的规定,所以需要根据需求自己设计。
为什么建议operator=返回Foo&?
待补充…
什么时候operator@返回Foo或者Foo&?
待补充…