6

C ++を教えた私の経験では、演算子のオーバーロードは、学生に最も悲しみを与えるトピックの1つです。ここにstackoverflowで質問を見ても、たとえば、+演算子を外部またはメンバーにしますか?対称性などの扱い方は大変そうです。

C ++からJavaに移行したとき、その機能が失われるのではないかと心配していましたが、[]や()などの演算子を除いて、演算子をオーバーロードする必要性を実際に感じたことはありません。実際、それらのないプログラムの方が読みやすいと思います。

注:私はこれをコミュニティウィキとして配置しました。これについて話し合いましょう。意見を聞きたい。

4

14 に答える 14

40

オーバーロードされた演算子はスパイスのようなものです。少しでも何かを良くすることができます。多すぎると口に合わない可能性があります。

于 2009-04-01T20:18:55.037 に答える
31

承認しなくても、すべてのC++プログラマーが知っておくべきオーバーロードの例:

  • C ++オブジェクトを値のように動作させるには、operator =()が必要です。
  • スマートポインタを実装するには、operator->()が必要です
  • タイプをiostreamフレームワークに統合するには、operator <<()およびoperator >>()が必要です。
  • operator <()は、標準ライブラリコンテナに格納されているオブジェクトを比較するときにデフォルトで使用されます
  • operator()()は、標準ライブラリアルゴリズムで使用されるファンクターを実装するために使用されます
  • 独自のイテレータを実装すると、operator ++()が使用可能になると予想されます
于 2009-04-01T20:00:13.240 に答える
14

オーバーロードされた演算子について不平を言うのは簡単ですが、それらが驚くべき方法で動作しない限り、私は実際に問題を見ていません。はい、そこには悪い例があります(stlでも)。たとえば、auto_ptr の代入演算子を参照してください。&&、、 などの一部の演算子をオーバーロードする||と、,ほとんどの場合、問題が発生します。しかし、ほとんどの場合、オペレーターに彼らが宣伝していることを実行させれば、実際の問題はありません.

operator+何か奇妙なことをする ためにオーバーロードするのは悪い習慣ですが、オブジェクトをディスクにシリアル化するクラスに "Add" というメソッドを配置するのも同様に悪いことです。

于 2009-04-01T20:27:51.240 に答える
6

オーバーロードされた演算子は、特定のことを行うための優れた方法である可能性がありますが、ひどく悪用されやすいです。

<<and演算子をオーバーロードすると>>、新しい種類のストリーム、I/O 用の新しいオブジェクト、およびその両方で、C++ のストリームを簡単に拡張できます。オーバーロード->により、スマート ポインターはほとんど C++ ポインターの完全な代替品になります。オーバーロードされた演算子を使用すると、文字列連結演算子を使用したり、構文的に s に似た新しい種類の数値を作成したりできintます。それらを使用すると、他の言語では言語レベルの変更が必要になるようなことをライブラリで実行できるようになります。

彼らには限界があります。累乗に適した演算子はありません。乗算演算子は 1 つしかなく、複数の乗算方法がある場合もあります (たとえば、3D ベクトルの場合、少なくとも内積と外積があります)。、&&||およびコンマ演算子は、短絡評価とシーケンス ポイントを持つことができないため、組み込み機能を複製できません。

そしてもちろん、それらは悪用される可能性があります。たとえば、算術演算子が算術のように機能する必要があるなど、言語の要件はありません。誰かが直感的だと思った SQL 表記法を考え出すために、恐ろしいことが行われているのを見てきました。よく書かれていない C++ プログラムでは、 が何をするのかを知ることはa = x * y;不可能a.operator=(x.operator*(y));ですa.operator=(operator*(x, y));

C++ の設計における Bjarne Stroustrup の意図は、乱用の可能性に関係なく有用な機能を含めることでしたが、Java の設計における James Gosling の意図は、多少有用であっても過度に悪用される機能を除外することでした。これらの哲学のどちらが正しいか正しくないかは私には明らかではありませんが、それらは異なります。

Java は、演算子のオーバーロード、多重継承、実行時の型推定など、通常 C++ の機能を必要とする状況を回避するように設計されているため、見落とされることはあまりありません。これが良いか悪いか、どちらでもないかは、私にはわかりません。

学生に教える限り、演算子自体をオーバーロードしないように (ファンクターや代入演算子などの定義された条件下を除いて)、ライブラリがオーバーロードされた演算子をどのように使用するかを指摘してください。私は C++ の学生がそれらを正しく行うとは信じていません。あなたがクラスでそれを禁止したので、彼らはそれが難しいことを知っているでしょう. ステートメントよりも複雑なもので私が絶対に信頼できないものの中には、for演算子をオーバーロードする方法を見つけて、とにかく実行するものもありますが、それが人生です。

于 2009-04-01T20:55:16.010 に答える
5

演算子のオーバーロードは、多くの目的で非常に重要です。operator() をオーバーロードする機能がなければ、ファンクターを作成することは不可能です。ジェネリック プログラミングは、多くの場合、厄介な問題になります。数値アルゴリズムを作成する場合、float、double、std::complex、または自家製の型のいずれであっても、値の型が同じように動作することに依存します。定義されている通常の算術演算子などに依存しているため、組み込み型用とカスタム型用に別々のオーバーロードを記述する必要はありません。

スマート ポインターは、ポインターのように動作できるように、オブジェクトが逆参照演算子をオーバーロードできることに依存しています。

演算子のオーバーロードは、C++ プログラミングを耐えられるものにするために非常に重要です。複雑なことについては、私にはわかりません。独自の関数を作成するのと同じくらい複雑ではありません。一般に、関数はかなり簡単です。「multiply」と名付ければ関数、「operator*」と名付ければ演算子です。しかし、本体のコードはまったく同じです。

もちろん、オペレーターが虐待されることもあります。そして、 << または >> は許容範囲内かもしれませんが、それらは非常に一般的に知られ、使用されているため、公平だと思います。

ただし、C# などでの演算子のオーバーロードについて尋ねられた場合、私は喜んでそれらを省略します。それらの実装ははるかにぎこちなく、ジェネリックでは機能せず、C++ が使用するすべての優れた便利なトリックを有効にするわけではありません。

于 2009-04-01T20:44:30.920 に答える
4

ユースケースにもよると思います。演算子が必要なクラスにはいくつかの種類があります。たとえば、スマートポインタは、->および*演算子がないと意味がありません。

また、比較、等式、代入演算子は、特定のタイプに非常に役立つことがわかりました。私はエディター環境で作業しているので、当然、座標とスパンを表すために数回あります。確かに、比較演算子ですべてを行うことができますが

if ( point1 > point2 ) ...

見た目は

if ( point1.Compare(point2) < 0 ) ...

キャストが便利な場合もありますが、他の演算子の使用は少ないと思います。

于 2009-04-01T20:00:17.203 に答える
3

演算子のオーバーロードが悪い考えだとは思いません。暗黙の変換をデフォルトの動作にするのは悪い考えだと思います。そして、演算子のオーバーロードと組み合わせたデフォルトの暗黙的な変換は、本当に悪い考えです。

暗黙の変換を完全に取り除くか、「暗黙の」キーワードに依存させると、言語には、このような数え切れないほどの記事で説明されている潜在的な落とし穴や落とし穴が数多くありませんでした。

于 2009-04-01T21:18:20.963 に答える
2

Neil が彼の回答で指摘しているように、演算子のオーバーロードは、優れたオブジェクト指向の C++ イディオムを学ぶために必要なトピックです。慣用的な規則に従ってオーバーロードされた演算子を実装しないと、非常にバグが多く予期しない動作が発生する可能性があることを学生に注意して教えます。演算子のオーバーロードは、創造的になるのに適した時期ではありません。

于 2009-04-01T20:10:26.460 に答える
1

C++の非組み込み型の算術演算子をオーバーロードする機能が本当に気に入っています。ただし、算術演算のような動作をする型の場合のみ。たとえば、固定小数点クラス、3Dベクトルクラス、複素数クラス、任意長の「bignum」クラス。私はJavaで同様のコードを記述しましたが、a.Add(b)の代わりにのようなものを記述しなければならないことに悩まされていましたa+b。念のために言っておきますが、私は訓練を受けた数学者です。演算子のオーバーロードを使用すると、実際に実装しなくても、C++でドメイン固有言語の優れた機能を少し得ることができます。

しかし、たとえば、 (危険であるが十分に確立されたiostream規則に従う)またはSTLのようなパターンoperator+によって実行される方がよい機能で過負荷になっているのを見ると、本当にイライラします。operator<<.push_back()

operator()…ポスト発見boost::bind&ファンクターのboost::functionいない人生は想像できません。operator*そして、スマートポインタは、オーバーロードoperator->などがなければ、ほとんど便利ではありません。

于 2009-04-01T20:35:04.733 に答える
1

使用した演算子とケース:
operator->, operator* - プロキシ オブジェクトとさまざまなラッパー用。
operator= - コピー時の予期しない動作を避けるために必要です。
演算子 < (>, <=, >=) - マップまたはセットに格納します (ただし、通常はファンクターをこれに渡す方が適切です)。
operator << ( >> ) - ストリームと boost::lexical_cast の互換性のため。
operator ==, != - 比較オブジェクトを許可します。
オペレーター!- 場合によっては代わりに valid() 関数。
operator Type - 他の型への変換用。
operator() - ブーストが許可されていない場合のスマートファンクター用。

それで全部です。
以前は他の演算子を使用したこともありましたが、それは私の数学ユーティリティ用でした。

また、論理演算子 (&&、||) にも注意する必要があります。標準のセマンティックとは異なります。

ptr && ptr->method()

operator&& をオーバーロードした場合、別の意味を持つ可能性があります。

于 2009-04-01T20:21:28.900 に答える
0

オーバーロードを避けた方がよい演算子の1つは、変換演算子です。それは、STLでさえそれをオーバーロードしないことを好み、代わりに関数スタイル変換を好むような予期しない結果につながります:

std::string str = "foo";
char *ch = str.c_str(); //rather than char *ch = str.operator *char();
于 2009-04-01T23:15:02.320 に答える
0

演算子のオーバーロードの問題は、演算子の元の (C) 目的との関連で実際には意味をなさない機能でそれらをオーバーロードすることを好む人がいるということです (ここで、std の >> および << 演算子を指します)。 ::iostreamのもの。) 私の考えでは、演算子をオーバーロードする必要があるのは、オーバーロードが基礎となる演算子の意味と正確に一致する場合 (つまり、< と > は比較でなければならない)、または別の方法と対話するために特定の方法でオーバーロードする必要がある場合のみです。図書館。

率直に言って、ライブラリで必要でない限り、演算子をオーバーロードしません。何が起こっているのかを理解するのに読者が苦労するだけです。

于 2009-04-01T20:21:57.633 に答える
-1

機能があるのは良いことですが、必須ではありません。多くの場合、私は彼らが持っている利点よりも混乱していると思います。JaredParsの例のように、.compare関数は「>」よりも明白です。point1はオブジェクトであり、プリミティブデータ型ではないことが直接わかります。私は使用するライブラリのオーバーロードされた演算子が好きですが、私自身のコードではそれらをほとんど使用しません。

編集:関数名の選択に一貫性がありません。.compareを.greaterThanに置き換えると、より明確になります。私が言いたいのは、私にとってオブジェクトにバインドされた関数名は、一見オブジェクトとの関連付けがない演算子よりも明白であるということです。よく選択された関数名は読みやすいです。

于 2009-04-01T20:33:15.843 に答える