运算符重载:成员函数与非成员函数?


121

我读到一个声明为成员函数的重载运算符是不对称的,因为它只能有一个参数,而另一个自动传递的参数是this指针。因此,没有标准可以比较它们。另一方面,声明为a的重载运算符friend对称的,因为我们传递了相同类型的两个参数,因此可以对其进行比较。

我的问题是,当我仍然可以将指针的左值与引用进行比较时,为什么首选朋友?(使用非对称版本可获得与对称相同的结果)为什么STL算法仅使用对称版本?


11
您的问题实际上仅涉及二进制运算符。并非所有重载运算符都限于单个参数。()运算符可以采用任意数量的参数。另一方面,一元运算符不能有任何参数。
Charles Salvia


Answers:


148

如果将运算符重载函数定义为成员函数,则编译器会将类似的表达式s1 + s2转换为s1.operator+(s2)这意味着,运算符重载的成员函数在第一个操作数上被调用。这就是成员函数的工作方式!

但是,如果第一个操作数不是类怎么办?如果我们想重载第一个操作数不是类类型的运算符,那就说一个主要问题double所以你不能这样写 10.0 + s2。但是,您可以为表达式编写运算符重载成员函数s1 + 10.0

为了解决此排序问题,我们将运算符重载函数定义为friend需要访问private成员的函数。让它friend只有当它需要访问私有成员。否则,只需使其成为非朋友非成员函数即可改善封装!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

阅读这些:
操作数排序中的一个小问题
非成员函数如何改善封装


2
friend仅当它需要访问私有成员时,以及当您不/无聊编写访问器时,才进行此操作,对吗?
badmaash 2011年

4
@Abhi:选择:改进封装与惰性书写习惯!
Nawaz

6
@matthias,并非所有运算符都是可交换的。一个简单的例子是a/b
edA-qa mort-ora-y 2012年

3
避免使您的非成员操作员需要的一种常见方法friend是根据操作分配操作员(几乎可以肯定是公共成员)来实施它们。例如,您可以定义T T::operator+=(const T &rhs)为成员,然后将非成员定义T operator(T lhs, const T &rhs)return lhs += rhs;。非成员函数应在与类相同的名称空间中定义。
阿德里安·麦卡锡

2
@ricky:但是如果lhs是一个副本(如我的评论所示),那么lhs更改的事实并不重要。
阿德里安·麦卡锡

20

全局运算符重载和成员函数运算符重载之间并不一定要区分friend运算符重载和成员函数运算符重载。

首选使用全局运算符重载的一个原因是,如果要允许在类类型出现在二进制运算符右侧的表达式。例如:

Foo f = 100;
int x = 10;
cout << x + f;

这仅在以下情况下有效:

Foo运算符+(int x,const Foo&f);

注意,全局运算符重载不一定是一个friend函数。仅在需要访问的私有成员时才需要这样做Foo,但并非总是如此。

无论如何,如果Foo只有成员函数运算符重载,例如:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

...那么我们将只能有一个表达式,其中Foo实例出现在加号运算符的左侧


3
+1用于区分成员函数和非成员函数,而不是成员和朋友函数。我想今天我们会说“全局或命名空间范围”。
阿德里安·麦卡锡
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.