2009-06-30 12 views

答えて

1

:オブジェクト指向プログラミングにおいて

、 仮想関数または仮想メソッドが でその挙動 が同じで機能によって継承 クラス内でオーバーライドすることができる関数またはメソッド署名。

7

他の言語と同様に、多態性が必要です。これにはたくさんの使い方があります。たとえば、入力をコンソールやファイルなどのデバイスから読み取る方法を抽象化したいとします。ジェネリックリーダーインターフェイスと仮想関数を使用した複数の具体的な実装が可能です。

4

プロキシ方法。実行時にメソッドを上書きする。例えば、NHibernateはこれを使用して遅延ロードをサポートします。

1

たとえば、基本クラスParamsと派生クラスのセットがあります。 paramsから派生したすべてのクラスを格納する配列に対して同じ操作を実行できるようにしたいとします。

問題ありません - メソッドを仮想的に宣言し、Paramsクラスに基本的な実装をいくつか追加し、派生クラスでオーバーライドします。これで、配列をたどって参照を介してメソッドを呼び出すことができます。正しいメソッドが呼び出されます。

class Params { 
public: 
    virtual void Manipulate() { //basic impl here } 
} 

class DerivedParams1 : public Params { 
public: 
    override void Manipulate() { 
     base.Manipulate(); 
     // other statements here 
    } 
}; 

// more derived classes can do the same 

void ManipulateAll(Params[] params) 
{ 
    for(int i = 0; i < params.Length; i++) { 
     params[i].Manipulate(); 
    } 
} 
3

派生クラスに関数をオーバーライドできることを伝えるために使用されます。

MSDNの良い例はhereです。

3

基本的に仮想メンバで多態性を表現できます。派生クラスは、その基本クラスのメソッドと同じシグネチャを持つメソッドを持つことができ、基本クラスは派生クラスのメソッドを呼び出します。

基本的な例:したがって、基本的

public class Shape 
{ 
    // A few example members 
    public int X { get; private set; } 
    public int Y { get; private set; } 
    public int Height { get; set; } 
    public int Width { get; set; } 

    // Virtual method 
    public virtual void Draw() 
    { 
     Console.WriteLine("Performing base class drawing tasks"); 
    } 
} 

class Circle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a circle... 
     Console.WriteLine("Drawing a circle"); 
     base.Draw(); 
    } 
} 
class Rectangle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a rectangle... 
     Console.WriteLine("Drawing a rectangle"); 
     base.Draw(); 
    } 
} 
class Triangle : Shape 
{ 
    public override void Draw() 
    { 
     // Code to draw a triangle... 
     Console.WriteLine("Drawing a triangle"); 
     base.Draw(); 
    } 
} 
+2

"で、基底クラスは派生クラスのメソッドを呼び出します。これはサンプルコードと対になるかなり混乱したステートメントです。テキストはbase.Drawによって派生クラスDrawメソッドが呼び出されることを示します(StackOverflowExceptionが発生すると思います)。私はあなたのポイントを得るが、新人はそうではないかもしれない。そのようなサンプルコードは本当に良いですが、それを実行すると、何が起こるかを明確に示します。 –

71

あなたの先祖クラスには、メソッドのために特定の行動をしたい場合。あなたの子孫が同じメソッドを使用していますが、実装が異なる場合は、に置き換えることができます。仮想というキーワードがある場合。

using System; 
class TestClass 
{ 
    public class Dimensions 
    { 
     public const double pi = Math.PI; 
     protected double x, y; 
     public Dimensions() 
     { 
     } 
     public Dimensions (double x, double y) 
     { 
     this.x = x; 
     this.y = y; 
     } 

     public virtual double Area() 
     { 
     return x*y; 
     } 
    } 

    public class Circle: Dimensions 
    { 
     public Circle(double r): base(r, 0) 
     { 
     } 

     public override double Area() 
     { 
     return pi * x * x; 
     } 
    } 

    class Sphere: Dimensions 
    { 
     public Sphere(double r): base(r, 0) 
     { 
     } 

     public override double Area() 
     { 
     return 4 * pi * x * x; 
     } 
    } 

    class Cylinder: Dimensions 
    { 
     public Cylinder(double r, double h): base(r, h) 
     { 
     } 

     public override double Area() 
     { 
     return 2*pi*x*x + 2*pi*x*y; 
     } 
    } 

    public static void Main() 
    { 
     double r = 3.0, h = 5.0; 
     Dimensions c = new Circle(r); 
     Dimensions s = new Sphere(r); 
     Dimensions l = new Cylinder(r, h); 
     // Display results: 
     Console.WriteLine("Area of Circle = {0:F2}", c.Area()); 
     Console.WriteLine("Area of Sphere = {0:F2}", s.Area()); 
     Console.WriteLine("Area of Cylinder = {0:F2}", l.Area()); 
    } 
} 

編集:コメントに質問
私は、基本クラスで仮想キーワードを使用しない場合、それは動作しますか?

子孫のクラスでoverrideキーワードを使用すると機能しません。コンパイラエラーCS0506 'function1':継承されたメンバ 'function2'を「仮想」、「抽象的な」、または「上書き」されていないためオーバーライドできません。

オーバーライドを使用しないと、 CS0108警告 'desc.Method()'は継承されたメンバー 'base.Method()を隠蔽します。隠蔽が意図されていれば、newキーワードを使用してください。

これを回避するには、方法の前にnewというキーワードを入れて、を隠してにしてください。

new public double Area() 
    { 
    return 2*pi*x*x + 2*pi*x*y; 
    } 

派生クラスの仮想メソッドをオーバーライドすることは必須ですか?
あなたがメソッドをオーバーライドしていない場合はNoを、子孫クラスは、それが継承されるメソッドを使用します。

+3

基本クラスでvirtualキーワードを使用しないとどうなりますか?それは働くでしょうか?派生クラスの仮想メソッドをオーバーライドすることは強制的ですか? – Preeti

+4

@Pre私はあなたの質問に答えました –

+1

John.nowありがとうございます。 – Preeti

3

これは、実行時ではなく、オブジェクトのメンバーが呼び出されます、コンパイル時に決定することを意味し、遅延バインディングを達成することができます。 Wikipediaを参照してください。

55

仮想関数の実用的な使用方法を理解する鍵は、特定のクラスのオブジェクトが最初のオブジェクトのクラスから派生したクラスの別のオブジェクトを割り当てることができることを心に留めておくことです。

例えば:

class Animal { 
    public void eat() {...} 
} 

class FlyingAnimal : Animal { 
    public void eat() {...} 
} 

Animal a = new FlyingAnimal(); 

Animalクラスは、一般的に、動物(例えば口の中のオブジェクトを入れて飲み込む)食べるべきかを説明関数eat()を有しています。

しかし、飛行動物には特別な食事方法があるため、FlyingAnimalクラスには新しいeat()メソッドを定義する必要があります。

だからここに頭に浮かぶ質問です:私はタイプAnimalの変数aを宣言し、それをタイプFlyingAnimalの新しいオブジェクトをasignedた後、a.eat()は何をしますか?どちらの方法のどちらを呼びますか?

答えはaAnimalのタイプであるため、Animalのメソッドを呼び出します。コンパイラはダムで、別のクラスのオブジェクトをa変数に代入することは知られていません。

ここにvirtualキーワードが現れます:メソッドをvirtual void eat() {...}と宣言すると、基本的にはコンパイラに「賢明ではないので扱えないほど賢明なことをしています。 "したがって、コンパイラはa.eat()という呼び出しを2つの方法のいずれかにリンクしようとしませんが、代わりに実行時にを実行するようにシステムに指示します

だからのみのコードが実行されると、システムはaを見てのコンテンツタイプないその宣言された型にしてFlyingAnimal実行の方法をします。

あなたは不思議に思うかもしれません:地獄は、私はそれをしたいと思う理由は?なぜ始まりからちょうどいいと言わないのですかFlyingAnimal a = new FlyingAnimal()

その理由は、たとえば、あなたが Animalから多くの派生クラスを持っていること、ということである

FlyingAnimalSwimmingAnimalBigAnimalWhiteDogなど

Animal[] happy_friends = new Animal[100]; 

我々は100匹の幸せな動物で世界を持っている:そして、ある時点で、あなたが言うので、多くのAnimal Sを含む世界を定義したいです。あなたはいくつかの点でそれらを初期化し :

... 
happy_friends[2] = new AngryFish(); 
... 
happy_friends[10] = new LoudSnake(); 
... 

そして、一日の終わりに、あなたは彼らがスリープ状態に入る前に、誰もが食べたいです。だからあなたは言ってみたいと思う:

あなたが見ることができるように、各動物はそれ自身の食べ方を持っています。 仮想機能を使用することによってのみ、この機能を実現できます。そうでなければ、誰もが全く同じ方法で "食べる"ように強制されるでしょう:最も一般的なeat関数に記載されているように、Animalクラス内で機能します。

編集: この動作は、実際にはのデフォルトで、Javaなどの一般的な高級言語で行われます。 C#での仮想関数の

+6

なぜ、Animalを代わりにインターフェイスにしてみませんか?すべての動物が使用できる動物の基本機能なので – Djorge

+1

@Djorge正確に。 –

+1

ありがとう非常に良い説明。最高の答え! :) – wenn32

1

使用は

仮想関数はほとんど同じ署名で派生クラスの基底クラスのメソッドをオーバーライドするために使用されます。

派生クラスが基本クラスを継承する場合、派生クラスのオブジェクトは、派生クラスまたは基本クラスのいずれかへの参照です。

仮想関数が基底クラスで(つまり、実行時バインディング)の後半コンパイラによる

virtualを解決され、機能のほとんどの派生クラスの実装は、参照されるオブジェクトの実際の型に応じて呼ばれています宣言されたポインタまたは参照の型にかかわらず。 virtualでなければ、メソッドはearlyで解決され、呼び出された関数はポインタまたは参照の宣言された型に従って選択されます。