150

这基本上是一个问题,是否有“正确”的实施方式operator<<?读到这里,我可以看到类似:

friend bool operator<<(obj const& lhs, obj const& rhs);

比类似的东西更受欢迎

ostream& operator<<(obj const& rhs);

但我不太明白为什么要使用其中一个。

我的个人情况是:

friend ostream & operator<<(ostream &os, const Paragraph& p) {
    return os << p.to_str();
}

但我可能会这样做:

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

我应该根据什么理由做出这个决定?

注意

 Paragraph::to_str = (return paragraph) 

其中段落是一个字符串。

4

8 回答 8

140

这里的问题在于您对所链接文章的解释。

平等

本文是关于在正确定义布尔关系运算符时遇到问题的人。

运营商:

  • 平等 == 和 !=
  • 关系 < > <= >=

这些运算符在比较相同类型的两个对象时应该返回一个布尔值。通常最容易将这些运算符定义为类的一部分。这是因为类自动成为自身的朋友,因此 Paragraph 类型的对象可以相互检查(甚至是相互的私有成员)。

制作这些独立函数有一个论据,因为如果它们不是同一类型,这允许自动转换转换两边,而成员函数只允许自动转换 rhs。我发现这是一个纸人论点,因为你真的不希望首先发生自动转换(通常)。但是,如果这是您想要的(我不推荐),那么使比较器独立存在可能是有利的。

流媒体

流运算符:

  • 运算符 << 输出
  • 运算符 >> 输入

当您将它们用作流运算符(而不是二进制移位)时,第一个参数是流。由于您无权访问流对象(它不是您可以修改的),因此它们不能是成员运算符,它们必须在类的外部。因此,他们必须要么是班级的朋友,要么有权访问将为您进行流式传输的公共方法。

这些对象返回对流对象的引用也是传统的做法,因此您可以将流操作链接在一起。

#include <iostream>

class Paragraph
{
    public:
        explicit Paragraph(std::string const& init)
            :m_para(init)
        {}

        std::string const&  to_str() const
        {
            return m_para;
        }

        bool operator==(Paragraph const& rhs) const
        {
            return m_para == rhs.m_para;
        }
        bool operator!=(Paragraph const& rhs) const
        {
            // Define != operator in terms of the == operator
            return !(this->operator==(rhs));
        }
        bool operator<(Paragraph const& rhs) const
        {
            return  m_para < rhs.m_para;
        }
    private:
        friend std::ostream & operator<<(std::ostream &os, const Paragraph& p);
        std::string     m_para;
};

std::ostream & operator<<(std::ostream &os, const Paragraph& p)
{
    return os << p.to_str();
}


int main()
{
    Paragraph   p("Plop");
    Paragraph   q(p);

    std::cout << p << std::endl << (p == q) << std::endl;
}
于 2008-10-25T21:53:00.707 回答
55

您不能将其作为成员函数执行,因为隐式参数是 -运算符this的左侧。<<(因此,您需要将其作为成员函数添加到ostream-class。不好 :)

你可以在没有friending 的情况下将它作为一个免费功能来做吗?这就是我更喜欢的,因为它清楚地表明这是与您的类的集成ostream,而不是您的类的核心功能。

于 2008-10-25T18:24:38.317 回答
31

如果可能,作为非会员和非朋友功能。

正如 Herb Sutter 和 Scott Meyers 所描述的,与成员函数相比,更喜欢非朋友非成员函数,以帮助增加封装。

在某些情况下,例如 C++ 流,您没有选择权,必须使用非成员函数。

但是,这并不意味着你必须让这些函数成为你的类的朋友:这些函数仍然可以通过你的类访问器访问你的类。如果您以这种方式成功编写了这些函数,那么您就赢了。

关于运算符 << 和 >> 原型

我相信你在问题中给出的例子是错误的。例如;

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

我什至无法开始思考这种方法如何在流中工作。

以下是实现 << 和 >> 运算符的两种方法。

假设您想使用类型为 T 的类流对象。

并且您想从/插入到 T 中您的段落类型对象的相关数据。

通用运算符 << 和 >> 函数原型

第一个是作为函数:

// T << Paragraph
T & operator << (T & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// T >> Paragraph
T & operator >> (T & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return p_oInputStream ;
}

通用运算符 << 和 >> 方法原型

第二个是作为方法:

// T << Paragraph
T & T::operator << (const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return *this ;
}

// T >> Paragraph
T & T::operator >> (const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return *this ;
}

请注意,要使用此表示法,您必须扩展 T 的类声明。对于 STL 对象,这是不可能的(您不应该修改它们......)。

如果 T 是 C++ 流呢?

以下是 C++ 流的相同 << 和 >> 运算符的原型。

对于通用 basic_istream 和 basic_ostream

请注意,这是流的情况,因为您不能修改 C++ 流,所以必须实现函数。这意味着类似:

// OUTPUT << Paragraph
template <typename charT, typename traits>
std::basic_ostream<charT,traits> & operator << (std::basic_ostream<charT,traits> & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> Paragraph
template <typename charT, typename traits>
std::basic_istream<charT,traits> & operator >> (std::basic_istream<charT,traits> & p_oInputStream, const CMyObject & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

对于 char istream 和 ostream

以下代码仅适用于基于字符的流。

// OUTPUT << A
std::ostream & operator << (std::ostream & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> A
std::istream & operator >> (std::istream & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

Rhys Ulerich 评论说基于字符的代码只是它上面的通用代码的“专业化”。当然,Rhys 是对的:我不推荐使用基于 char 的示例。仅在此处给出它是因为它更易于阅读。因为它只有在你只使用基于字符的流时才可行,所以你应该避免在 wchar_t 代码很常见的平台上(即在 Windows 上)。

希望这会有所帮助。

于 2008-10-25T22:22:15.123 回答
11

它应该作为一个免费的、非友元函数来实现,特别是如果像现在的大多数事情一样,输出主要用于诊断和日志记录。为所有需要进入输出的东西添加 const 访问器,然后让输出器调用它们并进行格式化。

实际上,我已经开始在“ostreamhelpers”头文件和实现文件中收集所有这些 ostream 输出自由函数,它使次要功能远离类的真正目的。

于 2008-10-25T18:59:38.573 回答
8

签名:

bool operator<<(const obj&, const obj&);

似乎相当可疑,这不符合stream约定也不符合按位约定,因此它看起来像是运算符重载滥用的情况,operator <应该返回booloperator <<可能应该返回其他内容。

如果你的意思是这样说:

ostream& operator<<(ostream&, const obj&); 

然后,由于您不能ostream根据需要添加函数,因此该函数必须是自由函数,它是否friend取决于它必须访问的内容(如果它不需要访问私有或受保护的成员,则无需创建它朋友)。

于 2008-10-25T18:29:00.447 回答
3

为了完整起见,我想补充一点,您确实可以在类中创建一个运算符ostream& operator << (ostream& os)并且它可以工作。据我所知,使用它不是一个好主意,因为它非常复杂且不直观。

假设我们有这段代码:

#include <iostream>
#include <string>

using namespace std;

struct Widget
{
    string name;

    Widget(string _name) : name(_name) {}

    ostream& operator << (ostream& os)
    {
        return os << name;
    }
};

int main()
{
    Widget w1("w1");
    Widget w2("w2");

    // These two won't work
    {
        // Error: operand types are std::ostream << std::ostream
        // cout << w1.operator<<(cout) << '\n';

        // Error: operand types are std::ostream << Widget
        // cout << w1 << '\n';
    }

    // However these two work
    {
        w1 << cout << '\n';

        // Call to w1.operator<<(cout) returns a reference to ostream&
        w2 << w1.operator<<(cout) << '\n';
    }

    return 0;
}

所以总结一下 - 你可以做到,但你很可能不应该:)

于 2017-06-02T09:42:08.513 回答
0

operator<<实现为友元函数:

#include <iostream>
#include <string>
using namespace std;

class Samp
{
public:
    int ID;
    string strName; 
    friend std::ostream& operator<<(std::ostream &os, const Samp& obj);
};
 std::ostream& operator<<(std::ostream &os, const Samp& obj)
    {
        os << obj.ID<< “ ” << obj.strName;
        return os;
    }

int main()
{
   Samp obj, obj1;
    obj.ID = 100;
    obj.strName = "Hello";
    obj1=obj;
    cout << obj <<endl<< obj1;

} 

输出:
100 你好
100 你好

这只能是友元函数,因为对象在右侧,operator<<而参数cout在左侧。所以这不能是类的成员函数,它只能是友元函数。

于 2012-01-20T11:15:09.857 回答
0

朋友运算符 = 与班级同等的权利

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}
于 2016-02-01T12:02:36.600 回答