全局运营商和成员运营商之间的差异

时间:2009-07-17 18:45:44

标签: c++ operator-overloading

定义一个为类获取两个引用的全局运算符和定义只接受右操作数的成员运算符之间是否有区别?

全局:

class X
{
public:
    int value;
};

bool operator==(X& left, X& right) 
{
    return left.value == right.value;
};

成员:

class X
{
    int value;
    bool operator==( X& right) 
    {
        return value == right.value;
    };
}

5 个答案:

答案 0 :(得分:44)

使用非成员运算符(通常称为朋友)的一个原因是因为左侧是执行操作的左侧。 Obj::operator+适用于:

obj + 2

但是:

2 + obj

它不起作用。为此,您需要以下内容:

class Obj
{
    friend Obj operator+(const Obj& lhs, int i);
    friend Obj operator+(int i, const Obj& rhs);
};

Obj operator+(const Obj& lhs, int i) { ... }
Obj operator+(int i, const Obj& rhs) { ... }

答案 1 :(得分:9)

您最明智的选择是使其成为朋友功能

正如JaredPar所提到的,全局实现无法访问受保护的和私有的类成员,但成员函数也存在问题。

C ++将允许隐式转换函数参数,但不允许隐式转换 this

如果存在可以转换为X类的类型:

class Y
{
public:
    operator X();  // Y objects may be converted to X
};


X x1, x2;
Y y1, y2;

以下表达式中只有一些将使用成员函数进行编译。

x1 == x2;   // Compiles with both implementations
x1 == y1;   // Compiles with both implementations
y1 == x1;   // ERROR!  Member function can't convert this to type X
y1 == y2;   // ERROR!  Member function can't convert this to type X

为了实现两全其美,解决方案是将其作为朋友实施:

class X
{
    int value;

public:

    friend bool operator==( X& left, X& right ) 
    {
        return left.value == right.value;
    };
};

答案 2 :(得分:6)

总结Codebender的答案:

成员运营商不对称。编译器不能对左侧和右侧操作符执行相同数量的操作。

struct Example
{
   Example( int value = 0 ) : value( value ) {}
   int value;

   Example operator+( Example const & rhs ); // option 1
};
Example operator+( Example const & lhs, Example const & rhs ); // option 2
int main()
{
   Example a( 10 );
   Example b = 10 + a;
}

如果运算符是成员函数,则上面的代码将无法编译,而如果运算符是自由函数,它将按预期工作。

通常,一个常见的模式是实现必须是成员函数作为成员的运算符,其余作为委托成员运算符的自由函数:

class X
{
public:
   X& operator+=( X const & rhs );
};
X operator+( X lhs, X const & rhs )
{
   lhs += rhs; // lhs was passed by value so it is a copy
   return lhs;
}

答案 3 :(得分:5)

至少有一个区别。成员运营商受访问修饰符的约束,可以是公共的,受保护的或私有的。全局成员变量不受访问修饰符限制的约束。

如果要禁用某些运算符(如赋值)

,这将特别有用
class Foo { 
  ...
private:
  Foo& operator=(const Foo&); 
};

通过声明一个全局运算符,您可以实现相同的效果。但它会导致链接错误与编译错误(nipick:是的,它会导致Foo中的链接错误)

答案 4 :(得分:0)

这是一个真实的例子,区别并不明显:

class Base
{
public:
    bool operator==( const Base& other ) const
    {
        return true;
    }
};

class Derived : public Base
{
public:
    bool operator==( const Derived& other ) const
    {
        return true;
    }
};

Base() == Derived(); // works
Derived() == Base(); // error

这是因为第一种形式使用基类中的相等运算符,该运算符可以将其右侧转换为Base。但是派生的类相等运算符不能相反,因此会出错。

如果将基类的运算符声明为全局函数,则两个示例都可以工作(在派生类中不使用相等运算符也可以解决问题,但有时是必需的)。