なぜ実際に仮想機能に?
-
19-09-2019 - |
質問
これまでC++.
きもしくは今後公式サイトにて差方式をオーバー仮想関数の概念可能です。
の機能を仮想機能の大きさを抱え、その由来。再定義関数を導出クラスではという機能をオーバー.
なぜ実際に仮想機能に?
解決
概要
本論文では、この仮想関数をC++.部ゼロを説明してい仮想関数が宣言され、オーバーライドになります。一つの試み(も失敗について説明仮想機能を実装しています。第二部ではサンプルプログラムを使用した例の授業に定義されたパーツをゼロとします。一つは、クラシック動物の例では毎に仮想機能-多型barcode.net(日本語版)がバージョン
部ゼロ
方法のクラスといわれる 仮想 場合にだけ、その宣言されています。
class my_base
{
public:
void non_virtual_test() { cout << 4 << endl; } // non-virtual
virtual void virtual_test() { cout << 5 << endl; } // virtual
};
(もちろん、私を想定したプログラマでないように #define virtual
.)
クラスredeclares再実装し、非仮想メソッドの一つの拠点と 過負荷 この方法です。クラスredeclares再実仮想メソッドの一つの拠点と オーバーライド この方法です。
class my_derived : public my_base
{
public:
void non_virtual_test() { cout << 6 << endl; } // overloaded
void virtual_test() { cout << 7 << endl; } // overriden
};
第一部
時のコンパイラで検出し、クラスが仮想の手法では,自動的に追加します 仮想メソッドテーブル (としても知られる vtable のクラスメモリーレイアウト。その結果、これから発生するの編集このコード:
class my_base
{
//<vtable>
// The vtable is actually a bunch of member function pointers
protected:
void (my_base::*virtual_test_ptr)();
//</vtable>
// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
void virtual_test_impl() { cout << 5 << endl; }
// Initializing the real_virtual_test pointer in the vtable.
public:
my_base() : virtual_test_ptr(&my_base::virtual_test_impl) {}
public:
void non_virtual_test() { cout << 4 << endl; }
// The interface of the virtual function is a wrapper
// around the member function pointer.
inline void virtual_test() { *virtual_test_ptr(); }
};
時のコンパイラで検出し、クラスのメソッドは仮想メソッドで置き換えそれに関連したエントリのvtable.その結果、これから発生するの編集このコード:
class my_derived : public my_base
{
// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
void virtual_test_impl() { cout << 7 << endl; }
// Initializing the real_virtual_test pointer in the vtable.
public:
my_derived() : virtual_test_ptr(&my_derived::virtual_test_impl) {}
public:
void non_virtual_test() { cout << 6 << endl; }
};
第二部
現在この仮想機能を活用して実施される。vtables、うどんの機能のポインタで明らかにこのコードは:
#include <iostream>
using namespace std;
class my_base
{
public:
void non_virtual_test() { cout << 4 << endl; }
virtual void virtual_test() { cout << 5 << endl; }
};
class my_derived : public my_base
{
public:
void non_virtual_test() { cout << 6 << endl; }
void virtual_test() { cout << 7 << endl; }
}
int main()
{
my_base* base_obj = new my_derived();
// This outputs 4, since my_base::non_virtual_test() gets called,
// not my_derived::non_virtual_test().
base_obj->non_virtual_test();
// This outputs 7, since the vtable pointer points to
// my_derived::virtual_test(), not to my_base::virtual_test().
base_obj->virtual_test();
// We shall not forget
// there was an object that was pointed by base_obj
// who happily lived in the heap
// until we killed it.
delete base_obj;
return 0;
}
第三部
からない仮想関数の例では、完全な例と動物...
#include <iostream>
using namespace std;
class animal
{
public:
virtual void say_something()
{ cout << "I don't know what to say." << endl
<< "Let's assume I can growl." << endl; }
/* A more sophisticated version would use pure virtual functions:
*
* virtual void say_something() = 0;
*/
};
class dog : public animal
{
public:
void say_something() { cout << "Barf, barf..." << endl; }
};
class cat : public animal
{
public:
void say_something() { cout << "Meow, meow..." << endl; }
};
int main()
{
animal *a1 = new dog();
animal *a2 = new cat();
a1->say_something();
a2->say_something();
}
他のヒント
仮想関数/メソッドは単に機能する可能性があります。オーバーライド内のサブクラス(またはC++で利派生クラス)による定義をどのように機能動作時と同じ署名).
思考の基底クラス哺乳動物と話す機能です。その機能は無効となcoutsどの哺乳動物に語りかけている。きのこのクラスを継承をオーバーライドすることができ語りの方法で料"Arf Arf!"や猫に行く"Meow Meow".
ご質問のようお願いと何が違うのは、なにもなるためには、正しい仮想関数がオーバーライドの動作はこれらの機能をまた後の差をオーバー機能-過負荷します。
オーバーロード機能を実現すること、関数と同名のストアドファンが異なる引数はすなわち別の番号-タイプの引数(s)です。ここでは説明の過負荷C++から IBMサイト:
過負荷(C++のみ)を指定した場合には複数の定義 機能名またはオペレーターの範囲には、過負荷 この関数名。過多機能通信事業者 に記載のオーバーロード機能(C++のみ)およびオーバーロード 事業者(C++のみ)。
過宣言で宣言された宣言 同じ名前として宣言された宣言と同じ範囲 双方の宣言しています。
お問い合わせいただいた場合の過機能の名称又はオペレーター、コンパイラ 判断し、最適な定義を使用との比較によ 引数の種類の呼び出しに使用する機能やオペレーターの パラメータ型を指定された規定します。選定のプロセス 最適な機能過負荷またはオペレーターと呼ばれ 過負荷分解能として過負荷分解能(C++のみ)。
としての合理的な理由の場合の仮想関数が必要で、このブログを与える事ができない生活を送っていると: http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions.html
の機能の違いは、オーバーライドとvirtual
機能の多型のに重要になります。具体的に基底クラスへの参照やポインタを使用している場合。
基本設定
C ++では、派生クラスは、ベースクラスのオブジェクトを要求する関数に渡すことができます。 (スライス<も参照してください。 />と LSP に)。与えられます:
struct Base_Virtual
{
virtual void some_virtual_function();
};
struct Base_Nonvirtual
{
void some_function();
};
void Function_A(Base_Virtual * p_virtual_base);
void Function_B(Base_Nonvirtual * p_non_virtual_base);
上記のコードでは、二つの基本クラスが存在する、一方が他方を非仮想関数を宣言し、仮想メソッドを宣言します。
2つの関数は、それぞれのベースclasesへのポインタを必要と宣言されています。
派生クラス
特にvirtual
対非仮想(メソッドをオーバーライド)、私たちは今、多型をテストしてみましょう。
構造:
struct Derived_From_Virtual
: public Base_Virtual
{
void some_virtual_function(); // overrides Base_Virtual::some_virtual_function()
};
のstruct Derived_From_Nonvirtual :公共Base_Nonvirtual { 空some_function(); }
C ++言語によれば、私はDerived_From_Virtual
はFunction_A
から継承するためDerived_From_Virtual
するBase_Virtual
へのポインタを渡すことができます。私はまたDerived_From_Nonvirtual
にFunction_B
へのポインタを渡すことができます。
virtual
とオーバーライドの違い
virtual
でBase_Virtual
修飾子は、代わりにFunction_A
方法のDerived_From_Virtual::some_virtual_function()
を使用しますBase_Virtual
コンパイラに指示します。この方法は、仮想であるので、これは、ある最終的な定義は、将来に存在し得るか、または のクラスを導出しました。実際の定義は、定義を含むほとんどの派生クラスでメソッドを使用することを述べています。
Derived_From_Nonvirtual
するFunction_B
へのポインタを渡す場合、、コンパイラは、Base_Nonvirtual::some_function()
を基本クラスのメソッドを使用する機能を指示します。派生クラスでsome_function()
メソッドは、基本クラスから別、無関係、方法である。
virtual
とオーバーライドの主な違いは、多形で発生します。
C ++よくある質問Liteは、 http://www.parashift.com/c++-faqをチェック-Lite / に。おそらく、初心者のための最高のC ++資源の一つです。それは仮想関数とオーバーライドについての詳細な書き込みアップを持っています。
私はC ++を学ぶとして私は個人的にはC ++ FAQは優れた供給源であることが判明しました。他の人々が異なる意見を持って、あなたの走行距離は変更になる場合があります。
このある以上、この自身で答えよりに答えます。
virtual
が宣言されたメソッドの実行時の派遣を要請すると同時に、の上書きの一つとして、メソッドを宣言するキーワードであるは(わき純粋仮想メソッドを実装します)。この方法は、宣言されており、このクラスから派生階層内の正確な署名と名前を共有する任意の方法がダウンし、のの上書きされますされています。あなたが親のポインタまたは参照を介して仮想メソッドを呼び出すと、ランタイムはの階層で最も派生の上書きのを呼び出しますときにオブジェクト。と呼ばれる
は、のの親メソッド隠しています。ここでの違いは、この方法は、ベースポインタを介して呼び出されるか、派生オブジェクトで呼び出されている場合、それが由来する実装をコールする一方、それは、基本実装を呼び出すする参照されているときことです。このベースおよび派生関数は無関係であるため、他の場合のうち、隠蔽と呼ばれ、派生クラスで、それが定義したコールからは非表示の基本バージョン:
struct base {
virtual void override() { std::cout << "base::override" << std::endl; }
void not_override() { std::cout << "base::not_override" << std::endl; }
};
struct derived : base {
void override() { std::cout << "derived::override" << std::endl; }
void not_override() { std::cout << "derived::not_override" << std::endl; }
};
int main() {
derived d;
base & b = d;
b.override(); // derived::override
b.not_override(); // base::not_override
d.not_override(); // derived::not_override
}
の違い、そして何の@ erik2redによって解答に間違っているが、の上書きされますのは仮想関数と密接に関係していると、の大半を決定代わりに、実行時ディスパッチメカニズムがあることを意味していることですコールするのオーバーライドを導出しました。答えに示すと、の上書きのに関連付けられている行動にはオーバーライドなく、むしろメソッドの隠蔽がない場合、実際の行動である。
のその他の問題の
言語を実装して、の純粋仮想の方法が可能になります。それは彼らと一緒に使用する必要がありますが、純粋仮想メソッドが実行時のディスパッチのために考慮されることはありませんどのような用語については何も言うことはありません。その理由は、純粋仮想メソッドを持つクラスが(実装しても)抽象クラスとみなされており、ときにクラスのオブジェクトをインスタンス化することはできませんということです。あなたがそのメソッドの実装を提供し、派生クラスを作成したら、その実装は、階層内のの最終オーバーライドのとなります。クラスは今、インスタンス化することができますが、純粋仮想メソッドが実行時のディスパッチ機構を介して呼び出されません。
仮想の最終オーバーライドのではない方法と同様に、完全修飾名を使用している場合は隠してメソッドを呼ぶことができます。仮想メソッドの場合は、完全修飾名を使用すると、通話のための多型派遣のメカニズムを無効にします。d.base::override()
は、クラスを導出する際に、他のの上書きのがあっても基本実装を呼び出します。
の方法は、基底クラスに他の方法を非表示にすることができます。
struct base {
void f() {}
};
struct derived : base {
void f(int) {}
};
int main() {
derived d;
// d.f() // error, derived::f requires an argument, base::f is hidden in this context
}
と同様にの上書きの方法は、それが多型behavior--を持っていませんが、決して仮想宣言されていないとして、それは、ない--it多型を無効にしていないので、d.base::f()
は、基本バージョンを呼び出しますので、完全な資格は、それが派生クラス内の別の方法で隠されていた場合でも、方法があるようコンパイラーに指示します。
仮想関数は、基底クラスの振る舞いの設計に役立つために存在しています。純粋仮想関数の基底クラスをインスタンス化することはできませんし、抽象クラスと呼ばれています。
これは、基本クラスに仮想関数によって記載されているもののメソッドを実装する派生クラス次第です。派生クラスは、(それらが存在するとメモリを占有)インスタンス化することができます。
派生クラスから派生することは既に親オブジェクトに定義された関数をredfineことができます。この技術はすでにオーバーライドとして知っていて、あなたがこの子オブジェクトの動作をカスタマイズすることができます。
あなたはより多くのC ++を学ぶとして、あなたはその継承は、あることを、最大ひび割れだことすべてではありません見つけることができます。組成とは、多くの場合、より良い代替手段です。楽しみを持っています。
ジャワから来た場合、1が混乱非仮想メンバ関数対仮想の概念を見つけるかもしれません。覚えておくべき事は、JavaメソッドはC ++での仮想メンバ関数に対応することです。
問題はそんなに私たちが実際に仮想関数を持っている理由はありませんが、なぜ我々は非仮想のものがありますか?私は(私が間違っているなら、私を修正)自分自身にそれを正当化する方法は、それらの呼び出しは、コンパイル時に解決できるよう、彼らは、実装が安価であるということです。
古典的な例は、基本Shapeクラスが仮想描画()関数を使用して作成されたペイントプログラムのことです。次に、形状(円形、長方形、三角形、等)の各々は、それぞれの適切な方法でそれらのドロー()関数を実装するサブクラスとして作成することができ、コアのペイントプログラムは、(それぞれ適切な描画を行います形状のリストを維持することができベースShapeクラスへのポインタのみが格納されていても)機能
あなたは、基本クラスのオブジェクトへのポインタを通じて、派生クラスのメソッドを呼び出したときに差がちょうど使用されています。その瞬間、あなたが呼び出しているメソッドは、派生クラスでオーバーライドした場合は、仮想た代わり場合は、ベースクラスのexceutionを得るでしょう、あなたは、派生クラスのメソッドの実行を持っています。
#include <iostream>
class A{
public:
virtual void getA() { std::cout << "A in base" << std::endl;};
};
class B : public A {
public:
void getA() { std::cout << "A in derived class" << std::endl;}
};
int main(int argc, char** argv)
{
A a;
B b;
a.getA();
b.getA();
A* t = new B;
t->getA();
}
例:このプログラムt->getA()
印刷"A in derived class"
ではなく、基底クラスAでない仮想改質剤there'were場合、それは"A in base"
を印刷することになる
それがお役に立てば幸いです。
ヘリコプターや飛行機が飛ぶの両方が、彼らはさまざまな方法でそれを行う - 彼らはいくつかの仮想的なオブジェクトフライヤーの両方のインスタンスです。あなたが「飛ぶ」ことフライヤーオブジェクトを求めることができます - 。しかし、フライヤーは、それは飛ぶことができるはずそれ以外の飛行については何も知らないだけのインターフェイスです。
しかし、ヘリコプターと飛行機の両方が、飛行場のオブジェクトを持っていたし、あなたがそれを与えた場合、すべての飛行場が行う必要フライヤーが飛ぶようにチラシを要求するよりも、チラシのインタフェースに従うならば。
例
Airplace X=Airplane X("boeing 747");
Airfield::takeoff(&X);
Helicopter Y= Helicopter("Comache");
Airfield::takeof(&Y);
void Airfield::takeOff(Flyer * f)
{
f->fly();
}
C ++は、厳密な型安全な言語であり、機能のこの種(機能させることは、基本クラスを介して間接的に派生クラスへのコール)RTTIをオブジェクト階層に対して有効にし、仮想メンバ関数を修飾することは、これを可能にされている場合にのみ可能ですます。