Vector of abstract classes filled with derived classes ?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Manuel

    Vector of abstract classes filled with derived classes ?

    Hi!

    If I've a vector filled with abstract classes, can I push in it the
    derived classes too? Even if derived classes have new methods?

    I've done some experiments, and it seem I can push the derived classes,
    but I can use them only calling the methods declared in abstract class
    (the new methods declared only in derived classes return an error).

    Please take a look to my code (hey, this is not an homework!
    I'm studying C++ to port this opensource project from python to C++
    http://www.makehuman.org):

    This is my abstract class:

    --------------------------------------------
    class mhwidget
    {

    public:
    virtual void draw()= 0;
    virtual ~mhwidget() {}

    };
    --------------------------------------------

    In another file, I've defined a vector for this class:

    --------------------------------------------
    std::vector<mhw idget*> widgetList;
    --------------------------------------------

    I fill this vector, and use the "draw()" method,
    in this way:

    --------------------------------------------
    //Put widget into container
    void mhcontainer::ad dWidget(mhwidge t* w)
    {
    widgetList.push _back(w);
    }

    //Draw all widgets
    void mhcontainer::dr awAll()
    {

    std::vector<mhw idget*>::iterat or it = widgetList.begi n();
    while(it != widgetList.end( ))
    {
    (*it++)->draw();
    }
    }
    --------------------------------------------

    I've written an example of derived class (this is only the header):

    ---------------------------------------------
    class square : public mhwidget
    {
    public:
    virtual void draw();
    };
    ---------------------------------------------

    and this seem to work (from application/client code):

    ---------------------------------------------
    mhcontainer contn;
    contn.addWidget (new square);
    contn.drawAll() ;
    ---------------------------------------------

    but I'm not sure it's a clean C++ code.

    If I add a new method in "square" and try to use it, like
    while(it != widgetList.end( ))
    {
    (*it++)->draw();
    (*it++)->newMethod();
    }
    it return an error, but this make sense...

    thx,

    Manuel
  • Shezan Baig

    #2
    Re: Vector of abstract classes filled with derived classes ?

    Manuel wrote:[color=blue]
    > This is my abstract class:
    >
    > --------------------------------------------
    > class mhwidget
    > {
    >
    > public:
    > virtual void draw()= 0;
    > virtual ~mhwidget() {}
    >
    > };
    > --------------------------------------------
    >
    > In another file, I've defined a vector for this class:
    >
    > --------------------------------------------
    > std::vector<mhw idget*> widgetList;
    > --------------------------------------------
    >
    > I've written an example of derived class (this is only the header):
    >
    > ---------------------------------------------
    > class square : public mhwidget
    > {
    > public:
    > virtual void draw();
    > };
    > ---------------------------------------------
    >
    > and this seem to work (from application/client code):
    >
    > ---------------------------------------------
    > mhcontainer contn;
    > contn.addWidget (new square);
    > contn.drawAll() ;
    > ---------------------------------------------
    >
    > but I'm not sure it's a clean C++ code.[/color]


    It's reasonable, just make sure you delete all elements in your
    container. Or better yet, use a vector of shared pointer to the base
    class (instead of normal pointer).

    [color=blue]
    > If I add a new method in "square" and try to use it, like
    > while(it != widgetList.end( ))
    > {
    > (*it++)->draw();
    > (*it++)->newMethod();
    > }
    > it return an error, but this make sense...[/color]


    Yes, this is an error, because widgetList is a vector of <mhwidget*>,
    not <square*>. Although you have a 'square' object inside, you might
    also have *other* derived types, e.g., 'circle' which does not have
    'newMethod'. So when you have a vector<mhwidget *>, you can only call
    methods from 'mhwidget'.

    One solution is to use dynamic cast:

    square *sq = dynamic_cast<sq uare*>(*it);
    if (sq) {
    sq->newMethod();
    }

    So, if the item in the vector happens to be a square, then invoke
    'newMethod' using the pointer obtained from the dynamic cast. If it is
    not a square, then the item will be ignored. But this is not very good
    way to design your class heirarchy, because you are making users of
    'widgetList' know about one of the possible implementations of
    'mhwidget' and code specifically for that implementation. It is better
    to remain general instead of being specific.

    Hope this helps,
    -shez-

    Comment

    • Rolf Magnus

      #3
      Re: Vector of abstract classes filled with derived classes ?

      Manuel wrote:
      [color=blue]
      > Hi!
      >
      > If I've a vector filled with abstract classes,[/color]

      You cannot fill a vector with classes, only with objects. You also can't
      fill it with instances of abstract classes, so I assum you are talking
      about pointers.
      [color=blue]
      > can I push in it the derived classes too?[/color]

      Yes.
      [color=blue]
      > Even if derived classes have new methods?[/color]

      Yes.
      [color=blue]
      > I've done some experiments, and it seem I can push the derived classes,
      > but I can use them only calling the methods declared in abstract class
      > (the new methods declared only in derived classes return an error).[/color]

      Well, how would the compiler know at compile time (i.e. when it sees your
      function call) which actual class the object you are accessing will be at
      runtime? How should it know which member functions are available?
      [color=blue]
      > Please take a look to my code (hey, this is not an homework!
      > I'm studying C++ to port this opensource project from python to C++
      > http://www.makehuman.org):
      >
      > This is my abstract class:
      >
      > --------------------------------------------
      > class mhwidget
      > {
      >
      > public:
      > virtual void draw()= 0;
      > virtual ~mhwidget() {}
      >
      > };
      > --------------------------------------------
      >
      > In another file, I've defined a vector for this class:
      >
      > --------------------------------------------
      > std::vector<mhw idget*> widgetList;[/color]

      So it is - as I assumed - a vector of pointers to mhwidget.
      [color=blue]
      > --------------------------------------------
      >
      > I fill this vector, and use the "draw()" method,
      > in this way:
      >
      > --------------------------------------------
      > //Put widget into container
      > void mhcontainer::ad dWidget(mhwidge t* w)
      > {
      > widgetList.push _back(w);
      > }
      >
      > //Draw all widgets
      > void mhcontainer::dr awAll()
      > {
      >
      > std::vector<mhw idget*>::iterat or it = widgetList.begi n();
      > while(it != widgetList.end( ))
      > {
      > (*it++)->draw();
      > }
      > }
      > --------------------------------------------
      >
      > I've written an example of derived class (this is only the header):
      >
      > ---------------------------------------------
      > class square : public mhwidget
      > {
      > public:
      > virtual void draw();
      > };
      > ---------------------------------------------
      >
      > and this seem to work (from application/client code):
      >
      > ---------------------------------------------
      > mhcontainer contn;
      > contn.addWidget (new square);
      > contn.drawAll() ;
      > ---------------------------------------------
      >
      > but I'm not sure it's a clean C++ code.[/color]

      This looks fine.
      [color=blue]
      > If I add a new method in "square" and try to use it, like
      > while(it != widgetList.end( ))
      > {
      > (*it++)->draw();
      > (*it++)->newMethod();
      > }[/color]

      Are you sure that you want to call draw() only for every second object?
      [color=blue]
      > it return an error, but this make sense...[/color]

      What is the programm supposed to do with those objects that are not of
      dynamic type "square"? If you're accessing the object through mhwidget's
      interface, only that is available.
      If you want newMethod() be called where draw() is called, simply call it
      from square::draw().
      An alternative (which is in many situations considered as a design flaw in
      C++) would be to use a dynamic cast, like:

      while(it != widgetList.end( ))
      {
      (*it++)->draw();

      square* sq = dynamic_cast<sq uare*>(*it++);
      if (sq)
      sq->newMethod();
      }

      Comment

      • Mateusz Łoskot

        #4
        Re: Vector of abstract classes filled with derived classes ?

        Manuel wrote:[color=blue]
        > Hi!
        >
        > If I've a vector filled with abstract classes,[/color]

        You can not fill any container with abstract classes and with classes
        too. But you can fill a container with instances of (non-abstract)
        classes. Second, you can fill container with references to instances of
        concrete classes through pointers to abstract class.

        [color=blue]
        > can I push in it thederived classes too?[/color]

        Only when you store C++ references or pointers to abstarct type.

        [color=blue]
        > Even if derived classes have new methods?[/color]

        It doesn't matter.
        [color=blue]
        > I've done some experiments, and it seem I can push the derived
        > classes,[/color]

        Not classes but objects.
        [color=blue]
        > but I can use them only calling the methods declared in
        > abstract class (the new methods declared only in derived classes
        > return an error).[/color]

        Yes, abstract class is an interface shared with concrete classes.
        In order to get access to some specific operations for concrete type
        then use "cast pointer-to-base type to pointer-to-derived type".
        [color=blue]
        > In another file, I've defined a vector for this class:
        >
        > std::vector<mhw idget*> widgetList;[/color]

        This is vector in which you can store elements of type of
        pointer-to-class-mhwidget. Here, the stored type is a pointer, not a class.
        [color=blue]
        > I fill this vector, and use the "draw()" method, in this way:
        >
        > -------------------------------------------- //Put widget into
        > container void mhcontainer::ad dWidget(mhwidge t* w) {
        > widgetList.push _back(w); }
        >
        > //Draw all widgets void mhcontainer::dr awAll() {
        >
        > std::vector<mhw idget*>::iterat or it = widgetList.begi n(); while(it !=
        > widgetList.end( )) { (*it++)->draw(); } }
        > --------------------------------------------
        >
        > I've written an example of derived class (this is only the header):
        >
        > --------------------------------------------- class square : public
        > mhwidget { public: virtual void draw(); };
        > ---------------------------------------------
        >
        > and this seem to work (from application/client code):
        >
        > --------------------------------------------- mhcontainer contn;
        > contn.addWidget (new square); contn.drawAll() ;
        > ---------------------------------------------
        >
        > but I'm not sure it's a clean C++ code.[/color]

        From the first sight it looks good (thought, I've not
        tried to compile it). Dynamic polymorphism can be achived through
        abstract types with virtual operations.
        [color=blue]
        > If I add a new method in "square" and try to use it, like while(it !=
        > widgetList.end( )) { (*it++)->draw(); (*it++)->newMethod(); } it
        > return an error, but this make sense...[/color]


        You need to do static_cast<som e_derived_class *>(*it)->newMethod();

        Cheers
        --
        Mateusz Łoskot

        Comment

        • Manuel

          #5
          Re: Vector of abstract classes filled with derived classes ?

          Thanks to all.
          The dynamic cast is not needed, because the widget interface is
          sufficient for this simple GUI. The only important thing is using
          pointers to abstract widget to store the derived objs too, is ok.

          Thanks!

          Manuel

          Comment

          • Ben Pope

            #6
            Re: Vector of abstract classes filled with derived classes ?

            Manuel wrote:[color=blue]
            > Thanks to all.
            > The dynamic cast is not needed, because the widget interface is
            > sufficient for this simple GUI. The only important thing is using
            > pointers to abstract widget to store the derived objs too, is ok.[/color]

            That's probably the most common way of storing GUI objects (by pointer),
            and sticking pointers to base classes in a container is pretty much the
            only thing you CAN do when you need to access them polymorphically .

            The problem is often that of who does the cleaning up (deleting the
            pointer) and when... this is often managed by either a container of
            boost::shared_p tr<objectType> or one of the boost ptr_containers.

            However, in a GUI situation, often the parent "window" deals with the
            cleanup, so you're probably ok.

            Ben Pope
            --
            I'm not just a number. To many, I'm known as a string...

            Comment

            • Axter

              #7
              Re: Vector of abstract classes filled with derived classes ?

              Ben Pope wrote:[color=blue]
              > Manuel wrote:[color=green]
              > > Thanks to all.
              > > The dynamic cast is not needed, because the widget interface is
              > > sufficient for this simple GUI. The only important thing is using
              > > pointers to abstract widget to store the derived objs too, is ok.[/color]
              >
              > That's probably the most common way of storing GUI objects (by pointer),
              > and sticking pointers to base classes in a container is pretty much the
              > only thing you CAN do when you need to access them polymorphically .
              >
              > The problem is often that of who does the cleaning up (deleting the
              > pointer) and when... this is often managed by either a container of
              > boost::shared_p tr<objectType> or one of the boost ptr_containers.[/color]

              I recommend using a cow_ptr smart pointer over using the boost
              ptr_containers.
              http:://code.axter.com/cow_ptr.h
              With the cow_ptr you don't have to add a clone function to the target
              type, and the cow_ptr can be use with any of the main STL containers.

              Also, the boost ptr_containers causes a lot of compiler warnings when
              using it with compilers like VC++ 7.x, and some of the boost pointer
              containers will not compile on VC++ 6.0, or other prestandard
              compilers.

              The cow_ptr is more portable, and can be used more generically.

              Comment

              • Earl Purple

                #8
                Re: Vector of abstract classes filled with derived classes ?


                Axter wrote:[color=blue]
                > I recommend using a cow_ptr smart pointer over using the boost
                > ptr_containers.
                > http:://code.axter.com/cow_ptr.h
                > With the cow_ptr you don't have to add a clone function to the target
                > type, and the cow_ptr can be use with any of the main STL containers.
                >
                > Also, the boost ptr_containers causes a lot of compiler warnings when
                > using it with compilers like VC++ 7.x, and some of the boost pointer
                > containers will not compile on VC++ 6.0, or other prestandard
                > compilers.
                >
                > The cow_ptr is more portable, and can be used more generically.[/color]

                Another option is not to use smart-pointers at all, but to use a
                parent-child relationship. That does mean there is a slight intrusion,
                i.e. your child has to be derived from a specific base class that knows
                of an abstract "parent" type. The child is created adding itself to its
                parent. When the parent is destroyed it deletes all its children.

                The parent class needs only be implemented once. The parent itself does
                not have to be a child but in the case of GUI programming they often
                will be.

                parent can implement with a vector of weak pointers and its destructor
                can delete all the elements in the list. parent should obviously be
                non-copyable and non-assignable.

                By the way, you can write specific derivatives of parent that handle
                their children in different ways. (parent itself should be abstract).
                And you can have different types.

                Comment

                • Manuel

                  #9
                  Re: Vector of abstract classes filled with derived classes ?

                  Ben Pope wrote:

                  [color=blue]
                  > The problem is often that of who does the cleaning up (deleting the
                  > pointer) and when... this is often managed by either a container of
                  > boost::shared_p tr<objectType> or one of the boost ptr_containers.[/color]

                  To delete all widgets, I'written the function deletaAll(). This is the
                  full code of container of widgets. Really I'm not sure it work: I've
                  modified an example (thanks to Mateusz) that worked, but I've not fully
                  understand functors, so maybe I've ruined all, using them in a function.
                  What do you think? Maybe OK?

                  ---------------------------------------------------

                  #include <windows.h>
                  #include "mhcontaine r.h"


                  #include <iostream>//For debug

                  struct DeleteObjs
                  {
                  template <typename T>
                  void operator()(cons t T* ptr) const
                  {
                  delete ptr;
                  }
                  };

                  //Put widget into container
                  void mhcontainer::ad dWidget(mhwidge t* w)
                  {
                  std::cout << "added";
                  widgetList.push _back(w);
                  std::cout << "now the size is: " << widgetList.size () << std::endl ;
                  }

                  //Draw all widgets
                  void mhcontainer::dr awAll()
                  {

                  std::vector<mhw idget*>::iterat or it = widgetList.begi n();
                  while(it != widgetList.end( ))
                  {
                  std::cout << "widget!";
                  (*it++)->draw();
                  //(*it++)->tryMe();
                  }
                  }

                  //Delete all widgets
                  void mhcontainer::de leteAll()
                  {
                  std::for_each(w idgetList.begin (), widgetList.end( ), DeleteObjs());
                  }

                  Comment

                  Working...