Working with the Final Class in C++

Environment: C++

Introduction

It is a common question, "Why shouldn't we inherit a class from STL classes?" The answer to this question is simple—the Big Three rule [CLI95]. In other words, you should have a virtual destructor in a class if you want to make it a base class. However, there isn't any class in STL that defines a virtual destructor. Even if you try to inherit a class from STL class, the compiler doesn't complain about it, although there is a high chance of a resource leak in the program. It makes a programmer's life easy if the compiler catches as many errors as it can during compilation. So, it is a nice idea to make a class in such a way that just anyone can't inherit class from it; in other words, you can create a Final class.

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

The Need for a Final Class

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

class B {
. . .
};

class D : public B {
. . .
};

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

To make the class final, the first solution that comes in mind is to make its constructor private, and make a static function 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. A quick fix to this problem is to make the destructor private instead of making a 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 the creation of an object is not possible on the stack; the object must be created on the 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 a resource leak if the user of this class forgets to delete the object of this class from the heap when it is not needed.

Let's take a look at another approach of making a class final and still make it in such a way that the client of this class can create its object on the stack, not only on the heap. We all know that when we make one class a friend of another, it can create an object of that class even its destructor is private. So, we can create one temporary class and make its constructor private. We also can inherit one class from it and make the drive class a friend of its base class because if we didn't do it, 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 the Drive class from FinalClass. Now, when you create the object of the Drive class, its constructor is called, which will call the constructor of FinalClass; FinalClass's constructor calls the constructor of Temp because FinalClass is a friend of Temp. So, everything is normal and our final class is still inheritable; in other words, it is not final until now.

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

We can apply the same technique here. We inherit our FinalClass virtually from the Temp class and make Temp a virtual base class. Now, whenever anyone attempts to inherit a class from FinalClass and make an object of it, its constructor tries to call the constructor of Temp. But the constructor of Temp is private, so the compiler complains about this and it gives an error during the compilation because your derived class is not a friend of Temp. Remember, friendship is not inherited in the drive class. After all, your parents' friends are not your friends and your friends are not friends of your children. But, when you create an object of FinalClass, its constructor can call the Temp's constructor because FinalClass is a friend of Temp. So, here is the final version of the final class, which can also be created on the stack, not only on 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; in other words, if you need N final classes, you have to write 2N classes in this method—one temp class with a private constructor and your final class. So, you have to write lots of the 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 the 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 a virtual base class. And, pass your final class name as a template parameter so your class becomes a friend of this and can call the constructor of this class.

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

Of course, nothing comes without cost. You have to pay the extra bytes to store the information about the virtual base class. Most compiler implementations use 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, this pointer, the pointer to the virtual base class, is different than the virtual pointer, which introduces in the case of a virtual function [LIP96]. This is totally an implementation issue; C++ standard doesn't say anything about the mechanism of calling a virtual function, virtual pointer, and a virtual table [ISO98].

Adding only one small class and changing a few lines of code makes your program more reliable and reduces the resource leak problems, which could be created without introducing a final class.

References

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


About the Author

Zeeshan Amjad

C++ Developer at Bechtel Corporation. zamjad.wordpress.com

Comments

  • Invalid C++.

    Posted by phreck on 01/26/2010 10:38am

    "friend T;" is simply not valid C++, sorry. I can only guess which one true compiler liberally accepts this non sanctioned code.

    • Invalid code

      Posted by Bijay on 09/26/2012 09:14am

      hi may be i wrong is this will give the compilation error , when i try to make the object of Temp *tmp;tmp= new FinalClass; delete tmp;//its giving the error.

      Reply
    Reply
  • Sorry I was wrong

    Posted by diyadas on 01/15/2008 10:26am

    I had my friend declaration as friend class T; If I change it to friend T; it works.. Wonder why former style is creating issues. I tried with MS Visual Studio.

    Reply
  • Great article...

    Posted by sachin_kl on 12/05/2007 10:46am

    In this small article you have provided some very important and useful information.
    
    Cheers,
    Sachin

    Reply
  • need private constructor as well

    Posted by Legacy on 06/27/2003 12:00am

    Originally posted by: Dan

    MakeFinal needs to look like this
    
    

    template <typename T>
    class MakeFinal
    {
    private:
    MakeFinal() {}
    ~MakeFinal() { };
    friend T;
    };


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

    class Derived : public FinalClass
    {
    };

    This prevents the use doing this:

    Derived *ptr = new Derived;

    without the private constructor, you can call new to create a derived class on the heap, but you can't delete the pointer.

    Reply
  • behavior forbidden by standard

    Posted by Legacy on 06/26/2003 12:00am

    Originally posted by: Pavel

    While this trick works for quite a lot of compilers (MSVC, BCB, Intel), it is non-standard. It is forbidden to declare template parameter as friend.
    
    

    Compilers as GCC or Comeau refuse to compile such a code.

    Legal but clumsy solution can be found on Bjarne Stroustrup pages.

    You may look on recent Usenet discussion why there's such a limitation.

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: September 17, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Another day, another end-of-support deadline. You've heard enough about the hazards of not migrating to Windows Server 2008 or 2012. What you may not know is that there's plenty in it for you and your business, like increased automation and performance, time-saving technical features, and a lower total cost of ownership. Check out this upcoming eSeminar and join Rich Holmes, Pomeroy's practice director of virtualization, as he discusses the …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds