5

基本コンストラクターから仮想関数を呼び出すと、コンパイラーはエラーを出しません。しかし、基本クラスのコンストラクターから純粋仮想関数を呼び出すと、コンパイルエラーが発生します。

以下のサンプルプログラムを検討してください。

#include <iostream>

using namespace std;
class base
{
   public:
      void virtual virtualfunc() = 0;
      //void virtual virtualfunc();
      base()
      {
         virtualfunc();
      }
};

void base::virtualfunc()
{
   cout << " pvf in base class\n";
}

class derived : public base
{
   public:
   void virtualfunc()
   {
      cout << "vf in derived class\n";
   }
};

int main()
{
   derived d;
   base *bptr = &d;
   bptr->virtualfunc();

   return 0;
}

ここでは、純粋仮想関数に定義があることがわかります。bptr->virtualfunc()基本クラスで定義された純粋仮想関数は、が実行されたときに呼び出されることを期待していました。代わりに、コンパイルエラーが発生します。

エラー:コンストラクターから呼び出された抽象仮想 `virtual void base :: virtualfunc()'

これの理由は何ですか?

4

4 に答える 4

10

コンストラクターから純粋仮想関数を呼び出さないでください。未定義動作が発生します。

C ++ 03 10.4/6状態

「メンバー関数は、抽象クラスのコンストラクタ(またはデストラクタ)から呼び出すことができます。そのようなコンストラクタから作成(または破棄)されるオブジェクトに対して、純粋仮想関数を直接または間接的に仮想呼び出し(10.3)する効果(またはデストラクタ)は未定義です。」

virtualfunc()Baseクラスで純粋仮想関数を定義していないため、コンパイルエラーが発生します。それを呼び出すことができるためには、それは体を持っている必要があります。

とにかく、コンストラクターで純粋仮想関数を呼び出すことは未定義動作であるため、避ける必要があります。

于 2011-12-27T08:02:12.833 に答える
2

C ++ 11には、回避策があります。

コンストラクター委任のプロセスでは、実際には、純粋仮想メソッドの実装を呼び出すことができます。ただし、純粋仮想メソッドを実装するクラスのコンストラクターの少なくとも1つが、純粋仮想呼び出しが呼び出される前に完了している必要があります。 。

サブクラスの動作が予測できないものにならないようにするために、「final」キーワードを使用できます/使用する必要があります。

参照:C ++ 11委任コンストラクター純粋仮想メソッドおよび関数呼び出し—危険?

#include <string>

/**************************************/
class Base
{
public:
    int sum;
    virtual int Do() = 0;

    void Initialize()
    {
        Do();
    }
    Base()
    {
    }
};

/**************************************/
// Optionally declare class as "final" to avoid
// issues with further sub-derivations.
class Derived final : public Base
{
public:

    virtual int Do() override final
    {
        sum = 0 ? 1 : sum;
        return sum / 2 ; // .5 if not already set.
    }

    Derived(const std::string & test)
        : Derived() // Ensure "this" object is constructed.
    {
        Initialize(); // Call Pure Virtual Method.
    }
    Derived()
        : Base()
    {
        // Effectively Instantiating the Base Class.
        // Then Instantiating This.
        // The the target constructor completes.
    }
};




/********************************************************************/
int main(int args, char* argv[])
{
    Derived d;
    return 0;
}
于 2013-02-04T19:01:03.477 に答える
1

基本クラスコンストラクターを使用している場合、派生クラスは存在しないことを覚えておく必要があります。詳細情報:</ p>

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.5

純粋仮想関数を呼び出そうとしている場合、まだ実装されていません。

解決策はたくさんあります。最も簡単な方法は、基本クラスのコンストラクターの後に呼び出す別のメンバー関数「init()」を作成することです。

于 2011-12-27T08:29:16.177 に答える
0

コンパイラーは、コンストラクターが完了する前に、純粋仮想関数にポインターが設定されていると想定する必要はありません。つまり、その時点で関数の定義があることを知っている必要はありません。したがって、動作は定義されていません。一部のコンパイラ(MSVC)では期待どおりに動作し、その他のコンパイラでは現在発生しているエラーが発生します。他のいくつかではコンパイルされますが、セグメンテーション違反が発生します。

とにかくコンストラクターから仮想関数を呼び出すことは、コードの意図が不明確で混乱を招くため、実際には悪い考えです。

于 2011-12-27T08:38:45.857 に答える