The Artima Developer Community
Sponsored Link

Articles Forum
"Pure Virtual Function Called": An Explanation

12 replies on 1 page. Most recent reply: Feb 26, 2007 9:00 AM by Bill Venners

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 12 replies on 1 page
Bill Venners

Posts: 2284
Nickname: bv
Registered: Jan, 2002

"Pure Virtual Function Called": An Explanation Posted: Feb 26, 2007 9:00 AM
Reply to this message Reply
Advertisement
"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


Paul Chisholm

Posts: 5
Nickname: psrc
Registered: Mar, 2007

Example program 1 for "Pure Virtual Function Called: An Explanation" Posted: Mar 2, 2007 8:24 PM
Reply to this message Reply

// 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!
}

Paul Chisholm

Posts: 5
Nickname: psrc
Registered: Mar, 2007

Example program 2 for "Pure Virtual Function Called: An Explanation" Posted: Mar 2, 2007 8:26 PM
Reply to this message Reply

// 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!
}

Paul Chisholm

Posts: 5
Nickname: psrc
Registered: Mar, 2007

Example program 3 for "Pure Virtual Function Called: An Explanation" Posted: Mar 2, 2007 8:27 PM
Reply to this message Reply

// 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!
}

Paul Chisholm

Posts: 5
Nickname: psrc
Registered: Mar, 2007

Example program 4 for "Pure Virtual Function Called: An Explanation" Posted: Mar 2, 2007 8:28 PM
Reply to this message Reply

// 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!
}

Paul Chisholm

Posts: 5
Nickname: psrc
Registered: Mar, 2007

Example program 5 for "Pure Virtual Function Called: An Explanation" Posted: Mar 2, 2007 8:29 PM
Reply to this message Reply

// 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!
}

Andrei Korostelev

Posts: 5
Nickname: andreika
Registered: Aug, 2006

Re: Pure Virtual Function Called: An Explanation Posted: Mar 3, 2007 7:54 AM
Reply to this message Reply
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.

Bill T

Posts: 3
Nickname: billt
Registered: Jun, 2007

Re: Pure Virtual Function Called: An Explanation Posted: Jun 8, 2007 6:37 AM
Reply to this message Reply
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.

Rob Stewart

Posts: 5
Nickname: robstewart
Registered: Aug, 2005

Re: Pure Virtual Function Called: An Explanation Posted: Feb 19, 2008 9:24 AM
Reply to this message Reply
> 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.

Rob Stewart

Posts: 5
Nickname: robstewart
Registered: Aug, 2005

Re: Pure Virtual Function Called: An Explanation Posted: Feb 19, 2008 9:46 AM
Reply to this message Reply
> 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

Bill T

Posts: 3
Nickname: billt
Registered: Jun, 2007

Re: Pure Virtual Function Called: An Explanation Posted: Mar 25, 2008 12:47 PM
Reply to this message Reply
>>>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."

Bill T

Posts: 3
Nickname: billt
Registered: Jun, 2007

Re: Pure Virtual Function Called: An Explanation Posted: Mar 25, 2008 12:57 PM
Reply to this message Reply
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.

Scott Colcord

Posts: 1
Nickname: sacolcor
Registered: Oct, 2011

Re: Pure Virtual Function Called: An Explanation Posted: Oct 5, 2011 6:56 AM
Reply to this message Reply
<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?

Flat View: This topic has 12 replies on 1 page
Topic: Cooperative Visitor: A Template Technique for Visitor Creation Previous Topic   Next Topic Topic: Scala's Stackable Trait Pattern

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use