[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

avoid inheritance from std::map

Hicham Mouline

12/15/2008 3:04:00 PM

we have bits of code where we inherit from std::map.

Why again shouldn't one inherit from STL containers in the C++ runtimes?


The proposal is to write a
template <typename Key, typename Value>
class MapDecorator {
//replicate map's interface here and simply forward all calls to m_
private:
std::map<Key,Value> m_;
}
and then inherit from MapDecorator <....>


I agree one shouldn't inherit from std::map, though I don't remember why,
However the above seems to me redundant?

regards,


15 Answers

AnonMail2005@gmail.com

12/15/2008 3:12:00 PM

0

On Dec 15, 10:03 am, "Hicham Mouline" <hic...@mouline.org> wrote:
> we have bits of code where we inherit from std::map.
>
> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>
> The proposal is to write a
> template <typename Key, typename Value>
> class MapDecorator {
>   //replicate map's interface here and simply forward all calls to m_
> private:
>   std::map<Key,Value> m_;}
>
> and then inherit from MapDecorator <....>
>
> I agree one shouldn't inherit from std::map, though I don't remember why,
> However the above seems to me redundant?
>
> regards,

Ths simplest answer is that std::map does not have a virtual
destructor. So if you are using pointers to the base class and you
delete them, the derived class detructor will not be properly called.

HTH

Rolf Magnus

12/15/2008 3:40:00 PM

0

AnonMail2005@gmail.com wrote:

> On Dec 15, 10:03 am, "Hicham Mouline" <hic...@mouline.org> wrote:
>> we have bits of code where we inherit from std::map.
>>
>> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>>
>> The proposal is to write a
>> template <typename Key, typename Value>
>> class MapDecorator {
>> //replicate map's interface here and simply forward all calls to m_
>> private:
>> std::map<Key,Value> m_;}
>>
>> and then inherit from MapDecorator <....>
>>
>> I agree one shouldn't inherit from std::map, though I don't remember why,
>> However the above seems to me redundant?
>>
>> regards,
>
> Ths simplest answer is that std::map does not have a virtual
> destructor. So if you are using pointers to the base class and you
> delete them, the derived class detructor will not be properly called.

That's the reason I'm hearing quite often. But which of those classes do you
actually use polymorphically? I never allocated an std::map dynamically. Why
would I? And why would I want to have a pointer to std::map pointing to an
object of a class derived from it? Since I don't see any reason for doing
that, I also don't see any reason to not derive from standard containers.


Juha Nieminen

12/15/2008 9:44:00 PM

0

Hicham Mouline wrote:
> we have bits of code where we inherit from std::map.
>
> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>
>
> The proposal is to write a
> template <typename Key, typename Value>
> class MapDecorator {
> //replicate map's interface here and simply forward all calls to m_
> private:
> std::map<Key,Value> m_;
> }
> and then inherit from MapDecorator <....>
>
>
> I agree one shouldn't inherit from std::map, though I don't remember why,
> However the above seems to me redundant?

It may be a question of abstraction.

By inheriting directly from std::map you may be (rather ironically)
breaking good object-oriented design principles related to modularity
and abstraction. That's because you are fully exposing the data
container you are using in your class (as your class *is* the data
container).

Sometimes that doesn't matter. If your class really *is* a map, just
with added functionality, then it usually is ok.

However, if your class is trying to represent some higher concept,
then by exposing to the outside that it really is a std::map, you are
lessening its abstraction. In some cases this can make it very difficult
to, for example, change the data container implementation in the future.

Usually you don't really want to replicate the entire std::map
interface in your class. You want to implement what you class can do.
Whether it internally uses an std::map or something else (such as a
hash_map or whatever) should be a hidden implementation detail.

Kai-Uwe Bux

12/16/2008 12:56:00 AM

0

Rolf Magnus wrote:

> AnonMail2005@gmail.com wrote:
>
>> On Dec 15, 10:03 am, "Hicham Mouline" <hic...@mouline.org> wrote:
>>> we have bits of code where we inherit from std::map.
>>>
>>> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>>>
>>> The proposal is to write a
>>> template <typename Key, typename Value>
>>> class MapDecorator {
>>> //replicate map's interface here and simply forward all calls to m_
>>> private:
>>> std::map<Key,Value> m_;}
>>>
>>> and then inherit from MapDecorator <....>
>>>
>>> I agree one shouldn't inherit from std::map, though I don't remember
>>> why, However the above seems to me redundant?
>>>
>>> regards,
>>
>> Ths simplest answer is that std::map does not have a virtual
>> destructor. So if you are using pointers to the base class and you
>> delete them, the derived class detructor will not be properly called.
>
> That's the reason I'm hearing quite often. But which of those classes do
> you actually use polymorphically? I never allocated an std::map
> dynamically. Why would I? And why would I want to have a pointer to
> std::map pointing to an object of a class derived from it? Since I don't
> see any reason for doing that, I also don't see any reason to not derive
> from standard containers.

The best reason I heard is about unwanted implicit conversions and matching
functions. Suppose, you have a function template

template < typename T >
std::vector<T> reverse ( std::vector<T> const & v ) {
...
}

then the function will match any class derived from std::vector, but the
return type will be std::vector<> and not the derived class. The standard
case would likely be not what you want.

Arguably, the signature of the function reverse() is at fault, but
inheritance from std::vector gets to take the blame. In any case, functions
like the above and inheritance from containers do not mix all that well. To
ease the problem, one could provide a constructor of the derived class from
a vector, but that that would preclude the existence of other data members.
Also, one cannot impose a restricted invariant (like all elements of the
vector are _even_ numbers). By and large, that leaves only two reasonable
scenarios for inheritance:

a) For convenience by extending the interface. Here, free standing functions
are usually better.

b) As a way to distingush types. E.g., one could define

template < typename ArithmeticType >
struct linalg_vector : public std::vector< ArithmeticType > {
// some constructors
};

template < typename LetterType >
struct word : public std::vector< LetterType > {
// some constructor
};

The nice thing is that now one could overload operator+ to mean elementwise
addition for linalg_vector and concatenation for word.


Best

Kai-Uwe Bux

Hicham Mouline

12/16/2008 8:55:00 AM

0


"Juha Nieminen" <nospam@thanks.invalid> wrote in message
news:ZcA1l.485$M21.77@read4.inet.fi...
> Hicham Mouline wrote:
>> we have bits of code where we inherit from std::map.
>>
>> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>>
>>
>> The proposal is to write a
>> template <typename Key, typename Value>
>> class MapDecorator {
>> //replicate map's interface here and simply forward all calls to m_
>> private:
>> std::map<Key,Value> m_;
>> }
>> and then inherit from MapDecorator <....>
>>
>>
>> I agree one shouldn't inherit from std::map, though I don't remember why,
>> However the above seems to me redundant?
>
> It may be a question of abstraction.
>
> By inheriting directly from std::map you may be (rather ironically)
> breaking good object-oriented design principles related to modularity
> and abstraction. That's because you are fully exposing the data
> container you are using in your class (as your class *is* the data
> container).
>
> Sometimes that doesn't matter. If your class really *is* a map, just
> with added functionality, then it usually is ok.
>
> However, if your class is trying to represent some higher concept,
> then by exposing to the outside that it really is a std::map, you are
> lessening its abstraction. In some cases this can make it very difficult
> to, for example, change the data container implementation in the future.
>
> Usually you don't really want to replicate the entire std::map
> interface in your class. You want to implement what you class can do.
> Whether it internally uses an std::map or something else (such as a
> hash_map or whatever) should be a hidden implementation detail.


If we just replicate part of map's public interface in MapDecorator<> above,
and inherit from MapDecorator<>, then aren't we in the same situation?

MapDecorator also hasn't been designed to be a base class.

Should we change tjhe map's interface that we replicate to be virtual
functions insead,
and to add a virtual destructor,
then we'd get rid of 1 reason why we shouldn't inherit from STL containers.

Our classes are curves of datapoints (x,y)


James Kanze

12/16/2008 12:35:00 PM

0

On Dec 16, 1:56 am, Kai-Uwe Bux <jkherci...@gmx.net> wrote:

[...]
> By and large, that leaves only two reasonable
> scenarios for inheritance:

> a) For convenience by extending the interface. Here, free
> standing functions are usually better.

Usually doesn't mean always. I don't derive from a standard
container that often, but when I do, the most frequent reason is
to add constructors, i.e. a pre-filled vector or map. (Usually,
it's quite adequate to use the two iterator constructor of the
container, copying some array of POD-types into the container,
possibly with an implicit conversion. But deriving provides the
ultimate flexibility, supporting algorithmic initialization.)

> b) As a way to distingush types. E.g., one could define

> template < typename ArithmeticType >
> struct linalg_vector : public std::vector< ArithmeticType > {
> // some constructors
> };

> template < typename LetterType >
> struct word : public std::vector< LetterType > {
> // some constructor
> };

> The nice thing is that now one could overload operator+ to
> mean elementwise addition for linalg_vector and concatenation
> for word.

Conceptually, the correct solution here is to use containment,
and redefine the interface in the containing class.
Practically, I don't know if it's really worth all that extra
work.

--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

AnonMail2005@gmail.com

12/16/2008 2:55:00 PM

0

On Dec 16, 3:54 am, "Hicham Mouline" <hic...@mouline.org> wrote:
> "Juha Nieminen" <nos...@thanks.invalid> wrote in message
>
> news:ZcA1l.485$M21.77@read4.inet.fi...
>
>
>
>
>
> > Hicham Mouline wrote:
> >> we have bits of code where we inherit from std::map.
>
> >> Why again shouldn't one inherit from STL containers in the C++ runtimes?
>
> >> The proposal is to write a
> >> template <typename Key, typename Value>
> >> class MapDecorator {
> >>   //replicate map's interface here and simply forward all calls to m_
> >> private:
> >>   std::map<Key,Value> m_;
> >> }
> >> and then inherit from MapDecorator <....>
>
> >> I agree one shouldn't inherit from std::map, though I don't remember why,
> >> However the above seems to me redundant?
>
> >  It may be a question of abstraction.
>
> >  By inheriting directly from std::map you may be (rather ironically)
> > breaking good object-oriented design principles related to modularity
> > and abstraction. That's because you are fully exposing the data
> > container you are using in your class (as your class *is* the data
> > container).
>
> >  Sometimes that doesn't matter. If your class really *is* a map, just
> > with added functionality, then it usually is ok.
>
> >  However, if your class is trying to represent some higher concept,
> > then by exposing to the outside that it really is a std::map, you are
> > lessening its abstraction. In some cases this can make it very difficult
> > to, for example, change the data container implementation in the future.
>
> >  Usually you don't really want to replicate the entire std::map
> > interface in your class. You want to implement what you class can do.
> > Whether it internally uses an std::map or something else (such as a
> > hash_map or whatever) should be a hidden implementation detail.
>
> If we just replicate part of map's public interface in MapDecorator<> above,
> and inherit from MapDecorator<>, then aren't we in the same situation?
>
> MapDecorator also hasn't been designed to be a base class.
>
> Should we change tjhe map's interface that we replicate to be virtual
> functions insead,
> and to add a virtual destructor,
> then we'd get rid of 1 reason why we shouldn't inherit from STL containers.
>
> Our classes are curves of datapoints (x,y)- Hide quoted text -
>
> - Show quoted text -

The correct way to do this IMHO (and picking up on Juha's
suggestions)...

Make MapDecorator *contain* a map as part of it's (private) data and
expose what you need of it's functionality via accessor functions.
This gets you encapsulation.

If MapDecorator is going to function as a base class for derived
classes, then, and only then, make it have a virtual destructor. Make
other functions virtual (or not) only if needed. There are clear
guidelines as to whether you make a non-destructor function non-
virtual, virtual, or pure-virtual. I'm sure they're in the FAQ but I
learned the guidelines from Meyers.

Also, I would rename MapDecorator to something more domain specific so
it's clearer.

HTH

Jeff Schwab

12/16/2008 5:13:00 PM

0

Hicham Mouline wrote:
> I agree one shouldn't inherit from std::map, though I don't remember why,

Others have pointed out the main problems with public inheritance from
std::map: (1) deletion of the derived type through a pointer-to-base
will cause UB (since std::map has a non-virtual destructor), and (2)
undesired implicit conversions will become possible.

The best solution (IMO) is just to derive privately from std::map, and
provide using-declarations for desired parts of the interface. This is
a lot less syntax than using a member map with a bunch of nearly
identical forwarding calls.

Kai-Uwe Bux

12/16/2008 10:34:00 PM

0

James Kanze wrote:

>> b) As a way to distingush types. E.g., one could define
>
>> template < typename ArithmeticType >
>> struct linalg_vector : public std::vector< ArithmeticType > {
>> // some constructors
>> };
>
>> template < typename LetterType >
>> struct word : public std::vector< LetterType > {
>> // some constructor
>> };
>
>> The nice thing is that now one could overload operator+ to
>> mean elementwise addition for linalg_vector and concatenation
>> for word.
>
> Conceptually, the correct solution here is to use containment,
> and redefine the interface in the containing class.

Which one is more correct actually depends on your code base and your
intentions. If there is a reversal function

template < typename T >
void reverse ( std::vector<T> & vec );

then you have to answer the question whether it should match a word or not.
Depending on the answer, inheritance may or may not be the more correct way
to go.

> Practically, I don't know if it's really worth all that extra
> work.

That's an additional consideration.


Best

Kai-Uwe Bux

James Kanze

12/16/2008 11:06:00 PM

0

On Dec 16, 11:33 pm, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
> James Kanze wrote:
> >> b) As a way to distingush types. E.g., one could define

> >> template < typename ArithmeticType >
> >> struct linalg_vector : public std::vector< ArithmeticType > {
> >> // some constructors
> >> };

> >> template < typename LetterType >
> >> struct word : public std::vector< LetterType > {
> >> // some constructor
> >> };

> >> The nice thing is that now one could overload operator+ to
> >> mean elementwise addition for linalg_vector and
> >> concatenation for word.

> > Conceptually, the correct solution here is to use
> > containment, and redefine the interface in the containing
> > class.

> Which one is more correct actually depends on your code base
> and your intentions. If there is a reversal function

> template < typename T >
> void reverse ( std::vector<T> & vec );

> then you have to answer the question whether it should match a
> word or not. Depending on the answer, inheritance may or may
> not be the more correct way to go.

Conceptually, it shouldn't. It says it wants an std::vector<T>.
std::vector<T> is not some arbitrary interface; it's a concrete
class.

Practically, of course, design is often the art of compromize,
and rather than write a completely new function, one might
prefer inheritance for that reason as well. (I'm not really
sure that "conceptually" is the word I'm looking for here. I
mean something that is really pure and abstract, independently
of any practical considerations.)

--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34