views:

553

answers:

7

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

+1  A: 

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)
{
};
Sergey Teplyakov
+8  A: 

You are going through contortions to prevent further subclassing. Why? Documenet 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();
...
};
KitsuneYMG
I think the point in Java is that the JIT compiler can optimize calls to virtual methods if the class is final
Manuel
@Manuel. I understand the jvm may like it, but there should be a easy way to undo that w/o changing the source. `@ReallyOverride?`
KitsuneYMG
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.
jalf
Sanity ftw. Let people shoot themselves in the foot if they want to.
Ed Swangren
+2  A: 

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.

Whoever
+6  A: 

You can't prevent inheritance - 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.

anon
+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
Binary Worrier
+1  A: 

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.

T.E.D.
A: 

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 CSealed<CBase>
{
};
Francis Boivin
It has to be class CBase : private virtual CSealed<CBase>. Otherwise, CBase can be derived.
Jagannath
A: 

Bjarne Stroustrup answers this on his FAQ:

He demonstrates a similar example using your 2nd example.

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

Great question! Thanks for getting creative with various solutions.

Shiftbit