运算符<<应该作为友元函数还是作为成员函数实现?
Should operator<< be implemented as a friend or as a member function?
这基本上是一个问题,有没有"正确"的方式来实现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)
其中段落是字符串。
这里的问题在于您对链接文章的解释。
平等
本文是关于在正确定义布尔关系运算符时遇到问题的人。
操作员:
- 相等 == 和 !=
- 关系 <> <=>=
这些运算符在比较两个相同类型的对象时应返回一个布尔值。通常最简单的方法是将这些运算符定义为类的一部分。这是因为类自动成为其自身的朋友,因此 Passage 类型的对象可以相互检查(甚至是彼此的私有成员)。
有一个论点是制作这些独立函数的,因为这允许自动转换转换两侧,如果它们不是相同的类型,而成员函数只允许 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;
}
不能将其作为成员函数执行,因为隐式 this
参数是 <<
-运算符的左侧。(因此,您需要将其作为成员函数添加到 ostream
-类中。不好:)
你能在不friend
的情况下将其作为免费功能来做吗?这是我更喜欢的,因为它清楚地表明这是与ostream
的集成,而不是类的核心功能。
如果可能,作为非成员和非友元函数。
正如 Herb Sutter 和 Scott Meyers 所描述的那样,更喜欢非友元非成员函数而不是成员函数,以帮助增加封装。
在某些情况下,例如C++流,您将无法选择,必须使用非成员函数。
但是,这并不意味着您必须使这些函数成为类的好友:这些函数仍然可以通过类访问器访问您的类。如果你成功地以这种方式编写了这些函数,那么你就赢了。
关于操作员<<和>>原型
我相信你在问题中给出的例子是错误的。例如;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
我什至无法开始思考这种方法如何在流中工作。
以下是实现<<和>>运算符的两种方法。
假设您要使用类型 T 的类似流的对象。
并且您要从 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 上)使用它。
希望这会有所帮助。
它应该作为一个免费的、非友元函数来实现,特别是如果像现在的大多数事情一样,输出主要用于诊断和日志记录。 为需要进入输出的所有内容添加 const 访问器,然后让输出器调用这些内容并进行格式化。
我实际上已经将所有这些 ostream 输出自由函数收集在一个"ostreamhelpers"头文件和实现文件中,它使辅助功能远离类的真正目的。
签名:
bool operator<<(const obj&, const obj&);
似乎相当可疑,这不符合stream
约定或按位约定,因此看起来像是运算符重载滥用的情况,operator <
应该返回bool
但operator <<
可能应该返回其他内容。
如果你的意思是这样说:
ostream& operator<<(ostream&, const obj&);
然后,由于您不能向ostream
添加函数,因此该函数必须是自由函数,是否friend
取决于它必须访问的内容(如果它不需要访问私有或受保护的成员,则无需使其成为朋友)。
只是为了完成起见,我想补充一点,您确实可以在类中创建运算符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;
}
所以总结一下 - 你可以做到,但你很可能不应该:)
friend operator = 平等权利作为类
friend std::ostream& operator<<(std::ostream& os, const Object& object) {
os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
return os;
}
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
位于左侧。所以这不能是类的成员函数,它只能是友元函数。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 为什么COUT在朋友函数中不起作用,该功能超载了操作员&lt;&lt;这是一个iStream运算符
- 重载运算符<<:此运算符函数的参数太多
- std::pair的默认构造函数<>将基本类型(int等)设置为零