I have recently dealt a lot with a certain aspect of class design:
Wether or
not to derive from concrete classes. I have learned from various books,
articles and newsgroup postings that deriving from concrete classes is
considered a bad idea. A base class should be made abstract, either by
giving it at least one pure virtual function or by making its destructor
protected (and non-virtual).
However, I have to object for two reasons:
1. What is so bad about concrete base classes? The only reason I found
for
not allowing concrete base classes is the problems involved with
assignment.
Since assignment is based on the static type, the derived part of
classes
might no be assigned.
2. Not being able to derive from concrete classes imposes limits that
defeat
the purpose of the language. One of the big advantages of C++ is the
ability
to extend an inheritance hierarchy. If only leaf classes may be
concrete,
than a certain hierarchy and class design is fixed once and forever -
this
makes the possibilities that C++ was designed for go down the drain.
Best regards,
Matthias Hofmann
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
More accurately, some people regard it as a bad idea.
> A base class should be made abstract, either by
> giving it at least one pure virtual function or by making its
destructor
> protected (and non-virtual).
<shrug>
In some kinds of code that's a good guideline. Be wary of people who
generalize from narrow experience, no matter how deep.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
A protected, non-virtual destructor is utterly useless and potentially
dangerous. You need virtual destructors for polymorphism to work right when
deleting an object of a derived class through a pointer to a base class. If
you design a class to be derived from, you must provide a virtual
destructor.
sm
> Hello everyone!
>
> I have recently dealt a lot with a certain aspect of class design:
> Wether or
> not to derive from concrete classes. I have learned from various books,
> articles and newsgroup postings that deriving from concrete classes is
> considered a bad idea. A base class should be made abstract, either by
> giving it at least one pure virtual function or by making its destructor
> protected (and non-virtual).
I think you've worded the guideline wrong way around. I'd say: Base
types should usually be abstract, because they are usually categories,
and there's seldom a reason to instantiate categories.
This is an orthodox pure OO notion. If you are using non-OO designs,
you are (more) likely to come across a reason to use concrete
base classes. Much (all?) of the programming advice we encounter
is based on assumptions about styles, tools, and methods. Change
any of those, and the advice may no longer make sense.
> However, I have to object for two reasons:
>
> 1. What is so bad about concrete base classes? The only reason I found
> for
> not allowing concrete base classes is the problems involved with
> assignment.
> Since assignment is based on the static type, the derived part of
> classes
> might no be assigned.
There are really two reasons, that I know of, one design-oriented,
and one technically oriented:
(a) Concrete types typically model 'real' entities. Base types
typically model categories of entities, or attributes common
to many kinds of entities. To use an analogy sword might be
modeled as a concrete type, while weapon might be modeled
as a base type, since weapon is a category. Now, does an object
of type weapon, which is not a sword, spear, rifle,
club, etc, but just some anonymous weapon make sense? Probably
not. This analogy doesn't apply to every problem, or
even every OO problem, but it makes a good guideline.
(b) Implicit conversions. In C++, deriving from a base class
creates 3 kinds of implicit conversions: Derived -> Base,
Derived& -> Base&, and Derived* -> Base* . The first
conversion results in slicing, which you mention, but slicing
doesn't just happen during assignment - it also happens
whenever functions take arguments of base type by value, and
anywhere else objects of base type are copy constructed from
objects of derived type (like catching an exception by value).
IMO, (a) is an important drawback whenever one is designing entity
types using OOP - but I can't see how it applies outside of
OOP. To me, someone who dislikes most implicit conversions,
especially lossy ones, (b) sounds serious - but in practice
assignment and copy by value are often the wrong thing for entity
independent of slicing concerns - so one makes the base class copy
constructor and copy assignment operator private anyway, stopping
the problem. The derived type could declare a private
operator base() conversion function, but I don't know if that's
actually useful.
>
> 2. Not being able to derive from concrete classes imposes limits that
> defeat
> the purpose of the language. One of the big advantages of C++ is the
> ability
> to extend an inheritance hierarchy. If only leaf classes may be
> concrete,
> than a certain hierarchy and class design is fixed once and forever -
> this
> makes the possibilities that C++ was designed for go down the drain.
?
I don't understand this claim. I don't see why one can't derive more
leaf classes from existing abstract base classes in order to
extend the hierarchy.
How do you propose to delete a derived object through a base class
pointer if the base has a protected dtor?
--
ACCU Spring Conference 2003 April 2-5
The Conference you should not have missed
ACCU Spring Conference 2004 Late April
Francis Glassborow ACCU
You're misunderstanding something here: you cannot delete an object via a
pointer to the base if the base's destructor is private. The essence of
this boils down to either providing a protected non-virtual or a public
virtual destructor.
I wish I could remember the name of the article that explained this in more
detail, anyone else remembers ?
Ulrich Eckhardt
--
Questions ?
see C++-FAQ Lite: http://parashift.com/c++-faq-lite/ first !
If your design calls for deleting objects through pointers to the base
then you must make the destructor virtual. There are many uses of
inheritance that do not involve deleting through base pointers.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Well, the point is that if the destructor is protected, you cannot delete a
derived class object through a base class pointer, so there is no danger to
it. You also cannot instantiate an automatic object of that type because the
destructor is inaccesible. This is the reason why many people prefer this
method in order to save the memory for the vtable pointer.
Regards,
Matthias Hofmann
Well, of course you could derive from an existing abstract class to extend
the hierarchy. But imagine that one of the leaf classes offers almost
exactly what you need, except for a little thing you would just like to add.
In that case, it would be convenient to derive from this concrete class and
just override one of the virtual functions to add this little extra
something. However, if you derive from another abstract class, you have to
implemement everything the concrete class offers yourself.
Of cource you can use a trick: Do not derive from the concrete class, but
implement your class in terms of the concrete one, by containment or even
better by private inheritance (so you can even redefine virtual functions).
Regards,
Matthias
You can't. So what is a protected destructor supposed to accomplish anyway?
If it is protected, I have to provide a member function that destroys the
object (e.g. using "delete this;"). The problem with that approach is, of
course, that the object can't be placed on the stack anymore. And if the
class is inteded as a base class, this whole system breaks if the destructor
is protected but still not virtual.
sm
If you call the derived class' dtor, that will in turn call the base's dtor
whan it is finished. No virtual needed. You can perfectly well put such a
beast on the stack, no need to provide any additional memberfunctions or
other hacks.
Note that the derived class does have a public (and sometimes virtual)
dtor.
> And if the class is inteded as a base class, this whole system
> breaks if the destructor is protected but still not virtual.
Being intended as a baseclass doesn't always mean that you're supposed to
store objects of the derived concrete classes via a pointer to the
baseclass. This is especially true if the baseclass only defines an
interface or when you are inheriting some functionality (intrusive
refcounting comes to mind).
Ulrich Eckhardt
--
Questions ?
see C++-FAQ Lite: http://parashift.com/c++-faq-lite/ first !
However be aware that there are some problems with using either private
inheritance if dynamic_casting might be used.
--
ACCU Spring Conference 2003 April 2-5
The Conference you should not have missed
ACCU Spring Conference 2004 Late April
Francis Glassborow ACCU
Learning about the idioms in common use in some programming domains
might help make it clear what they are good for.
protected/private dtors are deliberately used where stack based
instances would be an error. Windows type frameworks often have this
requirement, as do some cases for use with COM and CORBA
They are also used where any freestanding instance would be a mistake.
This is also a frequent idiom in COM and CORBA exactly because it saves
creating a vtable when one is not wanted.
The use of a protected/private dtor accomplishes two things
1) it prevents the creation of auto objects of that type
2) it prevents deleting derived object through a pointer to this type.
Those things are independently useful in some designs.
--
ACCU Spring Conference 2003 April 2-5
The Conference you should not have missed
ACCU Spring Conference 2004 Late April
Francis Glassborow ACCU
|> Matthias Hofmann wrote:
|> > Wether or not to derive from concrete classes. I have learned
|> > from various books, articles and newsgroup postings that
|> > deriving from concrete classes is considered a bad idea.
|> More accurately, some people regard it as a bad idea.
Most accurately, some people regard it as a bad idea in certain
circomstances. It's a bad idea often enough that if you find yourself
doing it, it's worth asking why. But if the reasons are valid...
--
James Kanze mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France Tel. +33 1 41 89 80 93
|> "Francis Glassborow" <francis.g...@ntlworld.com> wrote in
message
|> news:NynWonBI...@robinton.demon.co.uk...
|> > In message <vf78k2...@corp.supernews.com>, Sebastian Moleski
|> > <s.mo...@tcu.edu> writes
|> > >A protected, non-virtual destructor is utterly useless and
|> > >potentially dangerous. You need virtual destructors for
|> > >polymorphism to work right when deleting an object of a
|> > >derived class through a pointer to a base class. |> If you
|> > >design a class to be derived from, you must provide a virtual
|> > >destructor.
|> > How do you propose to delete a derived object through a base
|> > class pointer if the base has a protected dtor?
|> You can't. So what is a protected destructor supposed to
|> accomplish anyway?
Prevents deletion through a pointer to the base class. In other
words, prevents undefined behavior.
--
James Kanze mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France Tel. +33 1 41 89 80 93
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
|> Sebastian Moleski wrote:
|> > A protected, non-virtual destructor is utterly useless and
|> > potentially dangerous. You need virtual destructors for
|> > polymorphism to work right when deleting an object of a derived
|> > class through a pointer to a base class. If you design a class
|> > to be derived from, you must provide a virtual destructor.
|> You're misunderstanding something here: you cannot delete an
|> object via a pointer to the base if the base's destructor is
|> private.
You can't derive from the class if the destructor is private.
--
James Kanze mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France Tel. +33 1 41 89 80 93
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
"Virtuality" by Herb Sutter
(http://www.gotw.ca/publications/mill18.htm):
"Guideline #4: A base class destructor should be either public and
virtual, or protected and nonvirtual."
This is a recurring theme, and again and again we get postings saying
you need a virtual destructor for a base class, when that doesn't need
to be the case, and there are safe, efficient alternatives, such as
protected nonvirtual destructors.
Regards,
Terje
I don't know if this it what you are looking for, but some information
on
this topic can be found here:
http://www.gotw.ca/publications/mill18.htm
Best regards,
Matthias Hofmann
On the contrary. As has been said in this thread, a concrete base class
serves two roles: As an interface, and as an implementation of that
interface. It may make a more cohesive design to separate the roles. As
it says in the "Effective C++" books, then you identify the abstraction
that is common, and make an abstract/non-instantiable base class of it.
An apparently common misconception when it comes to OO is that you can
take any class and derive from it to make a new class. However, classes
typically need to be designed to be derived from. A class presents two
interfaces to the world: The public interface, for all, and the public
and protected interface, for any derived classes. If the class is not
designed with inheritance in mind, its interface for derived classes is
essentially random, and therefore not likely to be appropriate.
Regards,
Terje
The above question was itself the answer: A protected destructor expresses
that deletion is not one of the operations you can do through the base
interface. It can make sense to support other virtual operations and not
deletion (e.g., when a framework owns the objects, or in a
garbage-collected system where the objects are designed to clean
themselves up at finalization time, which is sometime after the last
pointer to the object has been let go).
As James put it:
>Prevents deletion through a pointer to the base class. In other
>words, prevents undefined behavior.
For more on this, see also my article:
"Virtuality"
C/C++ Users Journal, 19(9), September 2001
http://www.gotw.ca/publications/mill18.htm
Quoting from the Summary:
"For the special case of the destructor only:
- Guideline #4: A base class destructor should be either
public and virtual, or protected and nonvirtual."
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Visual C++ program manager, Microsoft (www.gotw.ca/microsoft)
> In message <3ef4a65d$1...@news.nefonline.de>, Matthias Hofmann
> <hof...@anvil-soft.com> writes
>>Of cource you can use a trick: Do not derive from the concrete class,
> but
>>implement your class in terms of the concrete one, by containment or
> even
>>better by private inheritance (so you can even redefine virtual
> functions).
>
> However be aware that there are some problems with using either
private
> inheritance if dynamic_casting might be used.
[snip]
Could you explain what these problems are?
What are these problems? As far as I know, private inheritance simply means
(among other things) that you cannot convert a
pointer-to-derived-class-object to a pointer-to-base-class-object.
Regards,
Matthias
I understand that an abstract base class makes sense as it provides an
interface. The one thing that confused me a little was just the idea of a
concrete class being the end of an inheritance-path.
>
> An apparently common misconception when it comes to OO is that you can
> take any class and derive from it to make a new class. However, classes
> typically need to be designed to be derived from. A class presents two
> interfaces to the world: The public interface, for all, and the public
> and protected interface, for any derived classes. If the class is not
> designed with inheritance in mind, its interface for derived classes is
> essentially random, and therefore not likely to be appropriate.
>
>
"...and the public and protected interface, for any derived classes." - I
guess one has to add private virtual functions to this list.
So one can generally say:
"If the class is abstract, derive from it if you like. If it is concrete,
please don't."
Is that a good guideline?
Regards,
Matthias
> > llewelly <llewe...@xmission.dot.com> schrieb in im Newsbeitrag:
> > 86fzm4f...@Zorthluthik.local.bar...
> > > "Matthias Hofmann" <hof...@anvil-soft.com> writes:
> > > > 2. Not being able to derive from concrete classes imposes
> > > > limits that defeat the purpose of the language. One of the big
> > > > advantages of C++ is the ability to extend an inheritance
> > > > hierarchy. If only leaf classes may be concrete, than a certain
> > > > hierarchy and class design is fixed once and forever - this
> > > > makes the possibilities that C++ was designed for go down the
> > > > drain.
> On the contrary. As has been said in this thread, a concrete base
> class serves two roles: As an interface, and as an implementation of
> that interface. It may make a more cohesive design to separate the
> roles. As it says in the "Effective C++" books, then you identify the
> abstraction that is common, and make an abstract/non-instantiable base
> class of it.
You're supposing that the only role of a base class should be an
interface. I agree that it is the most frequent pattern, but, for
example, what about an implementation of the template pattern when you
have a very good default implementation.
> An apparently common misconception when it comes to OO is that you can
> take any class and derive from it to make a new class. However,
> classes typically need to be designed to be derived from. A class
> presents two interfaces to the world: The public interface, for all,
> and the public and protected interface, for any derived classes. If
> the class is not designed with inheritance in mind, its interface for
> derived classes is essentially random, and therefore not likely to be
> appropriate.
Again: what about the template pattern. The base class has a concrete
implementation, which can be customized in one or a few very constrained
ways; customization is through derivation. If you have reasonable
defaults for all of the customizations, for example, what is the problem
with providing them? And if you provide them, the base class becomes
concrete. It's not a particularly frequent pattern, at least in my
code, but I certainly wouldn't want to see it banned, either, on the
grounds that it involves inheriting from a concrete class.
--
James Kanze GABI Software mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, Tél. : +33 (0)1 30 23 45 16
Try dynamic casting when private inheritance is involved. IIRC a private
base acts as a block to a dynamic cast. But do not take my word for it.
--
ACCU Spring Conference 2003 April 2-5
The Conference you should not have missed
ACCU Spring Conference 2004 Late April
Francis Glassborow ACCU
It is much easier to explain inheritance from an abstract base: you
should inherit if and only if your class needs to implement the
interface represented by the abstract base.
Inheritance from a concrete class is much harder to explain or
justify; an inexperienced programmer can't easily tell the good uses
from the bad, and an experienced programmer sometimes needs to look
several moves ahead to foresee the potential problems.
So I'd leave the virtual functions pure in your example, even if I had
good defaults. Of course this has its drawbacks, too. :-)
An abstract class usually represents an interface that specifies the
operations that can be performed via a pointer to this interface.
struct X
{
virtual void f() = 0;
};
says that if I have a pointer p to X, I can call p->f().
Whether 'delete p' is an acceptable operation is typically signalled
by the presence of a public virtual destructor in the interface:
struct Y
{
virtual ~Y() {}
virtual void g() = 0;
};
So I am (logically) allowed to delete an Y, but not an X.
Unfortunately, even though ~X is not explicitly present, it is still
public, so the user can (physically) delete a pointer to X. A
protected destructor closes this loophole, bringing the logical and
the physical interface in sync:
struct X
{
virtual void f() = 0;
protected:
~X() {}
};
boost::shared_ptr<X> supports this idiom and will not attempt to
invoke the protected destructor when initialized with a pointer to a
class derived from X.
> Well, of course you could derive from an existing abstract class to
extend
> the hierarchy. But imagine that one of the leaf classes offers almost
> exactly what you need, except for a little thing you would just like
to
add.
> In that case, it would be convenient to derive from this concrete
class
and
> just override one of the virtual functions to add this little extra
> something. However, if you derive from another abstract class, you
have to
> implemement everything the concrete class offers yourself.
In my experience, a very compelling argument to ignore the rule that we
ought to derive only from abstract classes.
Sometimes, several virtual functions are related, and if we override one
we
should override all of them. If we derive from concrete classes there
is a
possibility we may forget to override some virtual functions. However,
perhaps this problem is better solved through either: reading the
documentation, writing an extensive test driver, factoring the code so
that
there is only one function to override (not a whole group of functions).
> Of cource you can use a trick: Do not derive from the concrete class,
but
> implement your class in terms of the concrete one, by containment or
even
> better by private inheritance (so you can even redefine virtual
functions).
Not recommended. Too much typing, plus the implicit conversion problem
Francis pointed out.
--
+++++++++++
Siemel Naran
> Well, of course you could derive from an existing abstract class to
extend
> the hierarchy. But imagine that one of the leaf classes offers almost
> exactly what you need, except for a little thing you would just like
to
add.
> In that case, it would be convenient to derive from this concrete
class
and
> just override one of the virtual functions to add this little extra
> something. However, if you derive from another abstract class, you
have to
> implemement everything the concrete class offers yourself.
In my experience, a very compelling argument to ignore the rule that we
ought to derive only from abstract classes.
Sometimes, several virtual functions are related, and if we override one
we
should override all of them. If we derive from concrete classes there
is a
possibility we may forget to override some virtual functions. However,
perhaps this problem is better solved through either: reading the
documentation, writing an extensive test driver, factoring the code so
that
there is only one function to override (not a whole group of functions).
> Of cource you can use a trick: Do not derive from the concrete class,
but
> implement your class in terms of the concrete one, by containment or
even
> better by private inheritance (so you can even redefine virtual
functions).
Not recommended. Too much typing, plus the implicit conversion problem
Francis pointed out.
--
+++++++++++
Siemel Naran
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
I would certainly teach inhertance starting with abstract base classes.
But I don't work in a school; I expect my collegues to already know what
inheritance is, and to be familiar with the concept of interfaces.
For the rest, I've found that a reference to the design pattern has
generally been sufficient. No one in the places I've worked would dare
admit to not being familiar with the design patterns in the Gang of
Four's book:-).
On the other hand, the case is relatively rare. Generally speaking, in
my more recent work, I will use delegates rather than the template
pattern. So the issue doesn't come up that often. (And I can't think
of any other case off hand where you might want to inherit from a
concrete class.)
--
James Kanze GABI Software
mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter
Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, Tél. : +33 (0)1 30 23 45
16
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
I have (empirically) found that actively avoiding inheritance from
concrete classes pays off regardless of skill. It often leads to
better designs, but that aside, one very practical reason for the
guideline is that C++ doesn't have "override" to help prevent mistakes
like:
struct X
{
virtual void f() const;
};
struct Y: X
{
virtual void f();
};
where Y::f is intended to override X::f but does not.
> I have (empirically) found that actively avoiding inheritance from
> concrete classes pays off regardless of skill. It often leads to
> better designs, but that aside, one very practical reason for the
Why? Can you elaborate?
--
+++++++++++
Siemel Naran