「純粋な仮想関数呼び出し」というエラーでコンピューターがクラッシュするプログラムに気付くことがあります。
抽象クラスからオブジェクトを作成できない場合、これらのプログラムはどのようにコンパイルされるのでしょうか?
「純粋な仮想関数呼び出し」というエラーでコンピューターがクラッシュするプログラムに気付くことがあります。
抽象クラスからオブジェクトを作成できない場合、これらのプログラムはどのようにコンパイルされるのでしょうか?
コンストラクターまたはデストラクターから仮想関数呼び出しを実行しようとすると、これらが発生する可能性があります。コンストラクターまたはデストラクターから仮想関数呼び出しを行うことはできないため (派生クラス オブジェクトが構築されていないか、既に破棄されている)、基底クラス バージョンを呼び出しますが、純粋仮想関数の場合は、存在しません。
(ライブデモはこちら)
class Base
{
public:
Base() { doIt(); } // DON'T DO THIS
virtual void doIt() = 0;
};
void Base::doIt()
{
std::cout<<"Is it fine to call pure virtual function from constructor?";
}
class Derived : public Base
{
void doIt() {}
};
int main(void)
{
Derived d; // This will cause "pure virtual function call" error
}
As well as the standard case of calling a virtual function from the constructor or destructor of an object with pure virtual functions you can also get a pure virtual function call (on MSVC at least) if you call a virtual function after the object has been destroyed. Obviously this is a pretty bad thing to try and do but if you're working with abstract classes as interfaces and you mess up then it's something that you might see. It's possibly more likely if you're using referenced counted interfaces and you have a ref count bug or if you have an object use/object destruction race condition in a multi-threaded program... The thing about these kinds of purecall is that it's often less easy to fathom out what's going on as a check for the 'usual suspects' of virtual calls in ctor and dtor will come up clean.
To help with debugging these kinds of problems you can, in various versions of MSVC, replace the runtime library's purecall handler. You do this by providing your own function with this signature:
int __cdecl _purecall(void)
and linking it before you link the runtime library. This gives YOU control of what happens when a purecall is detected. Once you have control you can do something more useful than the standard handler. I have a handler that can provide a stack trace of where the purecall happened; see here: http://www.lenholgate.com/blog/2006/01/purecall.html for more details.
(Note you can also call _set_purecall_handler() to install your handler in some versions of MSVC).
通常、ダングリング ポインターを介して仮想関数を呼び出す場合、インスタンスは既に破棄されている可能性が高くなります。
より「創造的な」理由もある可能性があります。おそらく、仮想関数が実装されたオブジェクトの一部を切り取ることができたのでしょう。ただし、通常は、インスタンスが既に破棄されているだけです。
内部的な理由で抽象クラス用に作成された vtbl があると思います (ある種の実行時の型情報に必要な場合があります)。何かがうまくいかず、実際のオブジェクトがそれを取得します。バグです。それだけでは、あり得ないことがあると言うべきです。
純粋な憶測
編集:問題のケースでは間違っているようです。OTOH IIRC の一部の言語では、コンストラクター デストラクタからの vtbl 呼び出しが許可されています。
私は VS2010 を使用していますが、パブリック メソッドからデストラクタを直接呼び出そうとすると、実行時に「純粋仮想関数呼び出し」エラーが発生します。
template <typename T>
class Foo {
public:
Foo<T>() {};
~Foo<T>() {};
public:
void SomeMethod1() { this->~Foo(); }; /* ERROR */
};
だから私は ~Foo() の中にあるものを別のプライベートメソッドに移動しました、そしてそれは魅力のように働きました。
template <typename T>
class Foo {
public:
Foo<T>() {};
~Foo<T>() {};
public:
void _MethodThatDestructs() {};
void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};
これは、それが起こるための卑劣な方法です。今日、これが本質的に私に起こりました。
class A
{
A *pThis;
public:
A()
: pThis(this)
{
}
void callFoo()
{
pThis->foo(); // call through the pThis ptr which was initialized in the constructor
}
virtual void foo() = 0;
};
class B : public A
{
public:
virtual void foo()
{
}
};
B b();
b.callFoo();