14

重複の可能性:
演算子のオーバーロード

私は待望の C++ への復帰を行っていますが、他の言語ではあまり目立たないように思われる基本的な表記法がいくつかあります。

このコード行を見ると

cout << "firstvalue is " << firstvalue << endl;

私はこれがをするかを理解しています。「firstvalue is x」をコンソールに書き込みます。x は firstvalue の値です。ただし、「<<」または「>>」二重山括弧については何も知りません。正式な名前がわからないので、彼らや彼らが何をしているのかを調査することはできませんでした.

私の質問は、上記のステートメントで実際に(段階的に)何が起こるかということです。そして、これらの「<<」は何のためですか?cout がコンソールに書き込むための標準ライブラリ関数であることは理解できたと思います。しかし、私はobjective-cまたはドット表記に慣れています。この「cout」関数がメンバーであるオブジェクトがわかりません。

少なくとも引数に中かっこを提供するので、printf をもう少し簡単に理解できます。例 printf("your string here")。

4

5 に答える 5

22

C++ では、演算子のオーバーロードが可能です。つまり、ユーザー定義型は、組み込み演算子で独自の動作を定義できます。この場合、演算子はleft shiftorright shift演算子と呼ばれます。これらの演算子は従来、ビット シフトに使用されてきましたが、標準ライブラリではストリーミング操作を表すために再利用されています。

C および C++ で使用可能な演算子のリストは、こちら で確認できます。

あなたのケースでは、文字列リテラルと何らかの型の値を、型std::basic_ostreamのオブジェクトであるstd::coutにストリーミングしています。

ステップバイステップ

優先ルールが適用されると、コードは次のようになります。

((cout << "foobar") << x) << endl;

コンパイラは基本的にobject << object式を関数呼び出しに変換します。

operator<<(operator<<(operator<<(cout, "foobar"), x), endl);

次に、どのオーバーロードを呼び出すかを判断します。(これは非常にトリッキーです。今のところoperator<<、一致する引数を持つ のオーバーロードを単に検索すると考えるだけで十分です)。

basic_ostream の組み込みオーバーロードのほとんどはherehereです。

于 2012-10-17T16:29:50.310 に答える
5

演算子は、C++の<<「算術左シフト」です。例えば:

3 << 2

は 12 と評価されます。その理由は、3 のバイナリ表現が

00000011

左に2回シフトすると、

00001100

結果の数値は 12 です。

出力でこれを行う必要があるのは何ですか? 実際には何もありません。ただし、C++ では、オーバーロードのおかげで演算子の意味を再定義できます。C++ 標準ライブラリは、左シフト演算子の意味を一種の「ストリームへの送信」として再定義することを決定しました。

それで何が起こるかというと

std::cout << "whatever"

value として返しますstd::coutが、副作用として文字列「whatever」を出力します。

この演算子が選択されたのは、妥当な優先順位 (オーバーロードによって優先順位が変更されず、新しい演算子を定義できない) があり、形が「自然」に見えるためです。ただし、左シフト演算子は単なる通常の演算子であり、たとえば、評価の順序について保証がないことに注意してください。

std::cout << f() << g() << h();

ここで、出力は呼び出しの結果であり、呼び出しf()の結果が続き、呼び出しの結果がg()続きh()ます...しかし、関数自体は異なる順序で呼び出される可能性があり、たとえば、h()最初に呼び出される可能性があります!

したがって、ある意味では、演算子の「シーケンスの外観」は誤解を招く可能性があります。これは、出力のシーケンスに関するものであり、評価のシーケンスに関するものではないためです。

于 2012-10-17T16:37:48.100 に答える
2

これらはストリーム挿入(またはの場合は抽出)と呼ばれ、istream >>実際には左シフト演算子と右シフト演算子のセマンティックオーバーロードです。

したがって、この:

int x = 1 << 1;

少しシフトですが、これは:

std::cout << x;

ストリーム挿入です。次のように明示的に書き出すことができます。

operator <<(std::cout, x);

まったく同じ結果が得られます。ストリーム挿入演算子の従来の形式(ユーザー定義型ではオーバーロードされる可能性があるため、独自の演算子を作成することは珍しくありません)は次のとおりです。

std::ostream& operator <<(std::ostream&, T value);

出力ストリームは(参照により)返されるため、呼び出しを連鎖させることができます。例は次のように変換されます。

operator<< (
  operator<< (
    operator<<(std::cout, "firstvalue"),
    firstvalue
  ),
  std::endl
);

ああ、そして... std::coutstd::cerrなど)は関数ではありません:それらはグローバルオブジェクトです。ここでの関数は、オーバーロードされた<<演算子です。FILE *stdout, *stderrそれらを同等のものと考えてください。

printfetよりもC++iostreamsにはいくつかの利点があります。al:

  • 型安全性:オーバーロード解決によりコンパイル時"%f"に関数が自動的に選択されるため、誤って整数を出力してガベージを取得することはできません。std::ostream& operator<<(std::ostream&, double)
  • ユーザー定義型のサポート:ウィジーな新しいクラスのストリーム挿入演算子を記述でき、どこでも機能します
  • ストリームの抽象化:同じオーバーロードを使用して(1回だけ書き込む)、stdoutとstderr(cout/ cerr)、およびファイル(std::ofstream)と文字列(std::ostringstream)にフォーマットできます。printf//を個別にfprintf処理する必要はありません。snprintf

いくつかの欠点もあります。

  • パフォーマンス:すべての抽象化と、実行時に構成されるロケールシステムの一般性にはいくらかのペナルティがあります
  • 冗長性:少なくとも、ですでにサポートされているプリミティブ型printfの場合、フォーマット文字列ははるかに簡潔で表現力に富んでいます。
于 2012-10-17T16:35:05.183 に答える
2

<<演算子であり、同じ方法+は演算子で*あり、演算子です。このように、以下は同等の式です。

5 + 3 + 2
((5 + 3) + 2)

次の 2 つも同様です。

std::cout << "Hello" << std::endl
((std::cout << "Hello") << std::endl)

これは、2 つのオペランドを持つ単なる演算子です。基本型の<<and>>演算子は、実際には左シフト演算子と右シフト演算子として知られています。これらはビットごとのシフトを実行します。たとえば5 << 1、5 ( ) のすべてのビットを01011 つ左にシフトして、10 ( 1010) を取得します。

ただし、他のほとんどの演算子と同様に、シフト演算子をオーバーロードできます。入出力ライブラリの場合、ストリームへの入出力に自然な構文を提供するためにシフト演算子がオーバーロードされます。これは、<<>>トークンの方向性が、何かがどちらか一方に流れているように見えるためです。これらの I/O クラスを使用すると、これらの演算子のオーバーロードは、演算子を実行しているストリームへの参照を返すため、それらを連鎖させることができます。

operator<<メンバー関数を提供するかoperator>>、1 つの引数 (演算子の右側のオペランド) を取ることによって、特定のクラスのシフト演算子をオーバーロードできます。または、それぞれ演算子の 2 つのオペランドである 2 つの引数を取る同じ名前の非メンバー関数を指定できます。

于 2012-10-17T16:39:51.230 に答える
1

これは、次のシンタックス シュガーです。

// Let the function 'print' be a renaming of 'operator<<'
// with T being the type of the object you want to print.
std::ostream& print(std::ostream&, const T&);

// 1) Print "first value is" and then return the stream you
// to which to just printed (ie. cout). 2) Use the returned
// stream to chain function calls and print 'firstValue'.
print(print(std::cout, "first value is"), firstValue);
于 2012-10-17T16:33:47.967 に答える