'friend'函数和<<运算符重载:为类重载运算符的正确方法是什么?

F. *_* P. 22 c++ class operator-overloading friend friend-class

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

这是我试图做的事情:

ostream & Score::operator<< (ostream & os, Score right)
{
    os << right.getPoints() << " " << right.scoreGetName();
    return os;
}
Run Code Online (Sandbox Code Playgroud)

以下是返回的错误:

score.h(30) : error C2804: binary 'operator <<' has too many parameters
Run Code Online (Sandbox Code Playgroud)

(此错误实际出现4次)

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

friend ostream & operator<< (ostream & os, Score right);
Run Code Online (Sandbox Code Playgroud)

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

为什么这样做,但前一段代码没有?

谢谢你的时间!

编辑

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

以下是完整的分数

#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
Run Code Online (Sandbox Code Playgroud)

sbi*_*sbi 61

注意:您可能希望查看运算符重载常见问题解答.


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

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

请注意,它不是成员函数.另请注意,它会使对象按const引用进行流式处理.那是因为您不想复制对象以便对其进行流式处理,并且您不希望流式传输也改变它.


有时您希望通过类的公共接口流式传输内部无法访问的对象,因此操作员无法获取它们.然后你有两个选择:将公共成员放入进行流式处理的类中

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

并从运营商那里打电话:

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

或者让操作员成为 friend

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

这样它就可以访问班级的私人部分:

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

  • 您应该将“operator&lt;&lt;”的声明与类的定义放在一起:即位于同一标头和同一命名空间中。这样您就不会忘记包含它,并且编译器在寻找可接受的重载时将能够选择它。 (2认同)
  • `T 类{};ostream&amp; 运算符 &lt;&lt;(ostream&amp;, const T&amp;);` 就像这样。您希望它与类本身存在于同一名称空间中,而不一定位于类本身内部。 (2认同)
  • @Francisco:C++ 只允许朋友触摸你的私人部位,这是社区中最古老的笑话之一。(因此,我向其他人道歉,因为我在这里提出了这个问题。)编译器需要一个 __declaration__ 才能允许您调用该运算符。链接器稍后需要(一个)__definition.__ 您最好将与类相关的运算符和其他自由函数放入与该类相同的命名空间中。这允许依赖于参数的查找来获取它们。有关此内容的更多详细信息,请参阅此处http://stackoverflow.com/questions/1410563/1410632#1410632。 (2认同)

Kat*_*ory 9

假设您想编写一个运算符重载,+这样您就可以相互添加两个Score对象,另一个可以添加int一个Score,然后添加第三个,这样就可以添加Score一个int.a Score是第一个参数的那个可以是Score的成员函数.但是a int是第一个参数的那个不能成为成员函数int,对吗?为了帮助您,您可以将它们写为免费功能.这就是使用此<<运算符所发生的情况,您无法添加成员函数,ostream因此您可以编写一个自由函数.这就是你带走这Score::部分时的意思.

现在为什么它必须是一个friend?它没有.你只是在调用公共方法(getPointsscoreGetName).你看到很多朋友操作符,因为他们喜欢直接与私有变量交谈.我这样做是可以的,因为它们是由维护课程的人编写和维护的.只是不要让朋友部分与成员函数vs免费功能部分混淆.


pkh*_*pkh 6

如果是operator<<示例中的成员函数,则会出现编译错误,因为您正在创建一个operator<<将a Score作为第一个参数(调用该方法的对象),然后在结尾处为其添加一个额外参数.

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

A a;
B b

a.operator+(b)
Run Code Online (Sandbox Code Playgroud)

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

A a;
B b

operator+(a, b);
Run Code Online (Sandbox Code Playgroud)

这是一个完整的例子,展示了这两种方式; 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;
}
Run Code Online (Sandbox Code Playgroud)