それは基本的に質問です、operator<<
を実装する「正しい」方法はありますか? this を読むと、次のようなことがわかります。
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)
ここで、段落は文字列です。
ここでの問題は、記事 link の解釈にあります。
この記事は、bool関係演算子を正しく定義するのに問題がある人に関するものです。
オペレーター:
これらの演算子は、同じ型の2つのオブジェクトを比較しているため、ブール値を返す必要があります。通常、これらの演算子をクラスの一部として定義するのが最も簡単です。これは、クラスは自動的にそれ自体のフレンドであるため、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;
}
暗黙のthis
パラメーターは<<
- operatorの左側にあるため、メンバー関数としてはできません。 (したがって、ostream
- classのメンバー関数として追加する必要があります。良くありません:)
friend
ingせずに、無料の機能として実行できますか?これは、これがostream
との統合であり、クラスのコア機能ではないことが明らかになるためです。
Herb SutterとScott Meyersが説明しているように、カプセル化を促進するために、メンバー関数よりも非フレンド非メンバー関数を優先します。
C++ストリームのように、場合によっては選択肢がなく、非メンバー関数を使用する必要があります。
しかし、それでも、これらの関数をクラスの友達にする必要があるわけではありません。これらの関数は、クラスアクセサーを介してクラスにアクセスできます。この方法でこれらの関数を作成することに成功すると、勝ちました。
あなたの質問であなたが与えた例は間違っていると思います。例えば;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
この方法がストリームでどのように機能するかを考えることさえできません。
<<および>>演算子を実装する2つの方法を次に示します。
タイプTのストリームのようなオブジェクトを使用するとします。
そして、タイプParagraphのオブジェクトの関連データを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 ;
}
2番目はメソッドとして:
// 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オブジェクトの場合、これは不可能です(変更することはできません...)。
C++ストリームの同じ<<および>>演算子のプロトタイプを次に示します。
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ベースのストリームでのみ機能します。
// 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は、charベースのコードはその上の汎用コードの「特化」にすぎないという事実についてコメントしました。もちろん、Rhysは正しいです。charベースの例を使用することはお勧めしません。読みやすいので、ここでのみ説明します。 charベースのストリームでのみ作業する場合にのみ実行可能であるため、wchar_tコードが一般的なプラットフォーム(つまり、Windows)では避ける必要があります。
これが役立つことを願っています。
特に最近のほとんどの場合と同様に、出力が主に診断とログ記録に使用される場合は、無料の非フレンド関数として実装する必要があります。出力に入れる必要のあるすべてのものにconstアクセサーを追加し、アウトプッターにそれらを呼び出してフォーマットを実行させます。
実際に、これらのすべてのostream出力のない関数を「ostreamhelpers」ヘッダーと実装ファイルに収集しました。これにより、その2次機能がクラスの本来の目的から遠く離れた状態に保たれます。
完成のために、実際にcan演算子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<<
フレンド関数として実装:
#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 Hello 100 Hello任意のキーを押して続行します…
これは、オブジェクトがoperator<<
の右側にあり、引数cout
が左側にあるため、フレンド関数になります。したがって、これはクラスのメンバー関数にはできません。フレンド関数にしかできません。
フレンド演算子=クラスと同等の権利
friend std::ostream& operator<<(std::ostream& os, const Object& object) {
os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
return os;
}