views:

661

answers:

5

Hi all,

Say I've got a class called "Base", and a class called "Derived" which is a subclass of Base and accesses protected methods and members of Base.

What I want to do now is make it so that no other classes can subclass Derived. In Java I can accomplish that by declaring the Derived class "final". Is there some C++ trick that can give me the same effect?

(Ideally I'd like to make it so that no class other than Derived can subclass Base as well. I can't just put all the code into the same class or use the friend keyword, since Base and Derived are both templated, with Base having fewer template arguments than Derived does....)

Thanks, Jeremy

+14  A: 

The C++ FAQ Lite has a topic for this.

Sean Bright
So it does... however, none of those techniques seem entirely satisfactory:#1 means that users of Derived have to use a non-standard idiom to use the class; they can't just declare it directly like you normally would; instead you have to call a Named Constructor.#2 isn't won't generate any compile time errors if subclassing is attempted (which is the effect I am looking for)#3 Will add a vtable to my objects, which I'd prefer to avoid.
Jeremy Friesner
In which case the answer to your question (whether there is a technique) is almost certainly "No".
David Thornley
@Jeremy - Right, as David suggested, you may be out of luck.
Sean Bright
Not sure why this has been voted as high (?) Isn't this answer the equivalent of RTFM?
Alan
@Alan: It's more like "read this exact page of the manual". The second link (a topic), goes directly to an answer. 'RTFM' implies that an answer exists in the documentation, but doesn't specify where.
Conspicuous Compiler
+5  A: 

You can have a private constructor for 'Derived' and a public static Create function for instantiation

Indeera
+3  A: 

The easiest way to prohibiting subclassing is by making the constructor private:

class Foo
{
private:
    Foo() {}

public:
    static Foo* CreateFoo() { return new Foo; }
};


Edit: Thanks to Indeera for pointing out that this needs a static Factory method

Alan
Ah, but how would you create an instance of Foo ? :)
Indeera
Foo.CreateFoo()
Charlie Somerville
Actually `Foo::CreateFoo()`, though why it returns a pointer is a mystery to me, since the class can perfectly be copied...
Matthieu M.
+2  A: 

There is no simple and clean way to do it.

What the standard library does is simply make the destructor nonvirtual. That doesn't prevent subclassing, but it is a strong signal to users that it is not designed for inheritance, and it means you have to be very careful when using the derived class.

Ultimately though, do you need to absolutely make subclassing impossible? Isn't it good enough to indicate that "deriving from this class is a bad idea"?

People can always break your code if they really want to. The best you can do is make them aware of what they should and shouldn't do, and hope they won't actively try to break your code.

Protect your code against Murphy, not Machiavelli. ;)

jalf
+1  A: 

Since you are using templates I was thinking that the last part of your question about preventing any class other than Derived to subclass from Base could be done using appropriate partial specialisations.

The following code snippet is what I came up with but the complexity required only goes to reinforce the answer by jalf. Is it worth it? If anything this has helped me understand partial specialisation more than working out a technique I would ever use in practice.

I use COMMON to indicate a shared template parameter between Base and Derived and EXTRA to denote the extra parameters that you say Derived has. The actual numbers of these could be anything I just happened to have picked one and two for these respectively.

// Forward declaration of class Derived
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Derived;


// Definition of general class template Base
template< class SUBCLASS
        , class COMMON >
class Base
{
private:
    Base() {}
};


// Definition of partial specialisation of template class Base to open up
// access to the constructor through friend declaration.
template< class COMMON
        , class EXTRA1
        , class EXTRA2 >
class Base< Derived< COMMON, EXTRA1, EXTRA2 >
          , COMMON >
{
private:
    Base() {}

    friend class Derived< COMMON, EXTRA1, EXTRA2 >;
};


// Definition of class Derived
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class Derived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    static Derived* create() { return new Derived; }

private:
    Derived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                    , COMMON >()
    {
    }
};


// Definition of class HonestDerived.
// It supplies itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class HonestDerived
    : public Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    HonestDerived() : Base< HonestDerived< COMMON, EXTRA1, EXTRA2 >
                          , COMMON >()
    {
    }
};


// Definition of class DishonestDerived
// It supplies Derived rather than itself as the SUBCLASS parameter to Base.
template < class COMMON
         , class EXTRA1
         , class EXTRA2 >
class DishonestDerived
    : public Base< Derived< COMMON, EXTRA1, EXTRA2 >
                 , COMMON >
{
public:
    DishonestDerived() : Base< Derived< COMMON, EXTRA1, EXTRA2 >
                             , COMMON >()
    {
    }
};


template< class COMMON, class EXTRA1, class EXTRA2 >
class DerivedFromDerived
    : public Derived< COMMON, EXTRA1, EXTRA2 >
{
public:
    DerivedFromDerived() : Derived< COMMON, EXTRA1, EXTRA2 >()
    {
    }
};

// Test partial specialisation gives Derived access to the Base constructor
Derived< int, float, double >* derived
    = Derived< int, float, double >::create();

// Test that there is no access to the Base constructor for an honest subclass
// i.e. this gives a compiler error
HonestDerived< int, float, double > honestDerived;

// Test that there is no access to the Base constructor for a dishonest subclass
// i.e. this gives a compiler error
DishonestDerived< int, float, double > dishonestDerived;

// Test that there is no access to the Derived constructor
// i.e. this gives a compiler error
DerivedFromDerived< int, float, double > derivedFromDerived;

This code was tested with gcc 4.3.2.

Note that an alternative to the friend declaration would be to make the constructor protected in the partial specialisation of Base but then that would allow classes like DishonestDerived to work.

Troubadour