Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c++ > #7572 > unrolled thread

Deriving from concrete types like std::list

Started byUrs Thuermann <urs@isnogud.escape.de>
First post2011-07-07 14:02 +0200
Last post2011-07-07 15:51 -0500
Articles 14 — 9 participants

Back to article view | Back to comp.lang.c++


Contents

  Deriving from concrete types like std::list Urs Thuermann <urs@isnogud.escape.de> - 2011-07-07 14:02 +0200
    Re: Deriving from concrete types like std::list Leigh Johnston <leigh@i42.co.uk> - 2011-07-07 14:04 +0100
    Re: Deriving from concrete types like std::list Alexander Bartolich <alexander.bartolich@gmx.at> - 2011-07-07 13:42 +0000
      Re: Deriving from concrete types like std::list Kai-Uwe Bux <jkherciueh@gmx.net> - 2011-07-07 22:23 +0200
        Re: Deriving from concrete types like std::list Alexander Bartolich <alexander.bartolich@gmx.at> - 2011-07-07 22:04 +0000
          Re: Deriving from concrete types like std::list Kai-Uwe Bux <jkherciueh@gmx.net> - 2011-07-08 01:16 +0200
      Re: Deriving from concrete types like std::list red floyd <redfloyd@gmail.com> - 2011-07-08 11:23 -0700
        Re: Deriving from concrete types like std::list "Balog Pal" <pasa@lib.hu> - 2011-07-08 20:30 +0200
          Re: Deriving from concrete types like std::list "Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com> - 2011-07-08 21:16 +0200
          Re: Deriving from concrete types like std::list red floyd <redfloyd@gmail.com> - 2011-07-08 14:53 -0700
            Re: Deriving from concrete types like std::list "Balog Pal" <pasa@lib.hu> - 2011-07-09 10:33 +0200
              Re: Deriving from concrete types like std::list Leigh Johnston <leigh@i42.co.uk> - 2011-07-09 10:59 +0100
    Re: Deriving from concrete types like std::list "Bo Persson" <bop@gmb.dk> - 2011-07-07 19:12 +0200
    Re: Deriving from concrete types like std::list "MikeP" <mp011011@some.org> - 2011-07-07 15:51 -0500

#7572 — Deriving from concrete types like std::list

FromUrs Thuermann <urs@isnogud.escape.de>
Date2011-07-07 14:02 +0200
SubjectDeriving from concrete types like std::list
Message-ID<ygfd3hm1eor.fsf@janus.isnogud.escape.de>
In the 3rd edition of "The C++ Programming Language", Stroustrup
writes in section 25.2 about concrete types

        "Classes such as vector, list [...] are concrete [...]
         Also, [...] none are intended as a base for derivation."

and two pages later

        "A class that does this well is rarely a good candidate for
         the creation of different but related classes through public
         derivation."

Some of the following arguments seems valid, but still I don't see why
I shouldn't derive a new class from list.

As a C++ learning project I'm re-implementing a simple interpreter I
wrote in C almost 20 years ago.

I have an abstract base class Stmt from which I derive several
concrete statement types, one of which is the StmtList.  I want the
StmtList to inherit from Stmt and from list <Stmt *>, so I can call
push_back() on it in the parser or iterate through it to execute the
statement list.  Currently, I have something like this, which works
perfectly and, IMHO, looks quite nice:

        class Stmt {
        public:
                virtual void exec() = 0;
        };

        class WhileStmt : public Stmt {
                Expr *cond;
                Stmt *stmt;
        public:
                virtual void exec() {
                        while (cond->eval())
                                stmt->exec();
                }
        };

        class StmtList : public Stmt, public std::list <Stmt *> {
        public:
                virtual void exec() {
                        const_iterator it;
                        for (it = begin(); it != end(); ++it)
                                (*it)->exec();
                }
        };

        // in the parser
                stmt_list->push_back(stmt);

Is this, according to Stroustrup, the wrong approach?  Instead of
deriving I could have a member of type list <Stmt *> in class StmtList
but iterating would look more cumbersome and I had to implement my own
push_back() that calls push_back() for that list member, etc.  After
all, a statement list *is* a list.  Shouldn't I express this
relationship by derivation?

urs

[toc] | [next] | [standalone]


#7575

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-07-07 14:04 +0100
Message-ID<QpWdnSAF4YBGM4jTnZ2dnUVZ8lWdnZ2d@giganews.com>
In reply to#7572
On 07/07/2011 13:02, Urs Thuermann wrote:
> In the 3rd edition of "The C++ Programming Language", Stroustrup
> writes in section 25.2 about concrete types
>
>          "Classes such as vector, list [...] are concrete [...]
>           Also, [...] none are intended as a base for derivation."
>
> and two pages later
>
>          "A class that does this well is rarely a good candidate for
>           the creation of different but related classes through public
>           derivation."
>
> Some of the following arguments seems valid, but still I don't see why
> I shouldn't derive a new class from list.
>

[snip]

Either Stroustrup is inconsistent or you are taking things out of 
context as he goes on to derive from std::vector in section 25.6.1 
(Adjusting Interfaces).

I have written a short article on this topic here:

http://i42.co.uk/stuff/mutable_set.htm

HTH.

/Leigh

[toc] | [prev] | [next] | [standalone]


#7577

FromAlexander Bartolich <alexander.bartolich@gmx.at>
Date2011-07-07 13:42 +0000
Message-ID<iv4d4f$tkb$1@news.albasani.net>
In reply to#7572
Urs Thuermann wrote:
> [...]
>         class StmtList : public Stmt, public std::list <Stmt *> {
>         public:
>                 virtual void exec() {
>                         const_iterator it;
>                         for (it = begin(); it != end(); ++it)
>                                 (*it)->exec();
>                 }
>         };
>
>         // in the parser
>                 stmt_list->push_back(stmt);
>
> Is this, according to Stroustrup, the wrong approach?  Instead of
> deriving I could have a member of type list <Stmt *> in class StmtList
> but iterating would look more cumbersome and I had to implement my own
> push_back() that calls push_back() for that list member, etc.  After
> all, a statement list *is* a list.  Shouldn't I express this
> relationship by derivation?

Oh, my. There are two arguments against this.

First, the nice one: by deriving from std::list you make the interface
of StmtList extremly large. This gives users of StmtList a lot of ways
to mess with its content. Which means that you cannot deduce the behav-
iour of the class from its methods alone. You always have to check that
nobody out there does nothing stupid with its instances. This totally
defeats the reason we are doing OOP.

The second argument is not so nice: your code does not actually work.
That is, your claim that »a statement list *is* a list« is false.
Hint: how many methods of std::list are declared virtual?

-- 
host -t mx moderators.isc.org

[toc] | [prev] | [next] | [standalone]


#7599

FromKai-Uwe Bux <jkherciueh@gmx.net>
Date2011-07-07 22:23 +0200
Message-ID<iv54kp$mu$1@hoshi.visyn.net>
In reply to#7577
Alexander Bartolich wrote:

> Urs Thuermann wrote:
>> [...]
>>         class StmtList : public Stmt, public std::list <Stmt *> {
>>         public:
>>                 virtual void exec() {
>>                         const_iterator it;
>>                         for (it = begin(); it != end(); ++it)
>>                                 (*it)->exec();
>>                 }
>>         };
>>
>>         // in the parser
>>                 stmt_list->push_back(stmt);
>>
>> Is this, according to Stroustrup, the wrong approach?  Instead of
>> deriving I could have a member of type list <Stmt *> in class StmtList
>> but iterating would look more cumbersome and I had to implement my own
>> push_back() that calls push_back() for that list member, etc.  After
>> all, a statement list *is* a list.  Shouldn't I express this
>> relationship by derivation?
> 
> Oh, my. There are two arguments against this.
> 
> First, the nice one: by deriving from std::list you make the interface
> of StmtList extremly large. This gives users of StmtList a lot of ways
> to mess with its content. Which means that you cannot deduce the behav-
> iour of the class from its methods alone. You always have to check that
> nobody out there does nothing stupid with its instances. This totally
> defeats the reason we are doing OOP.

I do not understand the phrase "deduce the behavior of the class from its 
methods alone". What I can  see is that the implementation via derivation 
from std::list< Stmt* > prevents StmtList from imposing any sort of 
restriction on the content of the list of statements that is considered 
allowed: client code can append and erase in the list at will. What I fail 
to see is why that is wrong.

 
> The second argument is not so nice: your code does not actually work.
> That is, your claim that »a statement list *is* a list« is false.
> Hint: how many methods of std::list are declared virtual?

No methods in std::list< Stmt* > are virtual. But how would that matter? In 
the code shown, there is no attempt at overriding any of them.


Best,

Kai-Uwe Bux

[toc] | [prev] | [next] | [standalone]


#7614

FromAlexander Bartolich <alexander.bartolich@gmx.at>
Date2011-07-07 22:04 +0000
Message-ID<iv5agp$41c$1@news.albasani.net>
In reply to#7599
Kai-Uwe Bux wrote:
> [...]
> I do not understand the phrase "deduce the behavior of the class from its 
> methods alone". What I can  see is that the implementation via derivation 
> from std::list< Stmt* > prevents StmtList from imposing any sort of 
> restriction on the content of the list of statements that is considered 
> allowed: client code can append and erase in the list at will. What I fail 
> to see is why that is wrong.

Oh. That's fundamental. *scratches-head*
Well, let's say that somewhere in your program you have the line

  theStmtList->push_back(foo_stmt);

and a few thousand lines away you have

  theStmtList->push_back(bar_stmt);

Well, actually you have lot's of those lines. In a lot of different
places. But that's OK, you have it under control. Until one day your
program crashes because a stupid user dared to insert an invalid
statement instance. So you add error checking.

  if (theStmtList->isValid(foo_stmt))
    theStmtList->push_back(foo_stmt);
  else
    theStmtList->error("Your foo_stmt stinks.");

Problem is, your program still crashes. Because you forgot to fix
all occurences of that push_back. Fortunately your editor can search
strings across all source files. No problem.

A few months later the program starts to crash again. What happened?
Well, the new team member added a few push_back of his own, but
without error handling. Turns out that the documentation of StmtList
was never updated to include the requirement of calling isValid.

...

A few years later, after lots of changes to validation and error
handling, the documentation is still outdated, and your code is an
inconsistent mess. Some parts use the new error handling, other
parts the old version, and there are also places where the error
handling itself is erroneous. You know you should really have
consolidate this years ago, but now it's too late.

>> [...]
>> The second argument is not so nice: your code does not actually work.
>> That is, your claim that »a statement list *is* a list« is false.
>> Hint: how many methods of std::list are declared virtual?
>
> No methods in std::list< Stmt* > are virtual. But how would that
> matter? In the code shown, there is no attempt at overriding any
> of them.

Oh. That's also fundamental. Let's say you have

  void foo(std::list<Stmt*>* myList)
  {
    // ...
    delete myList;
  }

What happens when you pass an instance of StmtList to foo?
What can you do that programmers working with your code do not fall
into this trap?
What exactly is the advantage of your design, if you cannot actually
use instances of StmtList in place of std::list<Stmt*>?

-- 
host -t mx moderators.isc.org

[toc] | [prev] | [next] | [standalone]


#7618

FromKai-Uwe Bux <jkherciueh@gmx.net>
Date2011-07-08 01:16 +0200
Message-ID<iv5epu$31q$1@hoshi.visyn.net>
In reply to#7614
Alexander Bartolich wrote:

> Kai-Uwe Bux wrote:
>> [...]
>> I do not understand the phrase "deduce the behavior of the class from its
>> methods alone". What I can  see is that the implementation via derivation
>> from std::list< Stmt* > prevents StmtList from imposing any sort of
>> restriction on the content of the list of statements that is considered
>> allowed: client code can append and erase in the list at will. What I
>> fail to see is why that is wrong.
> 
> Oh. That's fundamental. *scratches-head*
> Well, let's say that somewhere in your program you have the line
> 
>   theStmtList->push_back(foo_stmt);
> 
> and a few thousand lines away you have
> 
>   theStmtList->push_back(bar_stmt);
> 
> Well, actually you have lot's of those lines. In a lot of different
> places. But that's OK, you have it under control. Until one day your
> program crashes because a stupid user dared to insert an invalid
> statement instance. So you add error checking.
> 
>   if (theStmtList->isValid(foo_stmt))
>     theStmtList->push_back(foo_stmt);
>   else
>     theStmtList->error("Your foo_stmt stinks.");
> 
> Problem is, your program still crashes. Because you forgot to fix
> all occurences of that push_back. Fortunately your editor can search
> strings across all source files. No problem.
> 
> A few months later the program starts to crash again. What happened?
> Well, the new team member added a few push_back of his own, but
> without error handling. Turns out that the documentation of StmtList
> was never updated to include the requirement of calling isValid.
> 
> ...
> 
> A few years later, after lots of changes to validation and error
> handling, the documentation is still outdated, and your code is an
> inconsistent mess. Some parts use the new error handling, other
> parts the old version, and there are also places where the error
> handling itself is erroneous. You know you should really have
> consolidate this years ago, but now it's too late.

You have a point. What you say is that _very_ likely one would want 
additional restrictions on the content (which std::list< Stmt* > cannot 
enforce). In this particular case, I can sympathise with that: I would 
conjecture that null pointers are _not_ allowed in a StmtList. Such a 
constraint is not enforced by StmtList as it stands.


>>> [...]
>>> The second argument is not so nice: your code does not actually work.
>>> That is, your claim that »a statement list *is* a list« is false.
>>> Hint: how many methods of std::list are declared virtual?
>>
>> No methods in std::list< Stmt* > are virtual. But how would that
>> matter? In the code shown, there is no attempt at overriding any
>> of them.
> 
> Oh. That's also fundamental. Let's say you have
> 
>   void foo(std::list<Stmt*>* myList)
>   {
>     // ...
>     delete myList;
>   }
> 
> What happens when you pass an instance of StmtList to foo?

UB.

> What can you do that programmers working with your code do not fall
> into this trap?

You document it - just as the many instances of UB in the standard library.

> What exactly is the advantage of your design, if you cannot actually
> use instances of StmtList in place of std::list<Stmt*>?

Actually, you can. E.g.:

  void clear_list ( std::list< Stmt* > & stmt_list ) { ... }
  ...
  StmtList stmt_list;
  ...
  foo( stmt_list );

What is UB is just deleting a StmtList* through std::list< Stmt* >*. The 
claim that, therefore, you cannot use instances of StmtList in place of 
std::list< Stmt* > is too strong.

Whether the restriction on deletion is a serious matter depends very much on 
local coding guidelines and code base.


Best,

Kai-Uwe Bux

[toc] | [prev] | [next] | [standalone]


#7653

Fromred floyd <redfloyd@gmail.com>
Date2011-07-08 11:23 -0700
Message-ID<c5eea6b8-8b0a-429d-a69e-092ca7eff931@e35g2000yqc.googlegroups.com>
In reply to#7577
On Jul 7, 6:42 am, Alexander Bartolich <alexander.bartol...@gmx.at>
wrote:
> Urs Thuermann wrote:
> > [...]
> >         class StmtList : public Stmt, public std::list <Stmt *> {
> >         public:
> >                 virtual void exec() {
> >                         const_iterator it;
> >                         for (it = begin(); it != end(); ++it)
> >                                 (*it)->exec();
> >                 }
> >         };
>
> >         // in the parser
> >                 stmt_list->push_back(stmt);
>
> > Is this, according to Stroustrup, the wrong approach?  Instead of
> > deriving I could have a member of type list <Stmt *> in class StmtList
> > but iterating would look more cumbersome and I had to implement my own
> > push_back() that calls push_back() for that list member, etc.  After
> > all, a statement list *is* a list.  Shouldn't I express this
> > relationship by derivation?
>
> Oh, my. There are two arguments against this.
>
> First, the nice one: by deriving from std::list you make the interface
> of StmtList extremly large. This gives users of StmtList a lot of ways
> to mess with its content. Which means that you cannot deduce the behav-
> iour of the class from its methods alone. You always have to check that
> nobody out there does nothing stupid with its instances. This totally
> defeats the reason we are doing OOP.
>

While many of the STL classes are not intended for *PUBLIC*
derivation,
private derivation seems OK.  Note, for example,
std::priority_queue<>,
which has protected members.  However, it's destructor is not virtual,
so you probably shouldn't publicly derive.

[toc] | [prev] | [next] | [standalone]


#7654

From"Balog Pal" <pasa@lib.hu>
Date2011-07-08 20:30 +0200
Message-ID<iv7ice$2m8k$1@news.ett.com.ua>
In reply to#7653
"red floyd" <redfloyd@gmail.com>
>While many of the STL classes are not intended for *PUBLIC*
> derivation, private derivation seems OK.

You don't have virtuals to override. So then what is the benefit of 
derivation over just having the thing member rather than base class?

[toc] | [prev] | [next] | [standalone]


#7655

From"Alf P. Steinbach /Usenet" <alf.p.steinbach+usenet@gmail.com>
Date2011-07-08 21:16 +0200
Message-ID<iv7ku4$uvq$1@dont-email.me>
In reply to#7654
* Balog Pal, on 08.07.2011 20:30:
> "red floyd" <redfloyd@gmail.com>
>> While many of the STL classes are not intended for *PUBLIC*
>> derivation, private derivation seems OK.
>
> You don't have virtuals to override. So then what is the benefit of derivation
> over just having the thing member rather than base class?

If you had read further on in Red Floyd's short article, you'd have seen one reason.

The above seems therefore to be an automatic knee jerk reaction, and given that, 
what would be the point of trying to explain this?

No point, I think.


Cheers & hth.,

- Alf

-- 
blog at <url: http://alfps.wordpress.com>

[toc] | [prev] | [next] | [standalone]


#7661

Fromred floyd <redfloyd@gmail.com>
Date2011-07-08 14:53 -0700
Message-ID<fc510dfe-6d41-4408-bd31-be75daa2f22c@gh5g2000vbb.googlegroups.com>
In reply to#7654
On Jul 8, 11:30 am, "Balog Pal" <p...@lib.hu> wrote:
> "red floyd" <redfl...@gmail.com>
>
> >While many of the STL classes are not intended for *PUBLIC*
> > derivation, private derivation seems OK.
>
> You don't have virtuals to override. So then what is the benefit of
> derivation over just having the thing member rather than base class?

How about getting access to those protected members?

And the bit about virtual destructors has been beaten
to death here in c.l.c++.  You don't want to risk having
your class destroyed by only the base class destructor.

In my case, for example, I needed a priority queue, with the ability
to delete an arbitrary element within said queue (I was using it for
a timer list).  std::priority_queue only allows deletion from the
front of the queue.  So I used private derivation to get access to
the protected queue data.

[toc] | [prev] | [next] | [standalone]


#7674

From"Balog Pal" <pasa@lib.hu>
Date2011-07-09 10:33 +0200
Message-ID<iv93pc$31d9$1@news.ett.com.ua>
In reply to#7661
"red floyd" <redfloyd@gmail.com>
>How about getting access to those protected members?

Look, the guideline is pretty simple. You derive if you must. (for access to 
virtuals, protected, etc).  Otherwise prefer not to derive.

We were talking about STL in general. Which  have no virtuals, and protected 
members are very rare. I very much doubt I'd want ever to derive from 
std::priority_queue.  If I need some specific elaborated queue I'd just 
write it directly, the way I see fit. Using ONE carefully chosen container 
that does the job well.  I would not struggle with excess genericity. Or 
constraints from STL.

>In my case, for example, I needed a priority queue, with the ability
>to delete an arbitrary element within said queue (I was using it for
>a timer list).  std::priority_queue only allows deletion from the
>front of the queue.  So I used private derivation to get access to
>the protected queue data.

And did you actually use it with all kinds of collections?


[toc] | [prev] | [next] | [standalone]


#7678

FromLeigh Johnston <leigh@i42.co.uk>
Date2011-07-09 10:59 +0100
Message-ID<PJKdnRJYwv0Ou4XTnZ2dnUVZ8o2dnZ2d@giganews.com>
In reply to#7674
On 09/07/2011 09:33, Balog Pal wrote:
> "red floyd" <redfloyd@gmail.com>
>> How about getting access to those protected members?
>
> Look, the guideline is pretty simple. You derive if you must. (for
> access to virtuals, protected, etc). Otherwise prefer not to derive.
>
> We were talking about STL in general. Which have no virtuals, and
> protected members are very rare. I very much doubt I'd want ever to

[snip]

std::iterator has no virtuals (including no virtual dtor) or protected 
members but you are *supposed* to derive from it; similarly deriving 
from the STL containers *can be* perfectly fine as long as you can 
justify it (as it is often a code smell).

/Leigh

[toc] | [prev] | [next] | [standalone]


#7593

From"Bo Persson" <bop@gmb.dk>
Date2011-07-07 19:12 +0200
Message-ID<97m7nuFo1uU1@mid.individual.net>
In reply to#7572
Urs Thuermann wrote:
>
>        class StmtList : public Stmt, public std::list <Stmt *> {
>        public:
>                virtual void exec() {
>                        const_iterator it;
>                        for (it = begin(); it != end(); ++it)
>                                (*it)->exec();
>                }
>        };
>
>        // in the parser
>                stmt_list->push_back(stmt);
>
> Is this, according to Stroustrup, the wrong approach?  Instead of
> deriving I could have a member of type list <Stmt *> in class
> StmtList but iterating would look more cumbersome and I had to
> implement my own push_back() that calls push_back() for that list
> member, etc.  After all, a statement list *is* a list.  Shouldn't I
> express this relationship by derivation?
>

Having the list as a member seems a much better idea.

How hard is it to write l.begin() instead of begin()?


Bo Persson

[toc] | [prev] | [next] | [standalone]


#7602

From"MikeP" <mp011011@some.org>
Date2011-07-07 15:51 -0500
Message-ID<iv5693$s0s$1@dont-email.me>
In reply to#7572
You will find it difficult to convince that your class IS-A list rather 
than it HAS-A list. Even more dificult to convince that it 
IS-AN-STL-LIST.

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c++


csiph-web