By ring0


2010-02-02 12:57:36 8 Comments

Recently one of my friend asked me how to prevent class inheritance in C++. He wanted the compilation to fail.

I was thinking about it and found 3 answers. Not sure which is the best one.

1) Private Constructor(s)

class CBase
{

public:

 static CBase* CreateInstance() 
 { 
  CBase* b1 = new CBase();
  return b1;
 }

private:

 CBase() { }
 CBase(CBase3) { }
 CBase& operator=(CBase&) { }


};

2) Using CSealed base class, private ctor & virtual inheritance

class CSealed
{

private:

 CSealed() {
 }

 friend class CBase;
};


class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

3) Using a CSealed base class, protected ctor & virtual inheritance

class CSealed
{

protected:

 CSealed() {
 }

};

class CBase : virtual CSealed
{

public:

 CBase() {
 }

};

All the above methods make sure that CBase class cannot be inherited further. My Question is:

1) Which is the best method ? Any other methods available ?

2) Method 2 & 3 will not work unless the CSealed class is inherited virutally. Why is that ? Does it have anything to do with vdisp ptr ??

PS:

The above program was compiled in MS C++ compiler (Visual Studio). reference : http://www.codeguru.com/forum/archive/index.php/t-321146.html

9 comments

@anon 2010-02-02 13:46:52

You can't prevent inheritance (before C++11's final keyword) - you can only prevent instantiation of inherited classes. In other words, there is no way of preventing:

class A { ... };

class B : public A { ... };

The best you can do is prevent objects of type B from being instantiated. That being the case, I suggest you take kts's advice and document the fact that A (or whatever) is not intended to be used for inheritance, give it a non-virtual destructor, and no other virtual functions, and leave it at that.

@Binary Worrier 2010-02-02 13:54:20

+1: You can't stop someone choosing to use inheritance over composotion, even though we (and the rest of the universe) might disagree. Document it

@Nemanja Boric 2014-04-12 09:20:54

Note that in C++11 you can easily prevent inheritance.

@Blazej Czapp 2016-05-10 14:36:48

To elaborate on Francis' answer: if class Bottom derives from class Middle, which virtually inherits from class Top, it is that most derived class (Bottom) that is responsible for constructing the virtually inherited base class (Top). Otherwise, in the multiple-inheritance/diamond-of-death scenario (where virtual inheritance is classically used), the compiler wouldn't know which of the two "middle" classes should construct the single base class. The Middle's constructor's call to the Top's constructor is therefore ignored when Middle is being constructed from Bottom:

class Top {
    public:
        Top() {}
}

class Middle: virtual public Top {
    public:
        Middle(): Top() {} // Top() is ignored if Middle constructed through Bottom()
}

class Bottom: public Middle {
    public:
        Bottom(): Middle(), Top() {}
}

So, in the the approach 2) or 3) in your question, Bottom() can't call Top() because it's inherited privately (by default, like in your code, but it's worth making it explicit) in Middle and thus is not visible in Bottom. (source)

@Francis Boivin 2010-02-02 14:23:43

To answer your question, you can't inherit from CBase because in virtual inheritance a derived class would need to have direct access to the class from which it was inherited virtually. In this case, a class that would derive from CBase would need to have direct access to CSealed which it can't since the constructor is private.

Though I don't see the usefulness of it all (ie: stopping inheritance) you can generalize using templates (I don't think it compiles on all compilers but it does with MSVC)

template<class T>
class CSealed
{
    friend T;    // Don't do friend class T because it won't compile
    CSealed() {}
};

class CBase : private virtual CSealed<CBase>
{
};

@Jagannath 2010-02-02 15:31:06

It has to be class CBase : private virtual CSealed<CBase>. Otherwise, CBase can be derived.

@KitsuneYMG 2010-02-02 13:28:36

You are going through contortions to prevent further subclassing. Why? Document the fact that the class isn't extensible and make the dtor non-virtual. In the spirit of c, if someone really wants to ignore the way you intended this to be used why stop them? (I never saw the point of final classes/methods in java either).

//Note: this class is not designed to be extended. (Hence the non-virtual dtor)
struct DontExtened
{
  DontExtened();
  /*NOT VIRTUAL*/
  ~DontExtened();
  ...
};

@Manuel 2010-02-02 13:39:55

I think the point in Java is that the JIT compiler can optimize calls to virtual methods if the class is final

@KitsuneYMG 2010-02-02 14:20:31

@Manuel. I understand the jvm may like it, but there should be a easy way to undo that w/o changing the source. @ReallyOverride?

@jalf 2010-02-02 15:54:31

That's getting sidetracked a bit. In Java, the final keyword makes sense because all functions are virtual by default, and so there's a lot to gain by allowing the JIT compiler to perform these optimizations. In C++, there'd be nothing to gain from a similar mechanism to prevent subclassing, which is why the language doesn't supply a mecehanism for doing it.

@Ed S. 2010-10-27 22:53:38

Sanity ftw. Let people shoot themselves in the foot if they want to.

@Thomas Eding 2014-09-04 20:45:14

-1: Such contortions are for accidents. Also, this does not answer the OP's question (even if it may (it's not) be an X-Y problem). Your answer is equivalent to make everything public and document every logically private item as such. My rule is let the compiler help you not make mistakes when possible.

@KitsuneYMG 2014-09-05 10:40:22

@ThomasEding Well, you can think that, but you're dead wrong. This is the idiomatic way in c++ 2011. My rule is to write code that can be expected to be well understood by the wider community of coders for a language. Also, if your compiler does not warn you about inheriting from a class with a non-virtual dtor, you need a new one.

@Thomas Eding 2014-09-05 18:53:47

I thought C++11 had final for that very purpose. Thus I would expect that to be the idiomatic way to do so in C++11. Why use comments when you have a language feature to do exactly what the comments try (but fail) to enforce?

@Tommy Andersen 2015-04-10 13:16:59

I agree with @ThomasEding the final identifier, is the better option. It allows the compiler to assist you, and documents very precisely that this class is not intended for inheritance. If you choose to take the path of comments, then you would have the programmer look up the source, and interpret the comment. Which can be multiple lines, perhaps even within a larger commenting block. By using final the IDE can help when at first attempting to inherit from the class. Besides using final the developer knows where to look, even if he/she is not used to the programming style used.

@Thomas Eding 2015-04-10 19:23:02

This answer has other problems in addition, including deriving from a derived class with a virtual destructor; and legitimate non-virtual subclassing (take std::unary_function for instance). stackoverflow.com/questions/7403883/…

@Andrew Lazarus 2015-10-23 21:49:48

-1. My compiler doesn't read documentation, and sometimes my colleagues don't either. Enterprise-quality software outsources constraints to the code.

@Limited Atonement 2016-01-13 15:02:06

The reason I'm using final on my struct is because I use it by value in a std::vector. If someone extends it and gets one in the std::vector, that's real bad.

@Vijay 2013-03-21 08:51:29

class myclass;

    class my_lock {
        friend class myclass;
    private:
        my_lock() {}
        my_lock(const my_lock&) {}
    };

    class myclass : public virtual my_lock {
        // ...
    public:
        myclass();
        myclass(char*);
        // ...
    };

    myclass m;

    class Der : public myclass { };

    Der dd;  // error Der::dd() cannot access
            // my_lock::my_lock(): private  member

I found it here to give credit. I am posting here just other people can easily access http://www.devx.com/tips/Tip/38482

@Peter N Lewis 2012-09-07 13:55:08

As of C++11, you can add the final keyword to your class, eg

class CBase final
{
...

The main reason I can see for wanting to do this (and the reason I came looking for this question) is to mark a class as non subclassable so you can safely use a non-virtual destructor and avoid a vtable altogether.

@Nemanja Boric 2014-04-12 09:20:12

There is another good reason and that's preventing derived classes to break the contract of the immutable classes.

@Peter N Lewis 2014-04-16 08:24:58

@Nemanja Boric that would apply to any subclass and any contract, not just mutability. Any subclass can potentially break any implied contracts of the class - that isn't really a good reason for disallowing all subclasses. For an immutable object, what if you want to add a derived value, for example FullName() from FirstName() and LastName() methods, or perhaps a specific hash function.

@T.E.D. 2010-02-02 13:47:23

If you can, I'd go for the first option (private constructor). The reason is that pretty much any experienced C++ programmer will see that at a glance and be able to recognize that you are trying to prevent subclassing.

There might be other more tricky methods to prevent subclassing, but in this case the simpler the better.

@Whoever 2010-02-02 13:30:16

1) is a matter of taste. If I see it correctly, your more fancy 2nd and 3rd solutions move the error in certain circumstances from link time to compile time, which in general should be better.

2) Virtual inheritance is needed to force the responsibility to initialize the (virtual) base class to the most derived class from where the base class ctor is no longer reachable.

@Sergey Teplyakov 2010-02-02 13:10:50

One more solution:

template < class T >
class SealedBase
{
protected:
    SealedBase()
    {
    }
};

#define Sealed(_CLASS_NAME_) private virtual SealedBase<_CLASS_NAME_>


#include "Sealed.h"

class SomeClass : Sealed(Penguin)
{
};

@Thomas Eding 2014-09-04 21:22:51

Adding a macro to the mix does not make this a new solution.

Related Questions

Sponsored Content

14 Answered Questions

[SOLVED] How can I profile C++ code running on Linux?

  • 2008-12-17 20:29:24
  • Gabriel Isenberg
  • 463018 View
  • 1639 Score
  • 14 Answer
  • Tags:   c++ unix profiling

21 Answered Questions

[SOLVED] What is the "-->" operator in C++?

6 Answered Questions

[SOLVED] Python class inherits object

33 Answered Questions

[SOLVED] Prefer composition over inheritance?

35 Answered Questions

1 Answered Questions

[SOLVED] The Definitive C++ Book Guide and List

  • 2008-12-23 05:23:56
  • grepsedawk
  • 2131156 View
  • 4248 Score
  • 1 Answer
  • Tags:   c++ c++-faq

26 Answered Questions

[SOLVED] Why not inherit from List<T>?

15 Answered Questions

1 Answered Questions

[SOLVED] prevent inheritance of derived class using protected c++

  • 2017-10-15 08:16:29
  • knils
  • 53 View
  • 2 Score
  • 1 Answer
  • Tags:   c++ inheritance

Sponsored Content