11

I have a value class according to the description in "C++ Coding Standards", Item 32. In short, that means it provides value semantics and does not have any virtual methods.

I don't want a class to derive from this class. Beside others, one reason is that it has a public nonvirtual destructor. But a base class should have a destructor that is public and virtual or protected and nonvirtual.

I don't know a possibility to write the value class, such that it is not possible to derive from it. I want to forbid it at compile time. Is there perhaps any known idiom to do that? If not, perhaps there are some new possibilities in the upcoming C++0x? Or are there good reasons that there is no such possibility?

SebastianK
  • 3,582
  • 3
  • 30
  • 48

7 Answers7

25

Bjarne Stroustrup has written about this here.


The relevant bit from the link:

Can I stop people deriving from my class?

Yes, but why do you want to? There are two common answers:

  • for efficiency: to avoid my function calls being virtual.
  • for safety: to ensure that my class is not used as a base class (for example, to be sure that I can copy objects without fear of slicing)

In my experience, the efficiency reason is usually misplaced fear. In C++, virtual function calls are so fast that their real-world use for a class designed with virtual functions does not to produce measurable run-time overheads compared to alternative solutions using ordinary function calls. Note that the virtual function call mechanism is typically used only when calling through a pointer or a reference. When calling a function directly for a named object, the virtual function class overhead is easily optimized away.

If there is a genuine need for "capping" a class hierarchy to avoid virtual function calls, one might ask why those functions are virtual in the first place. I have seen examples where performance-critical functions had been made virtual for no good reason, just because "that's the way we usually do it".

The other variant of this problem, how to prevent derivation for logical reasons, has a solution. Unfortunately, that solution is not pretty. It relies on the fact that the most derived class in a hierarchy must construct a virtual base. For example:

class Usable;

class Usable_lock {
    friend class Usable;
private:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
    // ...
public:
    Usable();
    Usable(char*);
    // ...
};

Usable a;

class DD : public Usable { };

DD dd;  // error: DD::DD() cannot access
        // Usable_lock::Usable_lock(): private  member

(from D&E sec 11.4.3).

Simon P Stevens
  • 27,303
  • 5
  • 81
  • 107
  • Very nice, better than my answer! – Motti Jun 16 '09 at 11:42
  • 2
    @Motti: I quite like your answer - I +1'd it. Depending on his requirements it could be a much better solution. Bjarnes is a bit complex if all you really want to do is stop someone using their type in place of yours. I think the important thing to consider is why are you trying to prevent derivation, and does it really achieve anything useful. – Simon P Stevens Jun 16 '09 at 11:53
  • Why is the base class virtual in this solution? – SebastianK Jun 16 '09 at 11:58
  • 2
    @SebastianK: "It relies on the fact that the most derived class in a hierarchy must construct a virtual base". Funny I gave the same answer before Simon and didn't get any votes. Upvote from me as well :) – the_drow Jun 16 '09 at 12:15
  • 2
    @SebastianK: Like the_drow said, by making the base virtual, the most deried class is required to construct it, but in this case can't because the constructor is private. @The_drow: It's the celebrity effect. Mention Stroustrup for extra credit =:) – Simon P Stevens Jun 16 '09 at 12:50
  • 1
    Note that the presence of a virtual base class will increase the runtime size of every Value object by sizeof(Value_Lock *). – Thomas L Holaday Jun 16 '09 at 13:02
  • Note that there's a way that avoids Tom's issue, and also allows you to use a single sealer class for all sealing. See the comments in http://stackoverflow.com/questions/656224/when-should-i-use-c-private-inheritance/656523#656523 – Stefan Monov May 23 '10 at 08:01
8

If you are willing to only allow the class to be created by a factory method you can have a private constructor.

class underivable {
    underivable() { }
    underivable(const underivable&); // not implemented
    underivable& operator=(const underivable&); // not implemented
public:
    static underivable create() { return underivable(); }
};
Motti
  • 110,860
  • 49
  • 189
  • 262
  • Ah, and that could be a static factory method of my class to keep things together? Sounds very good. – SebastianK Jun 16 '09 at 11:38
  • 3
    Unfoprtunately, this doesn't stop derivation. It merely prevents instatiation of the derived type, except via the factory. It would be nice if you could actually pprevent derivation, but as far as I'm aware, you can't. –  Jun 16 '09 at 11:43
  • In what practical way is there a difference between not being able to instantiate an object of the derived type and not being able to derive at all (access to protected static members??) – Motti Jun 16 '09 at 18:02
  • 1
    @Motti: it affects what bit of code fails to compile. I can write a library that defines a class inherited from underivable, and has functions taking parameters which are references to that type and does whatever with them. That code will compile fine: only the client / unit test (which tries to construct objects to pass in) will fail to compile. In general you probably want the compiler to fail early when you try to do something that for whatever reason you took steps to make impossible. It probably also has consequences for TMP, where you rarely need actual objects. – Steve Jessop Jun 17 '09 at 13:11
6

Even if the question is not marked for C++11, for people who get here it should be mentioned that C++11 supports new contextual identifier final. See wiki page

Community
  • 1
  • 1
Tony
  • 1,566
  • 2
  • 15
  • 27
4

Take a good look here.
It's really cool but it's a hack.
Wonder for yourself why stdlib doesn't do this with it's own containers.

the_drow
  • 18,571
  • 25
  • 126
  • 193
2

Well, i had a similar problem. This is posted here on SO. The problem was other way around; i.e. only allow those classes to be derived that you permit. Check if it solves your problem.

This is done at compile-time.

Community
  • 1
  • 1
Abhay
  • 7,092
  • 3
  • 36
  • 50
1

I would generally achieve this as follows:

// This class is *not* suitable for use as a base class

The comment goes in the header and/or in the documentation. If clients of your class don't follow the instructions on the packet, then in C++ they can expect undefined behavior. Deriving without permission is just a special case of this. They should use composition instead.

Btw, this is slightly misleading: "a base class should have a destructor that is public and virtual or protected and nonvirtual".

That's true for classes which are to be used as bases for runtime polymorphism. But it's not necessary if derived classes are never going to be referenced via pointers to the base class type. It might be reasonable to have a value type which is used only for static polymorphism, for instance with simulated dynamic binding. The confusion is that inheritance can be used for different purposes in C++, requiring different support from the base class. It means that although you don't want dynamic polymorphism with your class, it might nevertheless be fine to create derived classes provided they're used correctly.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
0

This solution doesn't work, but I leave it as an example of what not to do.


I haven't used C++ for a while now, but as far as I remember, you get what you want by making destructor private.

UPDATE:

On Visual Studio 2005 you'll get either a warning or an error. Check up the following code:

class A
{
public:
    A(){}
private:
    ~A(){}
};

class B : A
{
};

Now,

B b;

will produce an error "error C2248: 'A::~A' : cannot access private member declared in class 'A'"

while

B *b = new B();

will produce warning "warning C4624: 'B' : destructor could not be generated because a base class destructor is inaccessible".

It looks like a half-solutiom, BUT as orsogufo pointed, doing so makes class A unusable. Leaving answers

ya23
  • 14,226
  • 9
  • 46
  • 43
  • 1
    Doing that you get objects that cannot be deleted... it's true that you cannot inherit from a class like that, but then how do you use it? – Paolo Tedesco Jun 16 '09 at 15:18
  • Damnit, I need to refresh my C++. Thanks for that! – ya23 Jun 16 '09 at 15:26
  • you're welcome :) thanks for updating the post. Anyway, what I meant is that you cannot instantiate an object of type A, not only of type B. – Paolo Tedesco Jun 16 '09 at 15:33