C++ 全局运算符和成员运算符之间的差异
定义一个类具有两个引用的全局运算符和定义一个仅具有正确操作数的成员运算符之间有区别吗 全球:C++ 全局运算符和成员运算符之间的差异,c++,operator-overloading,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) {
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;
};
}
至少有一个区别。成员运算符受访问修饰符的约束,可以是公共的、受保护的或私有的。全局成员变量不受访问修饰符限制 当您想要禁用某些运算符(如赋值)时,这尤其有用
class Foo {
...
private:
Foo& operator=(const Foo&);
};
您可以通过使用声明的only全局运算符来实现相同的效果。但是它会导致链接错误而不是编译错误(nipick:是的,它会导致Foo中的链接错误)使用非成员运算符(通常声明为friends)的一个原因是,左侧是执行该操作的一侧<代码>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) { ... }
您最明智的选择是将其设置为好友功能 正如JaredPar提到的,全局实现无法访问受保护的和私有的类成员,但是成员函数也有问题 C++将允许隐式转换函数参数,但不允许隐式转换此 如果存在可转换为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;
};
};
总结一下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;
}
在上面的代码中,如果操作符是成员函数,那么它将无法编译,而如果操作符是自由函数,那么它将按预期工作
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
{
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;
}
下面是一个实际的例子,其中差异并不明显:
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
。但是派生类相等运算符不能做相反的事情,因此会出现错误
如果基类的运算符被声明为全局函数,则这两个示例都会起作用(在派生类中没有相等运算符也会解决问题,但有时是需要的)。声明的仅全局运算符将不会链接(即没有程序),而私有成员运算符将链接。只有非法使用私有成员才会导致编译错误。正确吗?只要确定可以,就最好不仅声明引用参数,而且声明常量参数。比较运算符可以是常量,并且可以确定使用常量引用。(若并没有别的,那个就是对编译器的承诺,允许它进行更多的优化。)