c++中的操作符重载

operator overloading in c++

本文关键字:重载 操作符 c++      更新时间:2023-10-16

如果我想重载操作符+,哪个原型是正确的?

  1. D operator+(const D& lhs, const D& rhs);
    然后将其声明为d的友元函数

  2. D operator+(const D& s);
    然后将其声明为d的成员函数。

第一个是正确的,第二个是明显错误的。您可以通过这样写来改进第二个

D operator+(const D& s) const;

但它仍然是错误的。原因是在第二个版本中,编译器将对+操作符的左右两侧应用不同的规则。例如给定以下代码

class C
{
};
class D
{
public:
  D(const C&);
};
C c;
D d;
d = d + c; // legal with both versions
d = c + d; // not legal with the second version

区别在于编译器会从C对象为方法或函数参数创建临时D对象,但不会在临时对象上调用方法。

简而言之,第一个版本平等地对待左边和右边,因此更符合编码人员的期望。

第二个应该是

D operator+(const D& s) const;

都可以

至于第一个需要成为朋友:只有当它确实需要访问任何私有内容时。通常您可以根据公共接口实现它,通常使用相应的operator+=:

D operator+(D lhs, const D& rhs) {  //copy left-hand side
    return lhs += rhs;  //add the right-hand side and return by value
}

我建议您遵循第三条路径:将operator+=作为成员函数实现,然后按照前面的方式实现operator+:

D operator+=( D lhs, D const & rhs ) {
   lhs += rhs;
   return lhs;
}

第三种方法的优点是,使用基本相同的代码,您提供++=,并且您可以将operator+实现为一个自由函数,这从对称的角度来看是一个优势,如果您的类具有隐式转换,它将允许d + tt + d用于类型为D的任何对象d和类型为D的任何其他对象t隐式转换。成员函数版本将只应用右边的转换,这意味着允许使用d + t,但不允许使用t + d

[自我宣传警告]你可以在这里阅读关于这个特定问题的更长的解释

从第一个开始。但是,如果它需要访问私有成员,那么只有将其设置为friend,否则将其设置为非友元函数。

我认为两者都是正确的。但有一件事你忽略了(可能适用,也可能不适用),如果左边的值可以是D以外的东西(比如整数或其他东西),那么选项1适用于此,例如

D operator+(int lhs, const D& rhs);

然后你可以这样做:

D d1;
D d2 = 5 + d1;

只有计划通过函数访问类的私有变量时,才需要将函数声明为类的友元。在第一个原型中,您不需要将函数声明为友元,因为您已经传入了计划使用的所有值。在第二个原型中,您可以将函数声明为友元,因为您将需要访问另外一个变量,但将函数声明为成员是更好的实践,也更容易。

两种方法几乎都是正确的。这是两种几乎相同的方法。但是当您需要将二进制操作符应用于D以外的其他类型(例如int+D)时,您需要使用第二个操作符。

如果选项1不需要访问private成员,它甚至不必是友元。

选项2必须稍微修正一下。您缺少D:: .

D D::operator+(const D& s) const {
}

最小意外原则表示您的重载应该表现良好或多或少类似于内置操作符。通常的解是根本不实现operator+,而是实现:

D& operator+=( D const& rhs );

作为成员,然后派生如下:

template<typename T>
class ArithmeticOperators
{
    friend T operator+( T const& lhs, T const& rhs )
    {
        T result( lhs );
        result += rhs;
        return result;
    }
    //  Same thing for all of the other binary operators...
};
这样,您就不必每次定义时都重写相同的东西一个重载算术运算符的类,可以保证++=的语义对应

(上面的friend只是为了让你把函数,以及它的实现,在类本身中,ADL将找到

两者都是正确的(在修复了其他人指出的const正确性问题之后),但我推荐使用成员操作符。如果是虚拟的,它可以被多态行为覆盖,与类有更好的内聚性,因此更容易维护和记录。如果可以的话,尽量避免使用好友功能。

对于"derived = base + derived"的情况,我建议不要将值语义操作符和多态性混合使用,由于各种隐式转换序列和对象切片,它可能会产生不可预见的后果。这个例子可能相当于derived = Derived(base) + derived,但如果base有操作符+,它也可以是derived = Derived(base + Base(derived))

只使用显式转换和强制类型转换,就不会遇到任何神秘的奇怪行为。在为多态类实现操作符之前要三思。

如果D具有隐式构造函数,则第一个具有不同的行为。

考虑
struct D
{
    D(int);
};
D operator+(const D&, const D&);

则可以执行1 + dd + 1,而成员operator+不能执行此操作(lhs必须是D,并且不能进行转换)