たくさんのブログを読んで、C++で仮想関数を使用する方法を理解しています。しかし、それでも仮想関数を使用する理由がわかりません。仮想関数の実際の意味をより簡単に視覚化できるように、実際の例を教えてください。
7 に答える
言及する重要なことは、継承(キーワードvirtualが基本である)はコードの再利用のみを目的とするべきではないということです。これには委任を使用してください。
委任は、connection()というメソッドを持つBroadbandConnectionというクラスがある場合に行われます。次に、マネージャーが暗号化を追加したいと言ったので、クラスBroadbandConnectionWithEncryptionを作成します。あなたの自然な本能は、継承を使用してから、新しいクラスBroadbandConnectionWithEncryptionをBroadbandConnectionから派生させることかもしれません。
これに対する欠点は、初期クラスの作成者が継承用に設計していなかったため、派生クラスでの動作をオーバーライドできるように、メソッドconnection()を仮想化するように定義を変更する必要があることです。これは必ずしも理想的ではありません。コードを再利用するために、ここで委任を使用することをお勧めします。
class BroadBandConnection
{
public:
void Connection (string password)
{
//connection code.
}
};
class BroadBandConnectionWithEndcryption
{
public:
void Connection (string password)
{
mbroadbandconnection.Connection(password);
//now do some stuff to zero the memory or
//do some encryption stuff
}
private:
BroadBandConnection mbroadbandconnection;
};
キーワードvirtualは、ポリモーフィズムの目的で使用されます。名前が示すように、それはオブジェクトが複数のフォームを持つ能力です。この種の決定は、インターフェースまたはクラスの設計時に行われます。
class IShape
{
virtual void Draw () = 0;
};
class Square
{
void Draw()
{
//draw square on screen
}
};
class Circle
{
void Draw()
{
//draw circle on screen
}
};
Draw()を= 0で純粋な仮想にしました。これを省略して、デフォルトの実装を追加することもできます。純粋な仮想は、合理的なデフォルトの実装がないインターフェースにとって意味があります。
これにより、Shapeオブジェクトをさまざまなメソッドに渡すことができ、それらは私が与えたものに関係する必要はありません。彼らが知っているのは、私が形がそれ自体を描く能力をサポートする何かを提供しなければならないということだけです。
IShape* circle = new Circle ();
IShape* square = new Square ();
void SomeMethod (IShape* someShape)
{
someShape->Draw(); //This will call the correct functionality of draw
}
将来、人々が新しい形状について考え始めると、それらはIShapeから派生し、Drawのいくつかの機能を実装する限り可能になります。このオブジェクトをSomeMethodに渡すことができます。
まず、これ。
さて、実際の例です。3つのタブを備えたGUIを備えたプログラムがあります。各タブは、共通ベースであるTabBaseから派生したクラスのオブジェクトです。仮想関数OnActivate()があります。タブがアクティブ化されると、ディスパッチャは現在のタブでタブを呼び出します。いくつかの一般的なアクションがあり、このタブに固有のアクションがあります。これは、仮想関数を介して実装されます。
利点は、コントローラーがタブの種類を知る必要がないことです。TabBaseポインターの配列を格納し、それらに対してOnActivate()を呼び出すだけです。仮想関数の魔法は、正しいオーバーライドが呼び出されることを確認します。
class TabBase
{
virtual void OnActivate()
{
//Do something...
}
};
class SearchTab: public TabBase
{
void OnActivate() //An override
{
TabBase::OnActivate(); //Still need the basic setup
//And then set up the things that are specific to the search tab
}
}
彼らは実際にウィキで例を挙げています
http://en.wikipedia.org/wiki/Virtual_function
動物を使用します。動物はスーパークラスであり、すべての動物が食べます(スーパークラスの仮想関数)。各動物は、他のすべての動物とは異なる方法で食べる可能性があります(仮想機能をオーバーライドします)。私は任意の動物のリストを持っています、そして私が食べる機能を呼ぶとき、それらは彼ら自身の異なる食習慣を表示します。
メソッドを持つ基本クラス(動物)が1つあり、その子によって異なる方法で実装できます(たとえば)。このメソッドを仮想として宣言すると、そのメソッドに対処でき、子の定義から実装されます。子のオーバーロードされたメソッドに対処する場合は仮想を使用する必要はありませんが、親のメソッドに対処する場合は使用する必要があります。
たとえば、それぞれが異なる動物のベクトルがある場合です。メソッド(たとえば)を仮想として宣言し、それを動物クラスから呼び出すと、対応する子から呼び出されます。
私が間違っているなら私を訂正してください、それは私がそれを理解した方法です。
Javaに精通している場合、それは簡単なはずです。Javaでは、すべてのクラスメソッドは事実上仮想です。派生クラスでオーバーライドし、基本クラス参照を介して呼び出す場合、オーバーライドは基本ではなく呼び出されます。
これはC++のデフォルトの動作ではありません。関数をそのように動作させたい場合は、基本クラスで仮想として宣言する必要があります。簡単です。
Javaは仮想関数でいっぱいです。それらの明示的なキーワードがないだけです。
仮想関数の目的は、動的ディスパッチを実現することです。
あなたはJavaに精通していると言っているので、仮想関数を実際に使用する場合は、Javaで、パブリック/保護されたメソッドを使用しinterface
たり、使用したりする場所を考えてみてください。@Override
仮想関数を使用するかどうかの決定は簡単です。基本メソッドをいつオーバーライドするかを知る必要があります。例として次のコードを取り上げます。
class animal
{
public:
void sound()
{
cout << "nothing";
}
};
class bird : public animal
{
public:
void sound()
{
cout << "tweet";
}
};
この場合、bird()をオーバーライドしたいと思います。しかし、私がしなかった場合はどうなりますか?これは何が起こるかです:
animal * a = new bird;
a->sound();
**Output**
nothing
すべての意図と目的のために、C ++は動物しか見ることができないため、画面には何も表示されません。ただし、仮想として宣言した場合は、クラス階層内で最も低いメソッドを検索することがわかります。もう一度やってみる:
class animal{
public:
virtual void sound(){cout<<"nothing";}
};
class bird : public animal
{
public:
void sound()
{
cout << "tweet";
}
};
animal * a = new bird;
a->sound();
**Output**
tweet.
お役に立てれば。