'朋友'功能和<<运算符重载:为类重载运算符的正确方法是什么?

时间:2010-05-13 15:59:24

标签: 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;
}

以下是返回的错误:

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

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

我设法通过将重载声明为友元函数来实现它:

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

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

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

谢谢你的时间!

修改

我删除了对头文件重载的所有提及...但是我得到以下(并且只有)错误。 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

3 个答案:

答案 0 :(得分:57)

注意:您可能需要查看operator overloading FAQ


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

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

请注意, 是会员功能。另请注意,每个const引用都会传输对象。那是因为您不想复制对象以便对其进行流式处理,并且您不希望流式传输也改变它。


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

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

并从运营商那里打电话:

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

或让运营商成为friend

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

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

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

答案 1 :(得分:9)

假设你想为+编写一个运算符重载,这样你就可以相互添加两个Score个对象,另一个可以向{{1}添加int第三个,以便您可以Score添加Scoreint是第一个参数的那个可以是Score的成员函数。但Score是第一个参数的那个不能成为int的成员函数,对吗?为了帮助您,您可以将它们写为免费功能。这就是这个int运算符所发生的情况,你不能将成员函数添加到<<,所以你编写了一个自由函数。当你拿走ostream部分时,这意味着什么。

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

答案 2 :(得分:6)

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

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

A a;
B b

a.operator+(b)

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

A a;
B b

operator+(a, b);

这是一个完整的例子,展示了这两种方式; 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;
}