Wednesday, July 05, 2006

"What's so pure about Pure Virtual Functions in C++?!"

I was asked this by someone.

Scene is somewhat like this. I'm explaining some class hierarchy and while doing so I explain
"....can be a abstract base class. It has virtual functions that define the interface and one of the functions as pure virtual. Even the pure virtual function can have implementation in the base class...."

At this point, the person listening to me shows surprise and asks, " Can a pure virtual function have implementation in the base class.. if so what is so pure about it??! ". And then, goes on and says, " no you are wrong, it can't have implementation".

Later, I checked Lippman et.al., which is the book from which I learnt C++ initially, and there is very little about pure virtual functions(p 926 - 927). There is nothing about providing a implementation to a pure virtual function. Infact he only says,
"..The language provides us with a syntactic construct by which to signify that a virtual function is providing a interface for subsequent subtypes to override but not itself be invoked through virtual mechanism: a pure virtual function. ..."


Then he explains further to say that a object of such a class can't be created('cuz you would end up invoking that function directly from that object).

The things that got missed out of this explanation and many people don't know is this:
even though you are restricted from invoking a pure virtual function from an object of that class, and hence, you are restricted from creating an object of that class, you can invoke a pure virtual function from within; ie., from its constructor, destructor, or any other function and from functions within the derived types!! Now, armed with this knowledge, think about the original question. Does the body for a pure virtual function make sense? Answer is yes, depends!.

To dwell deeper, here is a piece of code.


// Filename PureVirtualDemo.cpp
// Demo code to demostrate pure virtual functions having definition.

// A typical use for this feature would be to provide default implementation or
// the common part that will be used by all concrete instances

#include <iostream>
using namespace std;

class AbstractType
{
public:
virtual void test() = 0;
};

void AbstractType::test()
{
// Put the {common stuff}/{minimal thing} here.

// This may be doing the minimal thing or it may
// be doing the common stuff. Depends.
cout << " Inside AbstractType::test() " << endl;
}

class ConcreteType : public AbstractType
{
public:
void test()
{
cout << " Inside ConcreteType::test() " << endl;
// Call the common part
AbstractType::test();
// Now do the specific stuff if you have any.
// Or if its the minimal thing that you have, you are done.
}
};

int main()
{
AbstractType *obj = new ConcreteType;
obj->test();
return 0;
}
/*
Output after compiling in g++ and running on a GNU/Linux machine.

[vinay@technicolor_sounds ~]$ g++ PureVirtualDemo.cpp -o pvd
[vinay@technicolor_sounds ~]$ ./pvd
Inside ConcreteType::test()
Inside AbstractType::test()

*/
// vim: ts=4:sw=4:expandtab:ai:cindent


So, this code clearly proves the point. And sometimes, pure virtual
functions can get called accidentally from within the object and it becomes a very hard bug to find. So, even if you don't have requirements as described above, its best to provide body for pure virtual functions with proper comments and doing an assert(false) inside it would gets the debugger's attention in such case.

Hope you have enjoyed this piece!:-)

No comments:

Post a Comment