1

更新:この問題は、メモリの使用率が低いために発生します。下部の解決策を参照してください。

ここにいくつかの半疑似コードがあります:

class ClassA
{
public:
    virtual void VirtualFunction();
    void SomeFunction();
}

class ClassB : public ClassA
{
public:
    void VirtualFunction();
}

void ClassA::VirtualFunction()
{
    // Intentionally empty (code smell?).
}

void ClassA::SomeFunction()
{
    VirtualFunction();
}

void ClassB::VirtualFunction()
{
    // I'd like this to be called from ClassA::SomeFunction()
    std::cout << "Hello world!" << endl;
}

C# に相当するものは次のとおりです。C# の例は、実際の問題とは関係がないため削除されました。

ClassB::VirtualFunctionから呼び出されたときに関数が呼び出されないのはなぜClassA::SomeFunctionですか? 代わりClassA::VirtualFunctionに呼び出されています...

仮想関数 ClassA::VirtualFunction の実装を強制すると、次のようになります。

class ClassA
{
public:
    virtual void VirtualFunction() = 0;
    void SomeFunction();
}

class ClassB : public ClassA
{
public:
    void VirtualFunction();
}

void ClassA::SomeFunction()
{
    VirtualFunction();
}

void ClassB::VirtualFunction()
{
    // I'd like this to be called from ClassA::SomeFunction()
    std::cout << "Hello world!" << endl;
}

派生関数が確実に宣言および定義されているにもかかわらず、実行時に次のエラーが発生します。

pure virtual method called
terminate called without an active exception

注:メモリ使用量が悪い場合でも、エラーが発生する可能性があるようです。詳細については、自己回答を参照してください。

更新 1 - 4:

コメントは削除されました (関係ありません)。

解決:

回答として投稿しました。

4

8 に答える 8

5
class Base {
public:
   virtual void f() { std::cout << "Base" << std::endl; }
   void call() { f(); }
};
class Derived : public Base {
public:
   virtual void f() { std::cout << "Derived" << std::endl; }
};
int main()
{
   Derived d;
   Base& b = d;
   b.call(); // prints Derived
}

Base クラスで関数を実装したくない場合は、次のように宣言する必要があります。

class Base {
public:
   virtual void f() = 0; // pure virtual method
   void call() { f(); }
};

また、コンパイラはクラスのインスタンス化を許可しません:

int main() {
   //Base b; // error b has a pure virtual method
   Derived d; // derive provides the implementation: ok
   Base & b=d; // ok, the object is Derived, the reference is Base
   b.call();
}

補足として、予期しない結果が生じる可能性があるため、コンストラクタまたはデストラクタから仮想関数を呼び出さないように注意してください。

于 2009-04-10T16:49:24.340 に答える
4

「pure virtual method called terminate called without an active exception」というエラー メッセージが表示される場合は、classA (基本クラス) のコンストラクタまたはデストラクタから仮想関数を呼び出していることを意味します。これは、実行すべきではありません。

于 2009-04-10T17:57:35.050 に答える
3

error と呼ばれる純粋仮想メソッド:

実際には他の質問とは異なるため、別の質問を作成する必要があります。この質問への回答は、最初の質問に対する私の以前の回答の最後の段落にあります。

コンストラクタまたはデストラクタから仮想関数を呼び出さないでください

class Base
{
public:
   Base() { f(); }
   virtual void f() = 0;
};
class Derived : public Base
{
public:
   virtual void f() {}
};
int main()
{
   Derived d; // crashes with pure virtual method called
}

上記のコードの問題は、コンパイラが Derived 型のオブジェクトをインスタンス化できることです (抽象的ではないため、すべての仮想メソッドが実装されています)。クラスの構築は、すべてのベース (この場合は Base) の構築から始まります。コンパイラは、 f()のエントリが 0 (base で実装されていない)である Base 型の仮想メソッド テーブルを生成します。その後、コンパイラはコンストラクタでコードを実行します。Base パーツの構築が完了すると、Derived element パーツの構築が開始されます。コンパイラは、 f()のエントリがDerived::f( )を指すように仮想テーブルを変更します。

Base の構築中にメソッドf()を呼び出そうとすると、仮想メソッド テーブルのエントリは null のままで、アプリケーションがクラッシュします。

于 2009-04-10T18:15:49.110 に答える
1

A が VirtualFunction() を呼び出すと、B のバージョンが自動的に呼び出されます。これが仮想関数のポイントです。

私はC++の構文に精通していません。ヘッダーだけでなく、ボディのポイントでも関数を仮想として宣言する必要がありますか?

また、クラス B では、おそらくオーバーライドとしてマークする必要があります

C# では簡単です。C++の構文がわかりません。

public class ClassA
{
    public **virtual** void VirtualFunction(){}

    public void FooBar()
    {
        // Will call ClassB.VirtualFunction()
        VirtualFunction();
    } 

}

public class ClassB
{
    public **overide** void VirtualFunction()
    {
        // hello world
    }
}
于 2009-04-10T16:47:13.330 に答える
0

ClassBで関数を正しく定義していない場合は、次のようになります。

public class ClassB
{
    public void override AbstractFunction()
    {
        // hello world
    }
}

次に、基本クラスから仮想/抽象メソッドへの呼び出しは、派生インスタンスの実装を呼び出します。

于 2009-04-10T16:51:06.957 に答える
0

派生クラスに:を実装させる場合VirtualFunction

class ClassA
{
public:
    virtual void VirtualFunction()=0;
    void SomeFunction();
}

これはC++です。デフォルトでは、派生関数が呼び出されます。

基本クラス関数を呼び出したい場合は、次のようにします。

void ClassA::SomeFunction()
{
    // ... various lines of code ...

     ClassA::VirtualFunction();
}
于 2009-04-10T16:54:03.643 に答える
0

コードに問題はありませんが、サンプルは不完全です。どこから SomeFunction を呼び出しているかを述べていません。

すでに dribeas によって指摘されているように、仮想テーブルは階層内の各クラスが構築を完了するときにのみ構築されるため、コンストラクターから仮想関数を呼び出す場合は注意が必要です。

編集: 私の返信の次の段落は正しくありませんでした。申し訳ありません。vtable は (少なくとも) イニシャライザ リストの最後まで、つまりコンストラクタの本体に入ったら、ClassB のコンストラクタから SomeFunction を呼び出しても問題ありません。もちろん、ClassA のコンストラクターから呼び出すのは問題ありません。

元の段落:

ClassB のコンストラクターから SomeFunction を呼び出す必要があると思われます。その時点で ClassA 型までの vtable のみが完成します。つまり、仮想ディスパッチ メカニズムでは、クラスはまだ ClassA 型のままです。コンストラクターが完了すると、クラス B 型のオブジェクトになります。

于 2009-04-10T17:41:59.550 に答える
0

仮想関数関数を呼び出すには、ポインターまたは参照を介して呼び出す必要があります。

void ClassA::SomeFunction()
{
    VirtualFunction();       // Call ClassA::VirtualFunction

    this->VirtualFunction(); // Call Via the virtual dispatch mechanism
                             // So in this case call ClassB::VirtualFunction
}

2 つの異なるタイプの呼び出しを区別できるようにする必要があります。そうしないと、オーバーライドされたときに classA::VirtualFunction() にアクセスできなくなります。

他の人が指摘したように、基本クラスのバージョンを抽象化したい場合は、{} ではなく = 0 を使用します

class A
{
    virtual void VirtualFunction() =0;
....

しかし、空の定義を持つことが正当な場合もあります。これは、正確な使用状況によって異なります。

于 2009-04-10T20:05:04.230 に答える