Deriving from a STL container

M

mosfet

Could someone tell me why it's considered as bad practice to inherit
from STL container ?
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?
 
B

bjeremy

Could someone tell me why it's considered as bad practice to inherit
from STL container ?
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?

STL Containers are not written with virtual destructors. You may open
yourself up to memory leaks. Also any extension to a std container may
not guarantee compatability with the std algorithms, you clients would
need to be aware of this.

You shouldn't need to rewrite "tons of code", a standard practice is
to use composition and make an adapter class "have a" std container,
plus whatever extra functionality you need to extend the container for
in the first place.
 
V

Victor Bazarov

bjeremy said:
STL Containers are not written with virtual destructors. You may open
yourself up to memory leaks.

Actually, undefined behaviour, if you mean trying to invoke the d-tor
polymorphically.
Also any extension to a std container may
not guarantee compatability with the std algorithms, you clients would
need to be aware of this.

I am not sure what you mean by this.
You shouldn't need to rewrite "tons of code", a standard practice is
to use composition and make an adapter class "have a" std container,
plus whatever extra functionality you need to extend the container for
in the first place.

.... which can also be achieved by private inheritance, last time I looked.

In any case, there are issues that need to be considered, but there is
no hard rule saying "do not inherit".

V
 
B

bjeremy

Actually, undefined behaviour, if you mean trying to invoke the d-tor
polymorphically.
Yes I mean invoking the d-tor polymorphically
I am not sure what you mean by this.
I mean if you offer your extension as a replacement for a std
container, don't make any api changes that would break the containers
use with any of the std algs.
... which can also be achieved by private inheritance, last time I looked.

Really.. so you promote private inheritance as an alternative to
composition in the cases where you do not need to override virtual
functions.. of which std containers have none?
 
B

BobR

mosfet wrote in message...
Could someone tell me why it's considered as bad practice to inherit
from STL container ?

They do not have virtual destructors.
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?

You can derive from the standard containers, but you need to be aware of the
restrictions on using the derived classes.
[ mainly, do *not* handle it through a base pointer.]
 
V

Victor Bazarov

bjeremy said:
Yes I mean invoking the d-tor polymorphically

I mean if you offer your extension as a replacement for a std
container, don't make any api changes that would break the containers
use with any of the std algs.

Uh... Right. IOW, if you implement your own container (by deriving
from the standard one or otherwise), make sure your interface is such
that the users can use it with standard algorithms. Is that what you
are saying? I am not sure how this is relevant to the derivation from
the container. I mean, it does not matter whether you derive or you
don't derive; in order to make the class usable with algorithms, it has
to adhere to certain rules. At the same time, once you derived, you
don't _have_to_ adhere to those rules simply because you derived from
a standard container.
Really.. so you promote private inheritance as an alternative to
composition in the cases where you do not need to override virtual
functions.. of which std containers have none?

I don't "promote" anything and nobody said anything about anything's
being "an alternative". The question is about deriving from a standard
container. You used the term "composition". Are you saying private
inheritance cannot be used to implement composition? Does that mean
you promote limiting the implementation of "composition" to data member
definition only (when C++ is concerned)?

V
 
V

Victor Bazarov

BobR said:
mosfet wrote in message...

They do not have virtual destructors.

By answering this question you essentially confirm that "to inherit
from STL container" is "bad practice". Correct? Or is that more of
"those who say it's bad practice give absence of virtual destructors
as the rationale" type of answer?
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?

You can derive from the standard containers, but you need to be aware
of the restrictions on using the derived classes.
[ mainly, do *not* handle it through a base pointer.]

So, should the OP take it that it's really not such bad practice, after
all, to derive? <g>

V
 
G

Guest

Really.. so you promote private inheritance as an alternative to
composition in the cases where you do not need to override virtual
functions.. of which std containers have none?

It is rather more like using private inheritance to achieve composition
than an alternative to.
 
B

bjeremy

It is rather more like using private inheritance to achieve composition
than an alternative to.

Yeah.. I know what Victor was saying... The point I was trying to make
is that a lot people (Sutter, Myers, etc) suggest that if you do not
intend to override virtual members then in that scenario they *prefer*
composition to private inheritance. Since std containers contain no
virtual functions to override, then there would be no reason the
prefer private inheritance over composition... Actually, I'm not
married to this issue, just Victor's flippant response reminded me of
grade school... so I thought I would get in on the act.
 
K

Kai-Uwe Bux

bjeremy said:
STL Containers are not written with virtual destructors. You may open
yourself up to memory leaks.

It's not memory leaks, it's undefined behavior. You open yourself to that if
you delete a derived object through a pointer to the base class.

However, pointers to std::some_container< some_type > are not really likely
to be used polymorphically. In fact, the STL provides template classes like
std::iterator<> that do not have a virtual destructor yet are explicitly
meant to be derived from.


A more substantial issue are surprise matches with functions like

template < typename T, typename A >
std::vector< T, A > backwards ( std::vector< T, A > const & vec );

If you derive from std::vector, your class will match the argument type, but
the result will not be what you want.

Also any extension to a std container may
not guarantee compatability with the std algorithms, you clients would
need to be aware of this.

The standard algorithms work on ranges specified by pairs of iterators. They
will work just fine with iterators obtained from containers derived from
std::vector. (Actually, that is part of the problem and not part of a
solution, see below).

You shouldn't need to rewrite "tons of code", a standard practice is
to use composition and make an adapter class "have a" std container,
plus whatever extra functionality you need to extend the container for
in the first place.

That actually amounts to writing tons of code (just count the number of
methods in std::vector and you get an idea of how many stupid forwarding
methods you will have to provide to extend container classes this way). You
can cut down on that considerably by using private inheritance.



To the OP: to extend the functionality of standard containers just provide
free standing functions, preferably generic algorithms.


It is definitely bad practice to derive from standard containers in a way
that introduces new invariants (like the entries being sorted). The reason
is that your class inherits begin() and end() from the underlying container
whence you have no way to enforce the new invariants. E.g., mutating
sequence algorithms will happily destroy any given order of elements. This
also explains why it is by and large useless to inherit from standard
containers: you cannot do anything that you could not achieve by
free-standing functions.


There is exactly one case where I consider deriving from standard containers
useful: creating different sequence types that allow for distinct operator
overloading. The problem arises in math programming like this: Consider

typedef std::vector<int> word;
typedef std::vector<int> lattice_point;

You may want operator+ to add lattice_point objects component-wise, whereas
for objects of type word, you may want operator+ to concatenate. Typedefs
will not work since they just create alias names. In these cases, some
quick hack like

struct word : public std::vector<int> {
// forwarding templated constructors
// no further extensions.
};

struct lattice_point : public std::vector< int > {
// same as above
};

can be justified. The classes are not extending std::vector, they just
provide non-aliased versions that distinguish int-vectors according to
their meanings. That allows

word operator+ ( word const & lhs, word const & rhs );

and

lattice_point operator+ ( lattice_point const & lhs,
lattice_point const & rhs );

to coexist peacefully.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

BobR said:
mosfet wrote in message...

They do not have virtual destructors.

That's an orthogonal issue. The template classes std::iterator<> or
std::unary_function<> also do not have virtual destructors, yet they are
clearly provided to be inherited from.


Best

Kai-Uwe Bux
 
B

bjeremy

However, pointers to std::some_container< some_type > are not really likely
to be used polymorphically.
Good point.
..
..
..
The standard algorithms work on ranges specified by pairs of iterators. They
will work just fine with iterators obtained from containers derived from
std::vector. (Actually, that is part of the problem and not part of a
solution, see below).

Wouldn't this depend on how exaclty how you extend the container. The
for example iterators would assume they are iterating over a
std::vector, not your derived vector.
That actually amounts to writing tons of code (just count the number of
methods in std::vector and you get an idea of how many stupid forwarding
methods you will have to provide to extend container classes this way). You
can cut down on that considerably by using private inheritance.

Right.. but a drawback might be that you need to take care not
override std containers members. I guess this is still a problem
whether inheritance or composition is used, however it is clearer if
the future developer sees a "virtual" member specifically.
Also, what if another developer wants to inherit from your derived
container class?

Anyway... Herb Sutter had a good article about this in his GotW
series: http://www.gotw.ca/publications/mill06.htm
 
P

Pete Becker

You can derive from the standard containers, but you need to be aware of the
restrictions on using the derived classes.
[ mainly, do *not* handle it through a base pointer.]

Mainly, do *not* delete it through a base pointer.
 
K

Kai-Uwe Bux

bjeremy said:
Good point.
.
.
.

Wouldn't this depend on how exaclty how you extend the container. The
for example iterators would assume they are iterating over a
std::vector, not your derived vector.

Not really :)

If you inherit from std::vector, then begin() and end() provide iterators
that iterate over the std::vector subobject contained in the derived
vector. Since that subobject _is_ an honest vector, the standard algorithms
are not being lied to.

Of course, this assumes that you are not doing something weird like
redefining begin() and end() or the iterator class in the derived vector.
If someone does that (for reasons beyond my imagination), you are right:
the programmer has to make sure that the new iterators satisfy the standard
requirements.

Right.. but a drawback might be that you need to take care not
override std containers members.

With composition, the problem turns into getting the forwarding right.
I guess this is still a problem
whether inheritance or composition is used, however it is clearer if
the future developer sees a "virtual" member specifically.

Actually, in the few cases that I know where inheriting from std::vector
might be a good idea, there are no new methods, neither virtual nor
non-virtual. In those cases, the only desired effect of inheritance is that
the class is a type of its own that is recognized as distinct for
overloading purposes.

Also, what if another developer wants to inherit from your derived
container class?

Not a problem. Just document the expectations of your class clearly. In the
cases where I do it, the derived class is very much vector-like. So,
further derivation is subject to the same restrictions (i.e., no
polymorphic use of pointer-to-base).
Anyway... Herb Sutter had a good article about this in his GotW
series: http://www.gotw.ca/publications/mill06.htm


Best

Kai-Uwe Bux
 
T

tragomaskhalos

Yeah.. I know what Victor was saying... The point I was trying to make
is that a lot people (Sutter, Myers, etc) suggest that if you do not
intend to override virtual members then in that scenario they *prefer*
composition to private inheritance. Since std containers contain no
virtual functions to override, then there would be no reason the
prefer private inheritance over composition...

This makes no sense to me - there is an excellent reason to
prefer private inheritance over delegation in this case
(which is what I believe you mean by "composition", since
as has been pointed out private inheritance also implements
composition) - it's much less code, and much less error-prone.
 
B

BobR

Victor Bazarov wrote in message...
By answering this question you essentially confirm that "to inherit
from STL container" is "bad practice". Correct?

Not correct! I don't think it's "bad practice". The "bad practice" might
come into play in how you use the Derived_From_Container class.
"Slicing" is not bad, unless it's un-intentional. Sometimes it can help you,
sometimes it will kill you. Knowing "when" is the important part.
"Memory leaks" are bad, IMHO.
Or is that more of
"those who say it's bad practice give absence of virtual destructors
as the rationale" type of answer?

Yeah, more along that line of thinking.
As long as you are aware of the lack of virtual destructors, there are times
when inheriting from a std container can[1] be used in place of a more
complex design.
I've only inherited from std::vector for a special task in a personal
program. I don't think I'd use it in a serious program. I don't try to
'shoehorn' code in. Use the proper tool for the job at hand.

[1] - Just because you can said:
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?

You can derive from the standard containers, but you need to be aware
of the restrictions on using the derived classes.
[ mainly, do *not* handle it through a base pointer.]

So, should the OP take it that it's really not such bad practice, after
all, to derive? <g>

The OP should study what virtual destructors do, the class (s)he plans to
inherit from, and then make a decision.
Or even better, describe exactly the problem, and let you experts guide the
way.
 
B

BobR

Kai-Uwe Bux wrote in message...
That's an orthogonal issue. The template classes std::iterator<> or
std::unary_function<> also do not have virtual destructors, yet they are
clearly provided to be inherited from.

I didn't say "don't inherit", I said 'virtual destructors' are why some
people *say* it's bad practice (what the OP asked, AFAIK.).
<G>
I have some classes inherited from std::vector that works smooth as glass.
[ and the class has a // comment about further inheritance, and destruction
through a base pointer (thanks Pete.).]
 
D

Daniel T.

mosfet said:
Could someone tell me why it's considered as bad practice to inherit
from STL container ?

Because there is nothing that can be gained from doing it.
And when you want to customize a STL container, do you mean I need to
write tons of code just to avoid to derive from it ?

Not at all. You just need to write your custom functions. If you follow
the same idiom as the standard algorithms, your functionality will be
useful for several containers.
 
J

James Kanze

BobR wrote:
That's an orthogonal issue. The template classes
std::iterator<> or std::unary_function<> also do not have
virtual destructors, yet they are clearly provided to be
inherited from.

Yes, but they were designed to be base classes, std::vector
wasn't. In this case, I think the difference is that classes
like std::iterator<> don't have any semantics; you'd never,
never have an std::iterator<>* in your program, much less delete
through it. This is a lot less true for std::vector<>*.

My own feeling is that std::vector<> isn't designed for use as a
base class, and so shouldn't usually be used as a base class.
On the other hand, if the use were local and well controled, I
don't think I'd get up in arms about it. I would object to it
if the derived class were some general and widely used
component, however.
 
B

bjeremy

This makes no sense to me - there is an excellent reason to
prefer private inheritance over delegation in this case
(which is what I believe you mean by "composition", since
as has been pointed out private inheritance also implements
composition) - it's much less code, and much less error-prone.

I'm not intending to make a snyde comment so I apologize if it comes
across that way, but it seems to me that you are arguing for increased
coupling between objects for the sake of saving yourself some key-
strokes. For me, I try and always use the weakest relationship between
objects that I can get away with, not saying this is "the way" just
that is what I try to do.. Since in this example we know that the std
container will not have a) and protected data that I need to use, and
b) the std container contains no virtual functions I need to override
then I will use containment, forwarding functions have never been a
chore to write or get right. If you choose to inherit in this case, it
appears that you would be creating a needless dependency.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,232
Latest member
DorotheaDo

Latest Threads

Top