Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A non-inheritable class

0.00/5 (No votes)
30 Jun 2003 1  
A class that cannot be inherited from

Introduction

It is a common question why we shouldn�t inherit classes from STL classes. The answer of this question is simple. Big three rule [CLI95]. In other words you should have virtual destructor in a class if you want to make it a base class. However there aren�t any class in STL which define virtual destructor. Even if you try to inherit a class from STL class the compiler doesn�t complain about it, but there is a high chance of resource leak in the program. It makes programmers life easy if the compiler catches as many errors as it can during compilation. So it is nice idea to make a class in such a way that you  can�t inherit class from it, in other words a Final class.

Background

One day during a  discussion with one of my friends, who is mostly working on java, we compared the object-oriented features of Java and C++. And he introduced me to one interesting concept of java called Final class, i.e. a class from which you cant inherit any further class. It was a totally new concept for me at that time and I liked it because most of the time I wish I could make a class in C++ from which others cant inherit. He asked me to do this in C++ and I start thinking about it and doing some experimentation. Before further discussion on how we can make it, it is useful to discuss why should we need it.

Why might we need a final class?

One possible scenario, which comes to my mind that would be useful for "Final class" is a class without virtual destructor. Suppose you are making a class, which doesn�t have virtual destructor, and it may contain some dynamically allocated object. Now if any one inherits a class from it and creates the object of derived class dynamically then it will clearly create resource leak although compiler will not complain about this.

class B { 
. . .
};

class D : public B {
. . .
};

B* pB = new D;
delete pB; // resource leak

Details of implementing a final class

To make a class final the first solution, which may come in mind, is to make its constructor private. And make static functions to create the objects. But there is one problem in this approach that there may be more than one constructor in the class and you have to make all of them private. There is still a chance that anyone can make another public constructor. Quick fix of this problem is to make destructor private instead of constructor because there can be only one destructor in the class.

class FinalClass
{
private:
  ~FinalClass() { }
public:
  static FinalClass* CreateInstance()
  {
    return new FinalClass;
  }
. . .
};
The one problem with this approach is that creation of object is not possible on the stack; object must be created on heap. And now it is the responsibility of the user of this class to destroy this object. Again in this case there is a chance of resource leak if user of this class forgot to delete the object of this class from the heap when it is not needed.

Lets take a look at another approach of making a class final and still make it in such a way that a client of this class can create its object on stack and not just on the heap. We all know that when we make one class friend of other then it can create object of that class even its destructor is private. So we can create one temporary class and make its constructor private. And inherit one class from it and also make the derived class friend of it base, because if we didn�t do it then we can�t inherit the class from another class whose constructor or destructor is private.

class Temp
{
private:
  ~Temp() { };
  friend class FinalClass;
};

class FinalClass : public Temp
{
. . .
};
But when you inherit any class from FinalClass this works fine. Let�s suppose you inherit derived class from FinalClass. Now when you create the object of derived class, its constructor is called, which will call the constructor of FinalClass and FinalClass�s constructor calls the constructor of Temp because FinalClass is friend of Temp. So everything is normal and our final class is still inheritable in other words, it is not final as of now.

Before going into final class in more detail, let�s discuss the famous multiple inheritance problem, or the diamond problem. In that case you solve that problem by making virtual base class and inheriting intermediate classes virtually. But what is the purpose of virtual base class? In fact the object of most derived class (whose object is created) directly call the constructor of virtual base class. So we resolve the diamond problem, because now the most derived class (Bottom in case of diamond problem) directly calls the most base class (Top in diamond problem) constructor, and now there is only single copy of Top most class.

We can apply the same technique here. We inherit our FinalClass virtually from Temp class and make Temp as a virtual base class. Now whenever anyone attempts to inherit class from FinalClass and make object of it, then its constructor tries to call the constructor of Temp. But the constructor of Temp is private so compiler complains about this and it gives error during the compilation, because your derived class is not friend of Temp. Remember friendship is not inherited in the derived class. After all your parent's friends are not your friends and your friends are not friends of your children. But when you create object of FinalClass then its constructor can call the Temp�s constructor because FinalClass is friend of Temp. So here is final version of our final class, which can also be created on stack and not just in the heap.

class Temp
{
private:
  ~Temp() { };
  friend class FinalClass;
};

class FinalClass : virtual public Temp
{
. . .
};
But what is the case when you have to make more than one final class? You have to write double the classes i.e. if you need N final classes then you have to write 2N classes because in this method we have one temp class with private constructor and your final class. So you have to write lots of same code again and again. Why not try to take advantage of a template and try to make a class in such a way that if you inherit any class from that class your class will automatically become a Final class. The code of such a class is so simple.

template <typename T>
class MakeFinal
{
private:
  ~MakeFinal() { };
  friend T;
};
Don�t forget to virtually inherit your final class from this class to make sure this class becomes virtual base class. And pass your final class name as a template parameter so your class becomes friend of this and can call the constructor of this class.

class FinalClass : virtual public MakeFinal<FinalClass>
{
};

Conclusion

Of course nothing comes without cost. You have to pay the extra bytes to store the information about virtual base class. Most of the compiler implementations uses a pointer to store the information about the virtual base class in case of virtual inheritance. Therefore the size of the object is not just the sum of all the member variable storage allocation but it is greater than you expect. Remember that this pointer i.e. pointer to virtual base class is different than the virtual pointer, which is introduced in case of virtual function [LIP96]. This is totally an implementation issue; C++ standard doesn�t say anything about the mechanism of calling virtual function, virtual pointer and virtual table [ISO98].

Adding only one small class and changing few lines of code makes your program more reliable and reduces the resource leak problems, which could be created if you didn't use  a final class.

Reference

  1. [CLI95] 1995 C++ FAQs Marshall P Cline, Greg A Lomow
  2. [ISO98] 1998 International Standard Programming Language C++
  3. [LIP96] 1996 Inside the C++ Object Model Stanley B Lippman

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here