C++ “朋友”函数和<<运算符重载:为类重载运算符的正确方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2828280/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:11:42  来源:igfitidea点击:

'friend' functions and << operator overloading: What is the proper way to overload an operator for a class?

c++classoperator-overloadingfriendfriend-class

提问by F. P.

In a project I'm working on, I have a Scoreclass, defined below in score.h. I am trying to overload it so, when a <<operation is performed on it, _points + " " + _nameis printed.

在我正在进行的一个项目中,我有一个Score类,定义如下score.h。我试图重载它,以便<<在对其执行操作时_points + " " + _name打印。

Here's what I tried to do:

这是我尝试做的:

ostream & Score::operator<< (ostream & os, Score right)
{
    os << right.getPoints() << " " << right.scoreGetName();
    return os;
}

Here are the errors returned:

以下是返回的错误:

score.h(30) : error C2804: binary 'operator <<' has too many parameters

(This error appears 4 times, actually)

(这个错误实际上出现了 4 次)

I managed to get it working by declaring the overload as a friend function:

我设法通过将重载声明为友元函数来使其工作:

friend ostream & operator<< (ostream & os, Score right);

And removing the Score::from the function declaration in score.cpp (effectively not declaring it as a member).

Score::从 score.cpp 中的函数声明中删除(实际上不将其声明为成员)。

Why does this work, yet the former piece of code doesn't?

为什么这行得通,而前一段代码却行不通?

Thanks for your time!

谢谢你的时间!

EDIT

编辑

I deleted all mentions to the overload on the header file... yet I get the following (and only) error. binary '<<' : no operator found which takes a right-hand operand of type 'Score' (or there is no acceptable conversion)How come my test, in main(), can't find the appropriate overload? (it's not the includes, I checked)

我删除了头文件上所有对重载的提及......但我收到以下(也是唯一的)错误。binary '<<' : no operator found which takes a right-hand operand of type 'Score' (or there is no acceptable conversion)为什么我的测试在 main() 中找不到合适的重载?(这不是包含,我检查过)

Below is the full score.h

以下是满分.h

#ifndef SCORE_H_
#define SCORE_H_

#include <string>
#include <iostream>
#include <iostream>

using std::string;
using std::ostream;

class Score
{

public:
    Score(string name);
    Score();
    virtual ~Score();
    void addPoints(int n);
    string scoreGetName() const;
    int getPoints() const;
    void scoreSetName(string name);
    bool operator>(const Score right) const;

private:
    string _name;
    int _points;

};
#endif

回答by sbi

Note:You might want to look at the operator overloading FAQ.

注意:您可能需要查看运算符重载 FAQ



Binary operators can either be members of their left-hand argument's class or free functions. (Some operators, like assignment, must be members.) Since the stream operators' left-hand argument is a stream, stream operators either have to be members of the stream class or free functions. The canonical way to implement operator<<for any type is this:

二元运算符可以是其左侧参数类的成员,也可以是自由函数。(某些运算符,如赋值,必须是成员。)由于流运算符的左侧参数是一个流,因此流运算符要么必须是流类的成员,要么是自由函数。operator<<为任何类型实现的规范方法是这样的:

std::ostream& operator<<(std::ostream& os, const T& obj)
{
   // stream obj's data into os
   return os;
}

Note that it is nota member function. Also note that it takes the object to stream per constreference. That's because you don't want to copy the object in order to stream it and you don't want the streaming to alter it either.

请注意,它不是成员函数。另请注意,每个const引用都需要对象进行流式处理。那是因为您不想复制对象以流式传输它,并且您也不希望流式传输更改它。



Sometimes you want to stream objects whose internals are not accessible through their class' public interface, so the operator can't get at them. Then you have two choices: Either put a public member into the class which does the streaming

有时,您希望流式传输无法通过其类的公共接口访问其内部结构的对象,因此操作员无法获取它们。那么你有两个选择:要么将一个公共成员放入执行流媒体的类中

class T {
  public:
    void stream_to(std::ostream&) const {os << obj.data_;}
  private:
    int data_;
};

and call that from the operator:

并从运营商那里调用:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   obj.stream_to(os);
   return os;
}

or make the operator a friend

或使操作员成为 friend

class T {
  public:
    friend std::ostream& operator<<(std::ostream&, const T&);
  private:
    int data_;
};

so that it can access the class' private parts:

以便它可以访问类的私有部分:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   os << obj.data_;
   return os;
}

回答by Kate Gregory

Let's say you wanted to write an operator overload for +so you could add two Scoreobjects to each other, and another so you could add an intto a Score, and a third so you could add a Scoreto an int. The ones where a Scoreis the first parameter can be member functions of Score. But the one where an intis the first parameter can't become member functions of int, right? To help you with that, you're allowed to write them as free functions. That is what is happening with this <<operator, you can't add a member function to ostreamso you write a free function. That's what it means when you take away the Score::part.

假设您想为其编写一个运算符重载,+以便您可以将两个Score对象相互添加,另一个以便您可以将 an 添加int到 a Score,以及第三个以便您可以将 a 添加Score到 an int。其中 aScore为第一个参数的可以是 Score 的成员函数。但是第一个int参数an不能成为 的成员函数int,对吗?为了帮助您,您可以将它们编写为免费函数。这就是这个<<运算符发生的事情,你不能添加成员函数,ostream所以你写了一个自由函数。这就是你拿走Score::零件时的意思。

Now why does it have to be a friend? It doesn't. You're only calling public methods (getPointsand scoreGetName). You see lots of friend operators because they like to talk directly to the private variables. It's ok by me to do that, because they are written and maintained by the person maintaing the class. Just don't get the friend part muddled up with the member-function-vs-free-function part.

现在为什么它必须是一个friend?它没有。您只调用公共方法(getPointsscoreGetName)。您会看到很多友元运算符,因为它们喜欢直接与私有变量对话。我可以这样做,因为它们是由维护班级的人编写和维护的。只是不要让朋友部分与成员函数与自由函数部分混淆。

回答by pkh

You're getting compilation errors when operator<<is a member function in the example because you're creating an operator<<that takes a Scoreas the first parameter (the object the method's being called on), and then giving it an extra parameter at the end.

operator<<是示例中的成员函数时,您会收到编译错误,因为您正在创建一个operator<<将 aScore作为第一个参数(调用该方法的对象),然后在最后为其提供一个额外参数。

When you're calling a binary operator that's declared as a member function, the left side of the expression is the object the method's being called on. e.g. a + bmight works like this:

当您调用声明为成员函数的二元运算符时,表达式的左侧是调用该方法的对象。例如a + b可能像这样工作:

A a;
B b

a.operator+(b)

It's typically preferable to use non-member binary operators (and in some cases -- e.g. operator<<for ostreamis the only way to do it. In that case, a + bmight work like this:

通常最好使用非成员二元运算符(并且在某些情况下 - 例如operator<<forostream是唯一的方法。在这种情况下,a + b可能会像这样工作:

A a;
B b

operator+(a, b);

Here's a full example showing both ways of doing it; main() will output '55' three times:

这是一个完整的例子,展示了两种方法;main() 将输出 '55' 三次:

#include <iostream>

struct B
{
    B(int b) : value(b) {}
    int value;
};


struct A
{
    A(int a) : value(a) {}
    int value;

    int operator+(const B& b) 
    {
        return this->value + b.value;
    }
};

int operator+(const A& a, const B& b)
{
    return a.value + b.value;
}

int main(int argc, char** argv)
{
    A a(22);
    B b(33);

    std::cout << a + b << std::endl;
    std::cout << operator+(a, b) << std::endl;
    std::cout << a.operator+(b) << std::endl;

    return 0;
}