11

C ++標準テンプレートライブラリには、多くのクラスが()演算子をオーバーロードしている「機能」部分があります。

C ++で関数をオブジェクトとして使用するのに便利ですか?

代わりに関数ポインタを使用できないのはなぜですか?例はありますか?

4

4 に答える 4

10

もちろん、関数オブジェクトの代わりに常に関数ポインタを使用できますが、関数オブジェクトには関数ポインタよりも優れた利点があります。

  • よりよい性能:

最も明確で重要な利点の 1 つは、パフォーマンスが向上する可能性が高いことです。関数オブジェクトの場合、コンパイル時に詳細が利用できるため、コンパイラーは呼び出される関数を正確に決定し、インライン化できます。これは、ポインターのデリファレンスによってコンパイラーが実際の関数を決定することが困難になる関数ポインターの場合とは異なります。それが呼び出されます。

  • 関数オブジェクトはスマート関数です:

関数オブジェクトには、他のメンバー関数と属性がある場合があります。これは、関数オブジェクトに状態があることを意味します。実際、関数オブジェクトで表される同じ関数が、同時に異なる状態を持つ場合があります。これは、通常の機能では不可能です。関数オブジェクトのもう 1 つの利点は、それらを使用または呼び出す前に実行時に初期化できることです。

  • 汎用プログラミングの力:

通常の関数は、シグネチャが異なる場合にのみ異なる型を持つことができます。ただし、関数オブジェクトは、シグネチャが同じであっても異なる型を持つことができます。実際、関数オブジェクトによって定義される各機能動作には、独自の型があります。これは、関数の動作をテンプレート パラメーターとして渡すことができるため、テンプレートを使用したジェネリック プログラミングの大幅な改善です。

于 2012-03-09T02:42:42.540 に答える
4

代わりに関数ポインタを使用できないのはなぜですか? 例はありますか?

C スタイルの関数ポインターを使用すると、インライン化の利点を活用できません。関数ポインターは通常、ルックアップのために追加の間接参照を必要とします。
ただし、がオーバーロードされている場合、コンパイラがコードを追加して余分な呼び出しを保存するoperator ()のは非常に簡単なので、パフォーマンスが向上しますinline

オーバーロードのもう 1 つの利点は、関数オブジェクトoperator ()を暗黙的に引数と見なす関数を設計できることです。別の関数として渡す必要はありません。手作業でコーディングされたプログラムが少ないほど、バグが少なくなり、可読性が向上します。

Bjarne Stroustrup (C++ 発明者) の Web ページからのこの質問は、その側面をうまく説明しています。

C++ 標準 (テンプレート) ライブラリは、必要に応じてオーバーロードされた関数型プログラミングoperator ()を使用します。

于 2012-03-09T02:45:25.293 に答える
3

> C++ で関数をオブジェクトとして使用すると便利ですか?

はい: C++ テンプレート メカニズムでは、他のすべての C/C++ プログラミング スタイル (C スタイルと OOP スタイル、以下を参照) が可能です。

>代わりに関数ポインタを使用できないのはなぜですか? 例はありますか?

単純な C 関数ポインターは、適切に定義された operator() を持つオブジェクトでもあります。ライブラリを設計する場合、望ましくない場合にその C ポインター スタイルを使用することを誰かに強制したくありません。通常、すべて/全員に OOP スタイルを強制する/使用するように強制するのと同じくらい望ましくありません。下記参照。


C プログラマーや関数型プログラマーの観点からすると、OOP は遅くなるだけでなく冗長になる傾向があり、ほとんどの場合、抽象化の方向性が間違っています (「情報」は「オブジェクト」ではなく、「オブジェクト」であってはなりません)。そのため、「オブジェクト」という言葉が他の文脈で使用されるたびに、人々は混乱する傾向があります.

C++ では、目的のプロパティを持つものはすべてオブジェクトと見なすことができます。この場合、単純な C 関数ポインターもオブジェクトです。これは、OOP パラダイムが望ましくないときに使用されることを意味するものではありません。テンプレートメカニズムを使用するための適切な方法です。


パフォーマンスの違いを理解するには、プログラミング (言語) スタイル/パラダイムとそれらの可能な最適化を比較します。

C スタイル:

  • 最初のパラメーターとしてクロージャー (OOP では "this"、構造体へのポインター) を持つ関数ポインター。
  • 関数を呼び出すには、最初に関数のアドレスにアクセスする必要があります。
  • これは 1 つの間接化です。インライン化はできません。

C++ (および Java) OOP スタイル:

  • 仮想関数を持つクラスから派生したオブジェクトへの参照。
  • 参照は第 1 ポインターです。
  • 仮想テーブルへのポインターは 2 番目のポインターです。
  • 仮想テーブルの関数ポインタは 3 番目のポインタです。
  • それは 3 つの間接的です。インライン化はできません。

C++ テンプレート スタイル:

  • () 関数を使用したオブジェクトのコピー。
  • そのオブジェクトのタイプはコンパイル時に認識されるため、仮想テーブルはありません。
  • 関数のアドレスはコンパイル時に認識されます。
  • つまり、0 間接です。インライン展開可能。

C++ テンプレートは、上記の他の 2 つのスタイルを可能にするのに十分な汎用性を備えており、インライン化の場合には、それらよりも優れたパフォーマンスを発揮する可能性さえあります…</p>

コンパイルされた関数型言語: (「適切な末尾呼び出し」がないため、JVM と Javascript をターゲット プラットフォームとして除外)

  • 関数ポインタと、マシン レジスタ内のそのクロージャへの参照。
  • 通常は関数の「呼び出し」ではなく、GOTO のようなジャンプです。
  • 関数はスタックを必要とせず、ジャンプバックするアドレスも、スタック上のパラメーターもローカル変数も必要としません。
  • 関数には、パラメーターを含むガベージ コレクション可能なクロージャーと、次に呼び出される関数へのポインターがあります。
  • CPU がジャンプを予測するには、関数のアドレスをできるだけ早くレジスタにロードする必要があります。
  • これは、ジャンプ予測の可能性がある 1 つの間接化です。すべてがインラインとほぼ同じ速さです。
于 2012-03-09T17:22:11.567 に答える
2

主な違いは、関数オブジェクトは状態を保持できるため、単純な関数ポインターよりも強力であるということです。ほとんどのアルゴリズムは、プレーン関数ポインターではなくテンプレート関数を使用します。これにより、ファンクターまたはC ++ 11の新しいラムダに格納された値で追加の引数を埋めることにより、異なるシグネチャを持つ関数を呼び出すバインダーとして強力な構造を使用できます。アルゴリズムがファンクターを使用するように設計されたら、ライブラリーに事前定義されたジェネリック関数オブジェクトのセットを提供することは理にかなっています。

それとは別に、ほとんどの場合、これらのファンクターは、コンパイラーが完全な定義を持ち、関数呼び出しのインライン化を実行してパフォーマンスを向上させることができる単純なクラスであるという潜在的な利点があります。これが、Cライブラリstd::sortよりもはるかに高速になる理由です。qsort

于 2012-03-09T02:51:45.607 に答える