Where do "pure virtual function call" crashes come from?
Asked Answered
H

8

129

I sometimes notice programs that crash on my computer with the error: "pure virtual function call".

How do these programs even compile when an object cannot be created of an abstract class?

Hartley answered 19/9, 2008 at 4:9 Comment(1)
Not sure why this question keeps getting tagged C++ only. This is R6025, a C runtime error. In addition to C++ many Windows v-tables are written in C and must be set up manually, regardless of whether you are using C, C++ or Java (see e.g. the DirectX V-Table for an example). If your program forgets to populate a function and sends the v-table back to the OS, you may get this.Cymose
D
127

They can result if you try to make a virtual function call from a constructor or destructor. Since you can't make a virtual function call from a constructor or destructor (the derived class object hasn't been constructed or has already been destroyed), it calls the base class version, which in the case of a pure virtual function, doesn't exist.

class Base
{
public:
    Base() { reallyDoIt(); }
    void reallyDoIt() { doIt(); } // DON'T DO THIS
    virtual void doIt() = 0;
};

class Derived : public Base
{
    void doIt() {}
};

int main(void)
{
    Derived d;  // This will cause "pure virtual function call" error
}

See also Raymond Chen's 2 articles on the subject

Darelldarelle answered 19/9, 2008 at 4:12 Comment(19)
Any reason why the compiler couldn't catch this, in general?Sprite
I don't see any technical reason why the compiler couldn't catch this.Hartley
GCC gives me a warning only: test.cpp: In constructor ‘Base::Base()’: test.cpp:4: warning: abstract virtual ‘virtual void Base::doIt()’ called from constructor But it fails at link time.Sprite
Ironically, I had this exact thing happen to me today. In a slightly more tricky fashion.Correlate
vc++ fails at link time too... >test.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall Base::doIt(void)" (?doIt@Base@@UAEXXZ)Hartley
In the general case can't catch it since the flow from the ctor can go anywhere and anywhere can call the pure virtual function. This is Halting problem 101.Brom
Answer is slightly wrong: a pure virtual function may still be defined, see Wikipedia for details. Correct phrasing: might not existShae
I think this example is too simplistic: The doIt() call in the constructor is easily devirtualised and dispatched to Base::doIt() statically, which just causes a linker error. What we really need is a situation in which the dynamic type during a dynamic dispatch is the abstract base type.Welt
This can be triggered with MSVC if you add an extra level of indirection: have Base::Base call a non-virtual f() which in turn calls the (pure) virtual doIt method.Oldline
When that abend happened to me, I tracked it down through nested destructors/deletes until I found an unimplemented destructor.Calpe
Complementing my previous comment: I actually found a double free in the destructor of a superclass, which is not pure virtual. In this case the runtime error message was misleading (MinGW compiler).Calpe
It is perfectly fine to call non-pure virtual functions from constructors. The standard explicitly allows it. Of course, it is not OK to call a pure virtual function (makes no sense).Overwhelming
I just copy-pasted the code in VS2015 and I don't get any errors not in Release nor in DebugAntisepticize
Furthermore I tried to build same code using cygwin gcc on Windows and it didn't crashed and have completely eaten the Derived d; statement.Antisepticize
@Romeno: It's Undefined Behavior to call a pure member virtual function (directly or indirectly) from the constructor or destructor of an abstract class. Both "crashes with a 'pure virtual function call' error" and "no observable effect" are valid behaviors for the compiler to generate in this scenario, precisely because the behavior is undefined by the language standard.Darelldarelle
@AdamRosenfield I'm interested how it is precisely specified, can you quote or link to the place/phrase in standard, thanks?Antisepticize
@Romeno: §10.4/6 [class.abstract] in either of the C++03 or C++11 language standards: "Member functions can be called from a constructor (or destructor) of an abstract class; the effect of making a virtual call (10.3) to a pure virtual function directly or indirectly for the object being created (or destroyed) from such a constructor (or destructor) is undefined."Darelldarelle
How would you go about calling the derived overridden method from the base constructor, then?Choli
Xcode can show these errors nowadays: looks like ⚠️ warning: call to pure virtual member function 'FUNC' has undefined behavior; overrides of 'FUNC' in subclasses are not available in the destructor of 'CLASSNAME' [-Wcall-to-pure-virtual-from-ctor-dtor]Colas
D
72

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).

Decoteau answered 19/9, 2008 at 8:48 Comment(4)
Thanks for the pointer about getting a _purecall() invocation on a deleted instance; I wasn't aware of that, but just proved it to myself with a little test code. Looking at a postmortem dump in WinDbg I thought I was dealing with a race where another thread was trying to use a derived object before it had been fully constructed, but this shines a new light on the issue, and seems to better fit the evidence.Semester
One other thing I'll add: the _purecall() invocation that normally occurs on calling a method of a deleted instance will not happen if the base class has been declared with the __declspec(novtable) optimization (Microsoft specific). With that, it's entirely possible to call an overridden virtual method after the object has been deleted, which could mask the problem until it bites you in some other form. The _purecall() trap is your friend!Semester
That's useful to know Dave, I've seen a few situations recently where I wasn't getting purecalls when I thought I should be. Perhaps I was falling foul of that optimisation.Decoteau
@LenHolgate: Extremely valuable answer. This was EXACTLY our problem case (wrong ref-count caused by race conditions). Thank you very much for pointing us in the right direction (we were suspecting v-table corruption instead and going crazy trying to find the culprit code)Planksheer
J
11

I ran into the scenario that the pure virtual functions gets called because of destroyed objects, Len Holgate already have a very nice answer, I would like to add some color with an example:

  1. A Derived object is created, and the pointer (as Base class) is saved somewhere
  2. The Derived object is deleted, but somehow the pointer is still referenced
  3. The pointer which points to deleted Derived object gets called

The Derived class destructor reset the vptr points to the Base class vtable, which has the pure virtual function, so when we call the virtual function, it actually calls into the pure virutal ones.

This could happen because of an obvious code bug, or a complicated scenario of race condition in multi-threading environments.

Here is an simple example (g++ compile with optimization turned off - a simple program could be easily optimized away):

 #include <iostream>
 using namespace std;

 char pool[256];

 struct Base
 {
     virtual void foo() = 0;
     virtual ~Base(){};
 };

 struct Derived: public Base
 {
     virtual void foo() override { cout <<"Derived::foo()" << endl;}
 };

 int main()
 {
     auto* pd = new (pool) Derived();
     Base* pb = pd;
     pd->~Derived();
     pb->foo();
 }

And the stack trace looks like:

#0  0x00007ffff7499428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff749b02a in __GI_abort () at abort.c:89
#2  0x00007ffff7ad78f7 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7adda46 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7adda81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7ade84f in __cxa_pure_virtual () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x0000000000400f82 in main () at purev.C:22

Highlight:

if the object is fully deleted, meaning destructor gets called, and memory gets reclaimed, we may simply get a Segmentation fault as the memory has returned to the operating system, and the program just can't access it. So this "pure virtual function call" scenario usually happens when the object is allocated on the memory pool, while an object is deleted, the underlying memory is actually not reclaimed by OS, it is still there accessible by the process.

Justajustemilieu answered 9/5, 2019 at 14:24 Comment(1)
Concerning the concluding section: There may be real-world examples without placement new where the runtime library decouples memory management from the OS for performance reasons. But thanks anyway for the walkthrough.Coconut
P
7

Usually when you call a virtual function through a dangling pointer--most likely the instance has already been destroyed.

There can be more "creative" reasons, too: maybe you've managed to slice off the part of your object where the virtual function was implemented. But usually it's just that the instance has already been destroyed.

Postpone answered 19/9, 2008 at 4:15 Comment(0)
B
1

If you use Borland/CodeGear/Embarcadero/Idera C++ Builder, your can just implement

extern "C" void _RTLENTRY _pure_error_()
{
    //_ErrorExit("Pure virtual function called");
    throw Exception("Pure virtual function called");
}

While debugging place a breakpoint in the code and see the callstack in the IDE, otherwise log the call stack in your exception handler (or that function) if you have the appropriate tools for that. I personally use MadExcept for that.

PS. The original function call is in [C++ Builder]\source\cpprtl\Source\misc\pureerr.cpp

Bloated answered 22/3, 2018 at 14:52 Comment(0)
J
0

I'd guess there is a vtbl created for the abstract class for some internal reason (it might be needed for some sort of run time type info) and something goes wrong and a real object gets it. It's a bug. That alone should say that something that can't happen is.

Pure speculation

edit: looks like I'm wrong in the case in question. OTOH IIRC some languages do allow vtbl calls out of the constructor destructor.

Julius answered 19/9, 2008 at 4:11 Comment(3)
It is not a bug in the compiler, if that is what you mean.Sprite
Your suspicion is right - C# and Java allow this. In those languages, bohjects under construction do have their final type. In C++, objects change type during construction and that's why and when you can have objects with an abstract type.Shae
ALL abstract classes, and real objects created derived from them, need a vtbl (virtual function table), listing which virtual functions should be called on it. In C++ an object is responsible for creating its own members, including the virtual function table. Constructors are called from base class to derived, and destructors are called from derived to base class, so in an abstract base class the virtual function table is not yet available.Peplos
L
0

I use VS2010 and whenever I try calling destructor directly from public method, I get a "pure virtual function call" error during runtime.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void SomeMethod1() { this->~Foo(); }; /* ERROR */
};

So I moved what's inside ~Foo() to separate private method, then it worked like a charm.

template <typename T>
class Foo {
public:
  Foo<T>() {};
  ~Foo<T>() {};

public:
  void _MethodThatDestructs() {};
  void SomeMethod1() { this->_MethodThatDestructs(); }; /* OK */
};
Liveried answered 16/11, 2012 at 5:8 Comment(0)
C
-2

Here is a sneaky way for it to happen. I had this essentially happen to me today.

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();
Correlate answered 19/9, 2008 at 4:22 Comment(3)
At least it can't be reproduced on my vc2008,the vptr does point to A's vtable when first initialized in A's contructor, but then when B is fully initialized, the vptr is changed to point to B's vtable, which is okJustajustemilieu
coudnt reproduce it either with vs2010/12Archuleta
I had this essentially happen to me today obviously not true, because simply wrong: a pure virtual function is called only when callFoo() is called within a constructor (or destructor), because at this time the object is still (or already) at A stage. Here is a running version of your code without the syntax error in B b(); - the parentheses make it a function declaration, you want an object.Coconut

© 2022 - 2024 — McMap. All rights reserved.