2

初心者の質問である必要があります...

既存のクラスAに既存のコードがあり、既存のメソッドA :: f()をオーバーライドするために拡張したいと思います。

それで、他のコードがそれに依存しているので、A :: f()を変更したくないので、f()をオーバーライドするクラスBを作成したいと思います。

これを行うには、A :: f()を仮想メソッドに変更する必要があると思います。

私の質問は、メソッドを動的に呼び出すことを許可する以外に(AではなくBの実装を使用するために)、メソッドを仮想化することに他の影響はありますか?私はある種の良いプログラミング慣行を破っていますか?これは、A :: f()を使おうとしている他のコードに影響しますか?

私にお知らせください。

ありがとう、jbu

編集:私の質問は、他の誰かのメソッドを仮想化することに何か問題がありますか?他の人の実装を変更していなくても、他の人の既存のコードにアクセスして宣言を変更する必要があります。

4

8 に答える 8

6

基本クラス内で関数を仮想化すると、その関数から派生したものもすべて仮想化されます。

仮想化された後、のインスタンスを作成するとA、それでも。を呼び出しますA::f

Bのインスタンスを作成し、それをタイプのポインターに格納する場合A*。そして、を呼び出すと、 'sA*::->fが呼び出されます。BB::f

副作用に関しては、わずかな(目立たない)パフォーマンスの低下を除いて、おそらく副作用はありません。

非常に小さな副作用もあります。Aから派生するクラスCが存在する可能性があり、を実装する可能性があり、呼び出されC::fた場合A*::->fは呼び出されることを期待A::fします。しかし、これはあまり一般的ではありません。

しかし、おそらく、C存在する場合、それはまったく実装されませんC::f。その場合、すべてが正常です。


ただし、すでにコンパイル済みのライブラリを使用していて、そのヘッダーファイルを変更している場合は、期待どおりに機能しない可能性があることに注意してください。ヘッダーファイルとソースファイルを再コンパイルする必要があります。

副作用を回避するために、次のことを検討できます。

  1. A2から派生するタイプを作成し、Aそれをf仮想 にします
    • A2代わりにタイプのポインタを使用するA
    • Bタイプから派生しA2ます。
    • このようにして、Aを使用したものはすべて同じように機能します

has-a必要なものによっては、の代わりにリレーションシップを使用できる場合もありますis-a

于 2009-11-30T18:37:01.053 に答える
4

仮想関数が呼び出されるたびに、vtableルックアップのパフォーマンスがわずかに低下します。仮想でない場合、コードの場所はコンパイル時にわかっているため、関数呼び出しは直接行われます。実行時に、仮想関数アドレスは、呼び出しているオブジェクトのvtableから参照する必要があります。

于 2009-11-30T18:40:10.653 に答える
4

これを行うには、 A::f() を仮想メソッドに変更する必要があると思います。

いいえ、オーバーライドするために仮想メソッドに変更する必要はありません。ただし、ポリモーフィズムを使用している場合、つまり、A から派生しているが A へのポインターとして格納されているさまざまなクラスが多数ある場合は、そうする必要があります。

vtableが原因で、仮想関数のメモリオーバーヘッドもあります(spoulsonが言及したことは別として)

于 2009-11-30T18:44:05.310 に答える
2

あなたの目標を達成する他の方法があります。であることは理にかなっていBますAか? たとえば、猫が動物であることは理にかなっていますが、猫が犬であることは意味がありません。おそらく、A と B の両方が関連している場合、基本クラスから派生する必要があります。

除外できる一般的な機能はありますか? これらのクラスをポリモーフィックに使用することは決してなく、単に機能が必要なように思えます。その共通機能を取り除いてから、2 つの別個のクラスを作成することをお勧めします。

コストに関しては、AとBを直接使用している場合、コンパイルは仮想ディスパッチをバイパスし、仮想ではないかのように関数呼び出しに直接進みます。B`A1 を期待する場所に (参照またはポインタとして) aを渡すと、ディスパッチする必要があります。

于 2009-11-30T18:44:03.567 に答える
2

仮想メソッドについて話すと、2 つのパフォーマンス ヒットがあります。

  • vtableのディスパッチ、本当に心配する必要はありません
  • 仮想関数はインライン化されません。これは前のものよりもはるかに悪い可能性があります。関数のインライン化は、状況によっては物事を本当に高速化できるものであり、仮想関数では決して起こりません。
于 2009-11-30T18:50:38.870 に答える
2

他人のコードを変更することがどれだけ正しいかは、その地域の慣習や慣習に完全に依存します。それは私たちがあなたにお答えできるものではありません。

次の問題は、クラスが継承されるように設計されているかどうかです。多くの場合、クラスはそうではなく、他の側面を変更せずにそれらを有用な基本クラスに変更するのは難しい場合があります。非基本クラスは、パブリック関数を除いてすべてがプライベートである可能性が高いため、B の内部にさらにアクセスする必要がある場合は、A にさらに変更を加える必要があります。

クラス A の代わりにクラス B を使用する場合は、仮想化せずに関数をオーバーライドできます。クラス B のオブジェクトを作成し、それらを A へのポインターとして参照する場合は、f() を仮想にする必要があります。また、デストラクタを仮想にする必要があります。

于 2009-11-30T18:52:07.820 に答える
1

適切な場所で仮想メソッドを使用することは、優れたプログラミング手法です。仮想メソッドは、C++ クラスがどれほど実用的であるかに関して多くの意味を持ちます。

仮想関数がないと、C++ でインターフェイスを作成できません。インターフェイスは、すべて未定義の仮想関数を持つクラスです。

ただし、仮想メソッドの使用が適切でない場合もあります。仮想メソッドを使用してオブジェクトの機能を変更することは、サブクラス化を意味するため、常に意味があるとは限りません。多くの場合、関数オブジェクトまたは関数ポインターを使用して機能を変更できます。

前述のように、仮想関数は、実行中のプログラムが使用する関数を確認するために参照するテーブルを作成します。

C++には多くの落とし穴があるため、何をしたいのか、そしてそれを行う最善の方法は何かを十分に認識しておく必要があります。Java や C# などのランタイム動的オブジェクト指向プログラミング言語と比較すると、何かを実行する方法はそれほど多くありません。いくつかの方法は完全に間違っているか、コードが進化するにつれて最終的に未定義の動作につながります。

とても良い質問をしてくれたので、Scott Myer の著書『Effective C++』と Bjarne Stroustrup の著書『The C++ Programming Language』を購入することをお勧めします。これらは、C++ での OO の微妙な点、特にいつどの機能を使用するかを教えてくれます。

于 2009-11-30T18:49:28.450 に答える
0

それがクラスが持つ最初の仮想メソッドである場合、POD ではなくなります。これは物事を壊す可能性がありますが、その可能性はわずかです。

POD: http://en.wikipedia.org/wiki/Plain_old_data_structures

于 2009-11-30T19:31:41.423 に答える