virtualとは - c++> virtual 0




C++仮想/純粋仮想記述 (9)

"仮想関数または仮想メソッドは、同じシグネチャを持つ関数によって継承クラス内でビヘイビアをオーバーライドできる関数またはメソッドです" - wikipedia

これは、仮想関数のための良い説明ではありません。 なぜなら、メンバが仮想ではないとしても、継承するクラスはそれをオーバーライドすることができるからです。 あなたは自分でそれを試して見ることができます。

この違いは、関数が基本クラスをパラメータとして取るときに、それ自体を示します。 継承クラスを入力として与えると、その関数はオーバーライドされた関数の基本クラスの実装を使用します。 ただし、その関数が仮想の場合は、派生クラスで実装されている関数を使用します。

関数が仮想として定義され、それが純粋仮想と同じであれば、正確にはどういう意味ですか?


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");
    }
}

純粋仮想という用語は、サブクラスによって実装される必要があり、基本クラスによって実装されていない仮想関数を指す。 仮想キーワードを使用し、メソッド宣言の最後にa = 0を追加することによって、メソッドを純粋な仮想として指定します。

したがって、Shape :: initShapeを純粋な仮想にしたい場合、次のようにします:

class Shape 
{
 ...
    virtual void initShape() = 0; // pure virtual method
 ... 
};

あなたのクラスに純粋な仮想メソッドを追加することで、クラスを抽象基本クラスにすることができます。これは、インタフェースを実装から分離するのに非常に便利です。


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'を考えてみましょう。 それはAnimalから派生している限り、どのタイプの動物オブジェクトに渡されるかは気にしません。 コンパイラは、基本クラスであるため、Animalから派生したクラスを自動的にAnimalにキャストします。

これを行うと:

Duck d;
SomeFunction(&d);

それは '2'を出力します。 これを行うと:

Horse h;
SomeFunction(&h);

それは '4'を出力します。 私たちはこれを行うことはできません:

Animal a;
SomeFunction(&a);

GetNumberOfLegs()仮想関数が純粋であるためにコンパイルされないため、クラス(サブクラス)を派生させて実装する必要があります。

純粋仮想関数は、主に以下を定義するために使用されます。

a)抽象クラス

これらは基本クラスであり、そこから派生し、純粋な仮想関数を実装する必要があります。

b)インターフェース

これらはすべての関数が純粋な仮想であるため、すべての関数を派生して実装する必要がある「空の」クラスです。


「仮想」とは、メソッドがサブクラスでオーバーライドされる可能性があるが、直接呼び出し可能な実装を基本クラスに持つことを意味します。 「純粋仮想」とは、それが直接呼び出し可能な実装を持たない仮想メソッドであることを意味します。 そのようなメソッドは、継承階層で少なくとも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()

仮想関数は、基本クラスで宣言され、派生クラスによって再定義されるメンバ関数です。 仮想関数は、継承の順に階層構造になっています。 派生クラスが仮想関数をオーバーライドしない場合、その基本クラス内で定義された関数が使用されます。

純粋仮想関数は、基本クラスに対して定義を含まない関数です。 基本クラスには実装がありません。 派生クラスであればこの関数をオーバーライドする必要があります


純粋仮想関数

このコードを試してください

#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関数は、基本クラスに仮想としてマークされています。これは、派生クラスの関数を検索して関数を実装しようとするコンパイラを表します。見つからなければ、ベースを実行します。


バーチャルメソッドはクラスを派生させることでオーバーライドできますが、オーバーライドされるベースクラスの実装が必要です

純粋な仮想メソッドは基本クラスを実装していません。 それらは派生クラスによって定義される必要があります。 (オーバーライドするものはないので、技術的にオーバーライドされるのは適切な用語ではありません)。

Virtualは、派生クラスが基本クラスのメソッドをオーバーライドするときのデフォルトのJavaの動作に対応します。

純粋仮想メソッドは、抽象クラス内の抽象メソッドの動作に対応しています。 純粋な仮想メソッドと定数だけを含むクラスは、Interfaceのcpp-pendantになります。


仮想キーワードはどのように機能しますか?

人は基本クラス、インドは人から派生したものとします。

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.

仮想が使用されていない場合は、オブジェクトが呼び出しているかどうかに応じて、静的に決定されるか、またはコンパイラによって静的にバインドされます。 したがって、Manのオブジェクトがdo_work()を呼び出すと、Manのdo_work()はそれもインディアンオブジェクトのポイントと呼ばれます

私はトップの投票答えが誤解を招くものであると信じています - 仮想が派生クラスにオーバーライドされた実装を持つことができるかどうかにかかわらず、あらゆるメソッド。 C ++を具体的に参照すると、実行時(仮想が使用されている場合)のバインディングとコンパイル時(仮想が使用されないがメソッドがオーバーライドされ、派生オブジェクトにベースポインタがポイントされる)

誤解を招くような別のコメントがあるようですが、

"ジャスティン、"純粋な仮想 "は、(このキーワードは、以下の私の答えを参照してください)単なる用語ではなく、"この関数は基本クラスでは実装できません "という意味です。

これは間違っています! 純粋に仮想関数も本体を持つことができ、実装することができます! 真実は、抽象クラスの純粋仮想関数は静的に呼び出すことができるということです! 非常に良い2人の作家はBjarne StroustrupとStan Lippmanです。彼らが言語を書いたからです。


  • 仮想関数は基本クラスと派生クラスの定義を持っていなければなりませんが、ToString()やtoString()などの関数はVirtualクラスであるため、ユーザ定義のクラスでオーバーライドすることで独自の実装を提供できます。

  • 仮想関数は、通常のクラスで宣言され、定義されます。

  • 純粋仮想関数は、 "= 0"で終わる宣言されなければならず、抽象クラスでのみ宣言できます。

  • 純粋仮想関数を持つ抽象クラスは、その純粋仮想関数の定義を持つことができないので、その抽象クラスから派生したクラスで実装を提供しなければならないことを意味します。





virtual