回答:
ウィキペディアの仮想機能 ...
オブジェクト指向プログラミングでは、C ++やObject Pascalなどの言語では、仮想関数または仮想メソッドは、動的ディスパッチが容易になる継承可能でオーバーライド可能な関数またはメソッドです。この概念は、オブジェクト指向プログラミング(OOP)の(実行時の)ポリモーフィズム部分の重要な部分です。つまり、仮想関数は実行するターゲット関数を定義しますが、ターゲットはコンパイル時に認識されない場合があります。
非仮想関数とは異なり、仮想関数がオーバーライドされると、作成されたレベルだけでなく、クラス階層のすべてのレベルで最も派生したバージョンが使用されます。したがって、基本クラスの1つのメソッドが仮想メソッドを呼び出す場合、基本クラスで定義されたバージョンの代わりに、派生クラスで定義されたバージョンが使用されます。
これは、派生クラスでオーバーライドできる非仮想関数とは対照的ですが、「新しい」バージョンは派生クラス以下でのみ使用され、基本クラスの機能はまったく変更されません。
一方..
純粋仮想関数または純粋仮想メソッドは、派生クラスが抽象でない場合に、派生クラスによって実装される必要がある仮想関数です。
純粋な仮想メソッドが存在する場合、クラスは「抽象的」であり、それ自体ではインスタンス化できません。代わりに、純粋仮想メソッドを実装する派生クラスを使用する必要があります。純粋仮想は基本クラスでまったく定義されていないため、派生クラスで定義する必要があります。そうでない場合、その派生クラスも抽象化されており、インスタンス化できません。インスタンス化できるのは、抽象メソッドを持たないクラスだけです。
virtualは基本クラスの機能をオーバーライドする方法を提供し、pure-virtual はそれを必要とします。
pure
キーワードを追加したいと言ったが、Bell LabsがC ++のメジャーリリースを作成しようとしており、彼のマネージャーはその後半の段階ではそれを許可しないと言っていたと思います。キーワードを追加するのは大変なことです。
ウィキペディアの仮想の定義についてコメントしたいと思います。[この回答が書かれた時点で]ウィキペディアは、仮想メソッドをサブクラスでオーバーライドできるものとして定義しました。[幸いなことに、ウィキペディアは編集されており、これで正しく説明されています。]これは誤りです。仮想メソッドだけでなく、すべてのメソッドをサブクラスでオーバーライドできます。virtualが行うことは、ポリモーフィズム、つまり、メソッドの最も派生したオーバーライドを実行時に選択する機能を提供することです。
次のコードを検討してください。
#include <iostream>
using namespace std;
class Base {
public:
void NonVirtual() {
cout << "Base NonVirtual called.\n";
}
virtual void Virtual() {
cout << "Base Virtual called.\n";
}
};
class Derived : public Base {
public:
void NonVirtual() {
cout << "Derived NonVirtual called.\n";
}
void Virtual() {
cout << "Derived Virtual called.\n";
}
};
int main() {
Base* bBase = new Base();
Base* bDerived = new Derived();
bBase->NonVirtual();
bBase->Virtual();
bDerived->NonVirtual();
bDerived->Virtual();
}
このプログラムの出力は何ですか?
Base NonVirtual called.
Base Virtual called.
Base NonVirtual called.
Derived Virtual called.
Derivedは、Baseのすべてのメソッドをオーバーライドします。仮想メソッドだけでなく、非仮想メソッドもオーバーライドします。
Base-pointer-to-Derived(bDerived)がある場合、NonVirtualを呼び出すと、Baseクラスの実装が呼び出されることがわかります。これはコンパイル時に解決されます。コンパイラはbDerivedがBase *であると認識し、NonVirtualは仮想ではないため、Baseクラスで解決します。
ただし、Virtualを呼び出すと、Derivedクラスの実装が呼び出されます。キーワードvirtualのため、メソッドの選択はコンパイル時ではなく実行時に行われます。ここでコンパイル時に行われるのは、コンパイラーがこれがBase *であると認識し、仮想メソッドを呼び出しているため、Baseクラスではなくvtableへの呼び出しを挿入することです。このvtableは実行時にインスタンス化されるため、実行時に最も派生したオーバーライドに解決されます。
これがあまり混乱しないことを願っています。つまり、任意のメソッドをオーバーライドできますが、ポリモーフィズム、つまり最も派生したオーバーライドを実行時に選択できるのは仮想メソッドだけです。ただし実際には、非仮想メソッドをオーバーライドすることは悪い習慣と見なされ、めったに使用されないため、多くの人(Wikipediaの記事を書いた人を含む)は、仮想メソッドのみをオーバーライドできると考えています。
Derived*
ポイントをホームにドライブするために同じ関数呼び出しでを追加すると役立つ場合があります。そうでなければ素晴らしい答え
virtualキーワードは、C ++にポリモーフィズムをサポートする能力を与えます。次のようなクラスのオブジェクトへのポインタがある場合:
class Animal
{
public:
virtual int GetNumberOfLegs() = 0;
};
class Duck : public Animal
{
public:
int GetNumberOfLegs() { return 2; }
};
class Horse : public Animal
{
public:
int GetNumberOfLegs() { return 4; }
};
void SomeFunction(Animal * pAnimal)
{
cout << pAnimal->GetNumberOfLegs();
}
この(愚かな)例では、GetNumberOfLegs()関数は、呼び出されたオブジェクトのクラスに基づいて適切な数を返します。
次に、関数 'SomeFunction'を考えます。動物に由来する限り、どのような種類の動物オブジェクトが渡されるかは関係ありません。コンパイラーは、アニマル派生クラスをアニマルに基本クラスとして自動的にキャストします。
これを行うと:
Duck d;
SomeFunction(&d);
「2」を出力します。これを行うと:
Horse h;
SomeFunction(&h);
それは「4」を出力します。これはできません:
Animal a;
SomeFunction(&a);
これは、GetNumberOfLegs()仮想関数が純粋であるためにコンパイルされないためです。つまり、派生クラス(サブクラス)によって実装する必要があります。
純粋仮想関数は、主に次の定義に使用されます。
a)抽象クラス
これらは、それらから派生し、純粋な仮想関数を実装する必要がある基本クラスです。
b)インターフェース
これらは「空の」クラスであり、すべての関数は純粋な仮想であるため、すべての関数を導出して実装する必要があります。
C ++クラスでは、virtualは、サブクラスによってメソッドをオーバーライド(つまり、実装)できることを指定するキーワードです。例えば:
class Shape
{
public:
Shape();
virtual ~Shape();
std::string getName() // not overridable
{
return m_name;
}
void setName( const std::string& name ) // not overridable
{
m_name = name;
}
protected:
virtual void initShape() // overridable
{
setName("Generic Shape");
}
private:
std::string m_name;
};
この場合、サブクラスはinitShape関数をオーバーライドして、特別な作業を行うことができます。
class Square : public Shape
{
public:
Square();
virtual ~Square();
protected:
virtual void initShape() // override the Shape::initShape function
{
setName("Square");
}
}
純粋仮想という用語は、サブクラスによって実装する必要があり、基本クラスによって実装されていない仮想関数を指します。virtualキーワードを使用し、メソッド宣言の最後に= 0を追加して、メソッドを純粋仮想として指定します。
したがって、Shape :: initShapeを純粋な仮想にしたい場合は、次のようにします。
class Shape
{
...
virtual void initShape() = 0; // pure virtual method
...
};
純粋な仮想メソッドをクラスに追加することにより、クラスを抽象基本クラスにし て、インターフェイスを実装から分離するのに非常に便利です。
m_name
。どういうm_
意味ですか?
「仮想」とは、メソッドがサブクラスでオーバーライドされる可能性があるが、基本クラスで直接呼び出し可能な実装があることを意味します。「純粋仮想」とは、直接呼び出し可能な実装がない仮想メソッドであることを意味します。このようなメソッドは、継承階層で少なくとも1回オーバーライドする必要があります。クラスに実装されていない仮想メソッドがある場合、そのクラスのオブジェクトは構築できず、コンパイルは失敗します。
@quarkは、純粋仮想メソッドに実装を含めることができることを指摘していますが、純粋仮想メソッドをオーバーライドする必要があるため、デフォルトの実装を直接呼び出すことはできません。以下は、デフォルトの純粋仮想メソッドの例です。
#include <cstdio>
class A {
public:
virtual void Hello() = 0;
};
void A::Hello() {
printf("A::Hello\n");
}
class B : public A {
public:
void Hello() {
printf("B::Hello\n");
A::Hello();
}
};
int main() {
/* Prints:
B::Hello
A::Hello
*/
B b;
b.Hello();
return 0;
}
コメントによると、コンパイルが失敗するかどうかはコンパイラ固有です。GCC 4.3.3以降では、コンパイルできません。
class A {
public:
virtual void Hello() = 0;
};
int main()
{
A a;
return 0;
}
出力:
$ g++ -c virt.cpp
virt.cpp: In function ‘int main()’:
virt.cpp:8: error: cannot declare variable ‘a’ to be of abstract type ‘A’
virt.cpp:1: note: because the following virtual functions are pure within ‘A’:
virt.cpp:3: note: virtual void A::Hello()
virtualキーワードはどのように機能しますか?
人間が基本階級であると仮定すると、インド人は人間から派生しています。
Class Man
{
public:
virtual void do_work()
{}
}
Class Indian : public Man
{
public:
void do_work()
{}
}
do_work()を仮想として宣言することは、単に、どのdo_work()を呼び出すかが実行時にのみ決定されることを意味します。
私がやるとします
Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.
virtualを使用しない場合は、どのオブジェクトが呼び出しているかに応じて、コンパイラによって静的に決定されるか、静的にバインドされます。そのため、Manのオブジェクトがdo_work()を呼び出す場合、Manのdo_work()は、インドのオブジェクトを指す場合でも呼び出されます。
私は、トップ投票の答えは誤解を招くものだと思います-virtualが派生クラスでオーバーライドされた実装を持つことができるかどうかにかかわらず、すべてのメソッド。C ++を特に参照すると、正しい違いは、関連する関数のバインディング(実行時(仮想が使用されている場合))バインディングとコンパイル時(仮想が使用されていないがメソッドがオーバーライドされ、ベースポインターが派生オブジェクトにポイントされている場合)バインディングです。
別の誤解を招くコメントがあるようです、
「ジャスティン、「純粋な仮想」は、「この関数は基本クラスでは実装できない」という意味で使用される用語(キーワードではなく、以下の私の回答を参照)にすぎません。
これは間違っています!純粋な仮想関数は、ボディを持つこともでき、実装することもできます。真実は、抽象クラスの純粋な仮想関数を静的に呼び出すことができるということです!2人の非常に優れた著者は、Bjarne StroustrupとStan Lippman ...です。彼らが言語を書いたからです。
Simula、C ++、およびC#は、デフォルトで静的メソッドバインディングを使用します。プログラマーは、特定のメソッドに仮想バインディングを付けることにより、動的バインディングを使用するように指定できます。動的メソッドバインディングは、オブジェクト指向プログラミングの中心です。
オブジェクト指向プログラミングには、カプセル化、継承、動的メソッドバインディングという3つの基本概念が必要です。
カプセル化により、抽象化の実装の詳細を単純なインターフェースの背後に隠すことができます。
継承によって、新しい抽象化を既存の抽象化の拡張または改良として定義し、その特性の一部またはすべてを自動的に取得できます。
動的メソッドバインディングにより、古い抽象化が必要なコンテキストで使用されている場合でも、新しい抽象化が新しい動作を表示できます。
仮想メソッドは、派生クラスによってオーバーライドできますが、基本クラス(オーバーライドされるもの)での実装が必要です。
純粋仮想メソッドには、基本クラスの実装はありません。それらは、派生クラスによって定義される必要があります。(つまり、オーバーライドするものがないため、技術的にオーバーライドすることは適切な用語ではありません)。
派生クラスが基本クラスのメソッドをオーバーライドする場合、VirtualはデフォルトのJava動作に対応します。
純粋仮想メソッドは、抽象クラス内の抽象メソッドの動作に対応しています。また、純粋な仮想メソッドと定数のみを含むクラスは、インターフェイスのcppペンダントになります。
純粋仮想関数
このコードを試してください
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()=0;
};
class anotherClass:aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"hellow World";
}
};
int main()
{
//aClassWithPureVirtualFunction virtualObject;
/*
This not possible to create object of a class that contain pure virtual function
*/
anotherClass object;
object.sayHellow();
}
クラスanotherClassで関数sayHellowを削除し、コードを実行します。エラーが発生します!クラスに純粋な仮想関数が含まれている場合、そのクラスからオブジェクトを作成できず、オブジェクトが継承されるため、派生クラスはその関数を実装する必要があります。
仮想機能
別のコードを試してください
#include <iostream>
using namespace std;
class aClassWithPureVirtualFunction
{
public:
virtual void sayHellow()
{
cout<<"from base\n";
}
};
class anotherClass:public aClassWithPureVirtualFunction
{
public:
void sayHellow()
{
cout<<"from derived \n";
}
};
int main()
{
aClassWithPureVirtualFunction *baseObject=new aClassWithPureVirtualFunction;
baseObject->sayHellow();///call base one
baseObject=new anotherClass;
baseObject->sayHellow();////call the derived one!
}
ここで、sayHellow関数は基本クラスで仮想としてマークされています。派生クラスで関数を検索して関数を実装しようとするコンパイラーを言います。見つからない場合は、基本関数を実行します。
仮想関数には基本クラスと派生クラスの定義が必要ですが、必須ではありません。たとえば、ToString()またはtoString()関数は仮想であるため、ユーザー定義クラスでオーバーライドすることで独自の実装を提供できます。
仮想関数は、通常のクラスで宣言および定義されます。
純粋仮想関数は「= 0」で終わるように宣言する必要があり、抽象クラスでのみ宣言できます。
純粋な仮想関数を持つ抽象クラスは、その純粋な仮想関数の定義を持つことができないため、その抽象クラスから派生したクラスで実装を提供する必要があることを意味します。