Sunday, September 11, 2011

pure virtual method called


bandken@six6six:~$ ./a.out
pure virtual method called
terminate called without an active exception
Aborted
bandken@six6six:~$


Someone had asked me to explain what this meant. Simply, it means that you called a pure virtual function without having the method defined. With most compilers (at least all the ones that I've used), a dummy function is inserted into the virtual function table entry, and it remains there until a concrete class is constructed and the pure virtual function replaced with the correct function. GCC won't even let you call a pure virtual function in the abstract class's constructor, as this example shows:


class Base
{
public:
Base()
{
Oopsie(); // ERROR: warning: abstract
// virtual ‘virtual void
// Base::Oopsie()’
// called from constructor
}

virtual void Oopsie() = 0;
};

class Derived : public Base
{
public:
virtual void Oopsie() {};
};

int main()
{
Derived derived;
return 0;
}



We're lucky that we actually get an error. There's no reason why the compiler needs to tell us about this. It doesn't take much tweaking to get past this.


class Base
{
public:
Base()
{
init();
}

void init()
{
Oopsie();
}

virtual void Oopsie() = 0;
};

class Derived : public Base
{
public:
virtual void Oopsie() {};
};

int main()
{
Derived derived;
return 0;
}



The constructor order is Base, then Derived. In the constructor for Base, we call init(), which calls Oopsie(). Since Derived hasn't been instantiated yet, we call the pure virtual method and our program crashes.

We're not limited to having this problem only in the constructor. Here you can see the problem happening in the destructor. The destructor order is Derived first, then Base (opposite order of the constructors). By the time the done() method is called, the Derived destructor has already been called, and the derived Oopsie() call no longer exists.


class Base
{
public:
~Base()
{
done();
}

void done()
{
Oopsie();
}

virtual void Oopsie() = 0;
};

class Derived : public Base
{
public:
virtual void Oopsie() {};
};

int main()
{
Derived derived;
return 0;
}



Typically, a good rule of thumb is to be very careful when putting any virtual functions into constructors and destructors. You might even know what you are doing. Maybe. Sure, calling that virtual function from the abstract base class constructor is fine, because....uh, I can't think of ANY examples why this is a good idea. If you really wanted some functionality the was to be provided in a function that happened to be virtual, please do something like this:


class Base
{
public:
Base()
{
init();
}

void init()
{
// ..
// ..
// ..
functionalityThatICannotLiveWithout();
}

void functionalityThatICannotLiveWithout()
{
// ..
}

virtual void pureVirtualMethod()
{
functionalityThatICannotLiveWithout();
}
};

class Derived : public Base
{
public:
virtual void pureVirtualMethod() {};
};

int main()
{
Derived derived;
return 0;
}

No comments:

Post a Comment