"Stuart Golodetz" <sgolodetz@NdOiSaPlA.pMiPpLeExA.ScEom> wrote in
message news:svSdneDykbPQ5SrWnZ2dnUVZ8nCdnZ2d@pipex.net...
Leigh Johnston wrote:
To accommodate my obvious failure to convey what I actually mean to
some people perhaps the following makes things clearer:
Some people eschew the derivation of the standard library containers
however the only issues to be aware of are 1) that their destructors
are not virtual and 2) the need to ensure that the derived class's
invariant is not broken by calling the container's member functions
which is only a problem if the derived class's invariant consists of
more than just the container's invariant (i.e. contains additional
state which depends on the container's state) which should not be the
case if interface augmentation only is being performed.
I apologize for my language but people calling common sense bullshit
winds me up no end.
/Leigh
I'm wary of wading into this argument, except in so far as I would add
that in this case the other issue to consider is whether tying
yourself down to implementing Path as a std::vector is a good idea
long-term. That's not an issue to do with inheriting from standard
containers (so I guess it doesn't augment your list above per se), but
it is a general design issue.
As far as inheriting from standard containers go, it's certainly the
case that designs which do so end up violating the Liskov Substitution
Principle. In particular, you can't pass a pointer to an instance of
the subclass to a function which calls delete on a pointer to the
superclass without invoking undefined behaviour. Whether this matters
to you or not is up to you (and I won't offer an opinion here) -- but
that's at least one consideration to bear in mind when deciding.
Cheers,
Stu
I agree. I have updated my website text to the following to (hopefully)
cover all bases:
Some people eschew the derivation of the standard library containers
however there are only 3 main issues to be aware of:
1. As the container has public mutation member functions there is a need
to ensure that the derived class invariant is not broken by calling
these member functions. This is only a problem if the derived class
invariant consists of more than just the container's invariant and
consists of state which depends on the container's state. This should
not be an issue if interface augmentation only is being performed.
2. Does such an "is-a" relationship make sense? Is it important to know
that your derived class "is-a" particular container? If not consider
private inheritance and either wrap container member functions with new
member functions or use using declarations to make particular container
member functions accessible. Again this should not be an issue if
interface augmentation only is being performed.
3. A standard library container destructor is not virtual so you cannot
delete the associated object via a pointer to the container (base class).
http://www.i42.co.uk/stuff/mutable_set.htm
0. Is the class derivation an implementation detail?
0.A. Introduces needless ways that bugs can creep in. E.g., when deriving
from std::vector<T>, then client code may use iterators incorrectly.
0.B. Makes it (much) more costly to change implementation later, with
secondary effects that you can guess at.
an implementation instead of providing the relevant abstraction(s).
abstract "abstraction", which can be hard to translate to actuality.
PS: The existence of a point 0 does not mean that there's not also a point 4, a