Re: pure virtual functions and runtime id
cerenoc wrote:
But isn't this the whole point of polymorphism?
The problem you presented has nothing to do with polymorphism, but
with object scope. Your derived objects are destroyed before the last
var->whoami() call. By all intents and purposes 'var' is not pointing to
an object of the derived class type anymore, because it has been
destroyed (because it went out of scope). That's why you are getting the
error.
As it happens, the object still physically exists because it's a
static instance, but most C++ compilers work in a way that when an
object of the derived type is destroyed, any virtual functions accessed
through a base class type pointer/reference will be purely of the base
class type, even if they point to this destroyed static object of the
derived type. This means that your pointer is not seeing a derived class
object anymore, only a base class object. That's why you are getting an
error message instead of a crash.
This behavior sometimes surprises even pros. For example, assume you
have a base class like this:
class Base
{
public:
~Base() { cleanup(); }
virtual void cleanup() { std::cout << "Base cleanup\n"; }
};
The idea with this would be that any derived class can implement the
'cleanup()' function, which will (in theory) be automatically called
when the object is destroyed. So you could do this:
class Derived: public Base
{
public:
virtual void cleanup() { std::cout << "Derived cleanup\n"; }
};
Now, assume that you have somewhere dynamically allocated instances of
different derived classes, for example like this:
std::vector<Base*> instances;
Then somewhere you destroy all the instances, like:
for(size_t i = 0; i < instances.size(); ++i)
delete instances[i];
instances.clear();
Now, it may become as a surprise that this will never print "Derived
cleanup", but will always print "Base cleanup" for all those objects,
even if the objects behind those pointers are of type Derived. The
'cleanup()' implementation of the derived objects is never called. This
attempt at a clever automatic cleaning up process failed.
The reason for this is that when the code reaches the 'cleanup()' call
in the base class destructor, the derived class part has already been
destroyed and by all intents and purposes doesn't exist anymore. From
the point of view of the base class destructor the object is purely of
type 'Base', nothing else. Because of this, the dynamic binding logic
calls the base class 'cleanup()' because that's what this object is at
this point. There simply is no way to call a derived class function from
a base class destructor.
Yes, it's a bummer, but it has a relatively simple solution:
class Derived: public Base
{
public:
~Derived() { cleanup(); }
virtual void cleanup() { std::cout << "Derived cleanup\n"; }
};
A bit less automatic, but that's just a side-effect of OOP.