运营商应该将其作为朋友还是作为会员?

这基本上是一个问题,是否有一个“正确”的方式来实现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) 

段落是一个string。

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

这篇文章是关于正确定义bool关系运算符的问题。

经营者:

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

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

有一个争论使这些独立的职能,因为这让自动转换转换双方,如果他们不是相同的types,而成员函数只允许rhs被自动转换。 我发现这是一个纸人的论点,因为你并不真正想要自动转换发生在第一位(通常)。 但如果这是你想要的东西(我不推荐它),那么使比较器独立可以是有利的。

stream运营商:

  • 运算符<<输出
  • 操作员>>input

当你使用这些stream操作符(而不是二进制移位)时,第一个参数是一个stream。 由于您无权访问stream对象(它不是您的修改对象),因此这些对象不能是成员操作符,因此它们必须在类的外部。 因此,他们必须是class上的朋友,或者可以使用公开的方法来为你做stream式传输。

对于这些对象来说,返回对stream对象的引用也是传统的,因此可以将stream操作链接在一起。

 #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; } 

你不能把它作为一个成员函数,因为这个隐含的参数是<< -operator的左边。 (因此,您需要将其作为成员函数添加到ostream类。不好:)

你可以做一个免费的function,而不需要friend吗? 这是我所喜欢的,因为它明确表示这是一个与ostream的集成,而不是你class级的核心function。

如果可能的话,作为非会员和非朋友的function。

正如Herb Sutter和Scott Meyers所描述的,非会员function中的非友元非成员函数,有助于增加封装。

在某些情况下,像C ++stream一样,你将不会有select,并且必须使用非成员函数。

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

关于运营商<<和>>原型

我相信你提出的问题是错误的。 例如;

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

我甚至不能开始思考这种方法如何在一个stream中工作。

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

假设您想使用types为T的stream对象。

而且你想从你的types对象的相关数据中提取/插入/插入段落。

generics操作符<<和>>函数原型

第一个function是:

 // 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 ; } 

generics操作符<<和>>方法原型

其次是作为方法:

 // 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 ++stream呢?

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

对于通用的basic_istream和basic_ostream

请注意,大小写的情况下,因为你不能修改C ++stream,你必须实现的function。 这意味着什么:

 // 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

以下代码仅适用于基于char的stream。

 // 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是正确的:我不build议使用基于char的示例。 它只是在这里给出,因为它更容易阅读。 因为只有在使用基于char的stream时才可行,所以在wchar_t代码常见的平台上(例如在Windows上)应该避免使用它。

希望这会有所帮助。

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

实际上,我已经将所有这些ostream输出免费函数收集在一个“ostreamhelpers”头文件和实现文件中,它使这个二级函数远离类的真正目的。

签名:

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

似乎相当可疑,这不符合stream公约,也不符合按位约定,所以它看起来像一个操作符重载滥用的情况下,运算operator <应返回booloperator <<应该可能返回别的东西。

如果你的意思是这样说:

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

然后,因为你不能添加函数ostream必要的function必须是一个自由的function,不pipe它是否是一个friend取决于它必须访问(如果它不需要访问私人或受保护的成员没有必要使它成为朋友)。

只是为了完成,我想补充一点,你确实可以在一个类中创build一个运算符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; } 

所以总结起来 – 你可以做到,但你最可能不应该:)

operator<<作为朋友function实施:

 #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; } 

OUTPUT:100 Hello 100 Hello按任意键继续…

这可以是朋友函数,只是因为对象在operator<<的右侧,而argument cout在左边。 所以这个不能是类的成员函数,它只能是一个朋友函数。

朋友操作员=与class级相同的权限

 friend std::ostream& operator<<(std::ostream& os, const Object& object) { os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl; return os; }