Summary:
"Pure virtual function called" is the dying message of the occasional crashed C++ program. What does it mean? You can find a couple of simple, well-documented explanations out there that apply to problems easy to diagnose during postmortem debugging. There is also another rather subtle bug that generates the same message. If you have a mysterious crash associated with that message, it might well mean your program went indirect on a dangling pointer. This article covers all these explanations.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: October 5, 2011 5:56 AM by
Scott
|
"Pure virtual function called" is the dying message of the occasional crashed C++ program. What does it mean? You can find a couple of simple, well-documented explanations out there that apply to problems easy to diagnose during postmortem debugging. There is also another rather subtle bug that generates the same message. If you have a mysterious crash associated with that message, it might well mean your program went indirect on a dangling pointer. This article covers all these explanations: http://www.artima.com/cppsource/pure_virtual.html
|
|
|
// Artima.com: The C++ Source: "Pure virtual function called", // example 1, virtual function directly called in constructor.
#include <cstdlib> #include <iostream>
class AbstractShape { public: virtual double area() const = 0; double value() const; // Meyers 3rd Item 7: virtual ~AbstractShape(); protected: AbstractShape(double valuePerSquareUnit); private: double valuePerSquareUnit_; };
class Rectangle : public AbstractShape { public: Rectangle(double width, double height, double valuePerSquareUnit); virtual double area() const; // Meyers 3rd Item 7: virtual ~Rectangle(); private: double width_; double height_; };
AbstractShape::AbstractShape(double valuePerSquareUnit) : valuePerSquareUnit_(valuePerSquareUnit) { // ERROR: Violation of Meyers 3rd Item 9! std::cout << "creating shape, area = " << area() << std::endl; }
AbstractShape::~AbstractShape() { }
#ifdef DEFINE_PURE_VIRTUAL_FUNCTION
// Some compilers optimize away the vtbl, and try to directly call the base // class version of the virtual function. If the function is defined, the // program links and runs; if not, it doesn't link.
double AbstractShape::area() const { return 0; }
#endif /* DEFINE_PURE_VIRTUAL_FUNCTION */
double AbstractShape::value() const { return valuePerSquareUnit_ * area(); }
Rectangle::Rectangle(double width, double height, double valuePerSquareUnit) : AbstractShape(valuePerSquareUnit), width_(width), height_(height) { }
Rectangle::~Rectangle() { }
double Rectangle::area() const { return width_ * height_; }
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " width height valuePerSquareUnit" << std::endl; return 1; // Failure! } double width = std::atof(argv[1]); double height = std::atof(argv[2]); double valuePerSquareUnit = std::atof(argv[3]);
Rectangle r(width, height, valuePerSquareUnit); std::cout << "value = " << r.value() << std::endl;
return 0; // Success! }
|
|
|
// Artima.com: The C++ Source: "Pure virtual function called", // example 2, virtual function directly called in destructor.
#include <cstdlib> #include <iostream>
class AbstractShape { public: virtual double area() const = 0; double value() const; // Meyers 3rd Item 7: virtual ~AbstractShape(); protected: AbstractShape(double valuePerSquareUnit); private: double valuePerSquareUnit_; };
class Rectangle : public AbstractShape { public: Rectangle(double width, double height, double valuePerSquareUnit); virtual double area() const; // Meyers 3rd Item 7: virtual ~Rectangle(); private: double width_; double height_; };
AbstractShape::AbstractShape(double valuePerSquareUnit) : valuePerSquareUnit_(valuePerSquareUnit) { }
AbstractShape::~AbstractShape() { // ERROR: Violation of Meyers 3rd Item 9! std::cout << "destroying shape, area = " << area() << std::endl; }
#ifdef DEFINE_PURE_VIRTUAL_FUNCTION
// Some compilers optimize away the vtbl, and try to directly call the base // class version of the virtual function. If the function is defined, the // program links and runs; if not, it doesn't link.
double AbstractShape::area() const { return 0; }
#endif /* DEFINE_PURE_VIRTUAL_FUNCTION */
double AbstractShape::value() const { return valuePerSquareUnit_ * area(); }
Rectangle::Rectangle(double width, double height, double valuePerSquareUnit) : AbstractShape(valuePerSquareUnit), width_(width), height_(height) { }
Rectangle::~Rectangle() { }
double Rectangle::area() const { return width_ * height_; }
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " width height valuePerSquareUnit" << std::endl; return 1; // Failure! } double width = std::atof(argv[1]); double height = std::atof(argv[2]); double valuePerSquareUnit = std::atof(argv[3]);
Rectangle r(width, height, valuePerSquareUnit); std::cout << "value = " << r.value() << std::endl;
return 0; // Success! }
|
|
|
// Artima.com: The C++ Source: "Pure virtual function called", // example 3, virtual function indirectly called in constructor.
#include <cstdlib> #include <iostream>
class AbstractShape { public: virtual double area() const = 0; double value() const; // Meyers 3rd Item 7: virtual ~AbstractShape(); protected: AbstractShape(double valuePerSquareUnit); private: double valuePerSquareUnit_; };
class Rectangle : public AbstractShape { public: Rectangle(double width, double height, double valuePerSquareUnit); virtual double area() const; // Meyers 3rd Item 7: virtual ~Rectangle(); private: double width_; double height_; };
AbstractShape::AbstractShape(double valuePerSquareUnit) : valuePerSquareUnit_(valuePerSquareUnit) { // ERROR: Indirect violation of Meyers 3rd Item 9! std::cout << "creating shape, value = " << value() << std::endl; }
AbstractShape::~AbstractShape() { }
#ifdef DEFINE_PURE_VIRTUAL_FUNCTION
// Some compilers optimize away the vtbl, and try to directly call the base // class version of the virtual function. If the function is defined, the // program links and runs; if not, it doesn't link.
double AbstractShape::area() const { return 0; }
#endif /* DEFINE_PURE_VIRTUAL_FUNCTION */
double AbstractShape::value() const { return valuePerSquareUnit_ * area(); }
Rectangle::Rectangle(double width, double height, double valuePerSquareUnit) : AbstractShape(valuePerSquareUnit), width_(width), height_(height) { }
Rectangle::~Rectangle() { }
double Rectangle::area() const { return width_ * height_; }
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " width height valuePerSquareUnit" << std::endl; return 1; // Failure! } double width = std::atof(argv[1]); double height = std::atof(argv[2]); double valuePerSquareUnit = std::atof(argv[3]);
Rectangle r(width, height, valuePerSquareUnit); std::cout << "value = " << r.value() << std::endl;
return 0; // Success! }
|
|
|
// Artima.com: The C++ Source: "Pure virtual function called", // example 4, virtual function indirectly called in destructor.
#include <cstdlib> #include <iostream>
class AbstractShape { public: virtual double area() const = 0; double value() const; // Meyers 3rd Item 7: virtual ~AbstractShape(); protected: AbstractShape(double valuePerSquareUnit); private: double valuePerSquareUnit_; };
class Rectangle : public AbstractShape { public: Rectangle(double width, double height, double valuePerSquareUnit); virtual double area() const; // Meyers 3rd Item 7: virtual ~Rectangle(); private: double width_; double height_; };
AbstractShape::AbstractShape(double valuePerSquareUnit) : valuePerSquareUnit_(valuePerSquareUnit) { }
AbstractShape::~AbstractShape() { // ERROR: Indirect violation of Meyers 3rd Item 9! std::cout << "destroying shape, value = " << value() << std::endl; }
#ifdef DEFINE_PURE_VIRTUAL_FUNCTION
// Some compilers optimize away the vtbl, and try to directly call the base // class version of the virtual function. If the function is defined, the // program links and runs; if not, it doesn't link.
double AbstractShape::area() const { return 0; }
#endif /* DEFINE_PURE_VIRTUAL_FUNCTION */
double AbstractShape::value() const { return valuePerSquareUnit_ * area(); }
Rectangle::Rectangle(double width, double height, double valuePerSquareUnit) : AbstractShape(valuePerSquareUnit), width_(width), height_(height) { }
Rectangle::~Rectangle() { }
double Rectangle::area() const { return width_ * height_; }
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " width height valuePerSquareUnit" << std::endl; return 1; // Failure! } double width = std::atof(argv[1]); double height = std::atof(argv[2]); double valuePerSquareUnit = std::atof(argv[3]);
Rectangle r(width, height, valuePerSquareUnit); std::cout << "value = " << r.value() << std::endl;
return 0; // Success! }
|
|
|
// Artima.com: The C++ Source: "Pure virtual function called", // example 5, going indirect on a dangling pointer.
#include <cstdlib> #include <iostream>
class AbstractShape { public: virtual double area() const = 0; double value() const; // Meyers 3rd Item 7: virtual ~AbstractShape(); protected: AbstractShape(double valuePerSquareUnit); private: double valuePerSquareUnit_; };
class Rectangle : public AbstractShape { public: Rectangle(double width, double height, double valuePerSquareUnit); virtual double area() const; // Meyers 3rd Item 7: virtual ~Rectangle(); private: double width_; double height_; };
AbstractShape::AbstractShape(double valuePerSquareUnit) : valuePerSquareUnit_(valuePerSquareUnit) { }
AbstractShape::~AbstractShape() { }
double AbstractShape::value() const { return valuePerSquareUnit_ * area(); }
Rectangle::Rectangle(double width, double height, double valuePerSquareUnit) : AbstractShape(valuePerSquareUnit), width_(width), height_(height) { }
Rectangle::~Rectangle() { }
double Rectangle::area() const { return width_ * height_; }
int main(int argc, char** argv) { if (argc != 4) { std::cerr << "usage: " << argv[0] << " width height valuePerSquareUnit" << std::endl; return 1; // Failure! } double width = std::atof(argv[1]); double height = std::atof(argv[2]); double valuePerSquareUnit = std::atof(argv[3]);
AbstractShape* p1 = new Rectangle(width, height, valuePerSquareUnit); std::cout << "value = " << p1->value() << std::endl; AbstractShape* p2 = p1; // Need another copy of the pointer. delete p1; std::cout << "now value = " << p2->value() << std::endl;
return 0; // Success! }
|
|
|
WRT calling PVF from the constructor/destructor. The Standard says:
10.4/2 [class.abstract] A pure virtual function need be defined only if explicitly called with the qualified-id syntax.
10.4/6 [class.abstract] 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.
12.7/3 Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor’s own class or in one of its bases, but not a function overriding it in a class derived from the constructor or destructor’s class, or overriding it in one of the other base classes of the most derived object.
Thus, we can say that a non-qualified PVF call from constructor (both explicit and implicit) is undefined behavior. Qualified PVF call from constructor leads to generating a code for the static call of this function. The fact that MSVC resolves the non-qualified call to a static call is just a special case of UB.
|
|
|
There's another scenario where this can happen that is common in multi-threaded, event-driven code:
Assume an abstract class that defines an interface (call it A), a base class (call it B) that derives from A, and that can be derived from, and a derived class (call it D) that represents what a user would write, and which derives from B.
Assume that B does not define the pure virtual method declared in A -- for instance, to force the derived class to implement it.
The pure virtual method is a "callback" method -- i.e., it is invoked in response to external events, and on a different thread.
If another thread invokes D's dtor while a callback is in progress, some "funny" things can happen:
* D's dtor executes -- the object is still a "D" at this point.
* At the end of D's dtor (but before ~D returns) B's dtor executes, since it is the next-most derived class. At this point the compiler replaces the vtable ptr in the object with B's vtable ptr, so the object is now a "B", not a "D" anymore. Remember that B chose not to define the pure virtual method, in order to force it to be defined in D.
* Another thread invokes the callback function on the object in response to an external event. At this point you get a "pure virtual error", because B's vtable doesn't have an entry for the callback method -- B's dtor effectively changed the object from a "D" to a "B".
One possible solution to this problem is to use a lock to control access to the dtor and callback function. This doesn't work, however, unless the lock is acquired in the most-derived class ("D") -- by the time the object morphs into a "B" (at entry to B's dtor) it is already too late.
If the code in question is a library (our case), it may not be reasonable to expect users of the convenience class to have to remember to lock the object in their dtor.
For this reason, it seemed to us that the only reasonable solution was to not define the callback method as pure virtual, but as a "plain old" virtual method, with a default (no-op) implementation. When the callback function is invoked in response to the external event, at least there is a vtable entry for the compiler to call. This assumes, of course, that the callback method can be skipped -- not an unreasonable assumption for an object that is halfway deleted already anyway.
|
|
|
> There's another scenario where this can happen that is > common in multi-threaded, event-driven code: [snip] > If another thread invokes D's dtor while a callback is in > progress, some "funny" things can happen: [snip] > If the code in question is a library (our case), it may > not be reasonable to expect users of the convenience class > to have to remember to lock the object in their dtor. > > For this reason, it seemed to us that the only reasonable > solution was to not define the callback method as pure > virtual, but as a "plain old" virtual method, with a > default (no-op) implementation. When the callback function > is invoked in response to the external event, at least > there is a vtable entry for the compiler to call. This > assumes, of course, that the callback method can be > skipped -- not an unreasonable assumption for an object > that is halfway deleted already anyway.
The problem is that multiple threads are using a shared object without synchronizing access. D's destructor has nothing to do with it. Since the object exists in a queue to be used when an event occurs and in some other context where it can be deleted, those two contexts must be synchronized. A simple means to that end is reference counting.
|
|
|
> WRT calling PVF from the constructor/destructor. The > Standard says: > > 10.4/2 [class.abstract] > 10.4/6 [class.abstract] > 12.7/3 > > Thus, we can say that a non-qualified PVF call from > constructor (both explicit and implicit) is undefined > behavior. Qualified PVF call from constructor leads to > generating a code for the static call of this function. > The fact that MSVC resolves the non-qualified call to a > static call is just a special case of UB. Issue 230 seeks, quite reasonably, to resolve that, but it remains open to date: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#230
|
|
|
>>>The problem is that multiple threads are using a shared object without synchronizing access.
You're right, but that's on purpose: "One possible solution to this problem is to use a lock to control access to the dtor and callback function. This doesn't work, however, unless the lock is acquired in the most-derived class ("D") -- by the time the object morphs into a "B" (at entry to B's dtor) it is already too late.
If the code in question is a library (our case), it may not be reasonable to expect users of the convenience class to have to remember to lock the object in their dtor."
|
|
|
10.4/6 states: "Member functions can be called from a ... 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 ... destroyed from such a ... destructor is undefined." (emphasis mine).
However, in B's dtor, the virtual function is not pure -- that's the whole point of defining a no-op implementation.
12.7/3 states: "When a virtual function is called directly or indirectly from ... a destructor, and the object to which the call applies is the object under ... destruction, the function called is the one defined in the ... destructor’s own class or in one of its bases" BTW, the behavior of the described solution (do not define the callback method as pure virtual, but as a "plain old" virtual method, with a default (no-op) implementation) is observed to work correctly under MSVC and g++ (3.4.6).
Based on my reading of the spec, this is in fact defined behavior.
|
|
|
< http://www.artima.com/cppsource/pure_virtual.html>:> Compilers are allowed to "zero out" (i.e., render unusable) > pointers after destructing their pointed-to data. I couldn't find this in the standard, can you provide a ref?
|
|