Array of derived classes

Discussion in 'C++' started by Michael Grünewald, Apr 24, 2012.

  1. Dear newsgroup,

    I have a basis class A and derivatives B1…Bn and would like to build an
    array containing various Bi's, and was unable to do so. Your help would
    be much appreciated!

    Ideally A would be a virtual (non instantiable) class, but this
    constraint is not realizable if we want to declare an array of objects
    of type A.

    In my attempt, I defined a `=` operator for objects of type A
    and declared an array

    A T[n];

    and initialised it like this

    T[0] = B0();
    …

    Which (of course?) ended up with T containing n copies of the implicit
    object of type A underlying Bi, but nothing like Bi.

    Am I doing here something wrong or pointers are the only way to obtain
    heterogeneous arrays?

    Is an allocation à la C

    A T[n] = { B0(), … };

    allowed in C++ ?

    I would also be very interested in literature references on this topic
    or similar topics, I am very new to C++ (but skilled in other languages,
    some OO, some not) and do not know yet about all these good books…

    Thank you very much for your help!
    --
    Michael
     
    Michael Grünewald, Apr 24, 2012
    #1
    1. Advertising

  2. Hi Michael,

    On Apr 24, 10:36 am, Michael Grünewald <> wrote:
    > Dear newsgroup,
    >
    > I have a basis class A and derivatives B1…Bn and would like to build an
    > array containing various Bi's, and was unable to do so.  Your help would
    > be much appreciated!


    I am afraid the language does not permit it. If you define an array

    A arr[n];

    (by the way, I used "arr" above rather than "T" ... "T" is typically
    used to indicate a templated type rather than a variable name in
    idiomatic example code snippets)

    .... and try to assign derived objects of A into it, then you will just
    end up with "slicing" exactly as you describe below.


    > Which (of course?) ended up with T containing n copies of the implicit
    > object of type A underlying Bi, but nothing like Bi.
    >
    > Am I doing here something wrong


    Almost certainly, sorry.

    > or pointers are the only way to obtain heterogeneous arrays?


    More or less. But instead of A* arr[n], consider something like one
    of the following work-alike solutions:

    * boost:: (or in C++11, std::) shared_ptr<A>[n], or
    std::unique_ptr<A>[n]

    * boost::ptr_array<A, size>

    * More generally, the Letter-Envelope idiom may be what you want; see
    for instance
    http://www.panix.com/~elflord/cpp/envelope/

    * In the (quite unlikely) case that you know at compile-time what the
    ordering of the derived types of your objects in your data structure
    would be, you could in principle use boost:: (or in C++11, std::)
    tuple<B1, B2, B3, ...> or whatever.


    > Is an allocation à la C
    >
    >    A T[n] = { B0(), … };
    >
    > allowed in C++ ?


    It is allowed, but it too will lead to slicing, and again you will
    only find base-class "A" objects in T[].


    > I would also be very interested in literature references on this topic
    > or similar topics, I am very new to C++ (but skilled in other languages,
    > some OO, some not) and do not know yet about all these good books…


    Letter-Envelope idiom is apparently described well (I say apparently,
    since I don't have the book myself) in _Advanced C++ Programming
    Styles and Idioms_, James Coplien, Addison Wesley (1991)... the book
    overall would be pretty dated though.

    boost::shared_ptr and boost::ptr_array are described in the Boost
    library documentation. std::shared_ptr and std::unique_ptr in, for
    instance, _The C++ Standard Library_, 2nd Edition, Nicolai Josuttis
    (2012). Book web site is at http://www.cppstdlib.com/

    - Kevin B. McCarty
     
    Kevin McCarty, Apr 24, 2012
    #2
    1. Advertising

  3. Michael Grünewald

    K. Frank Guest

    Hello Michael!

    On Apr 24, 1:36 pm, Michael Grünewald <> wrote:
    > Dear newsgroup,
    >
    > I have a basis class A and derivatives B1…Bn and would like to build an
    > array containing various Bi's, and was unable to do so.  Your help would
    > be much appreciated!


    As you've discovered, you can't, exactly, make an array
    of various Bi's. And as you've speculated, and Paavo and
    Kevin have replied, you can use an array of pointers to
    achieve your goal.

    The key point is that in c++ polymorphism is implemented
    using pointers (and references). So a pointer to your
    base class, A:

    A *pointer_to_A;

    can point not only to an A, but also to a derived Bi:

    B1 b1;
    B2 b2;
    pointer_to_A = &b1; // points to a B1
    pointer_to_A = &b2; // now it points to a B2

    Other object-oriented languages manage polymorphism
    differently, but this is how c++ does it.

    (For completeness, the other important piece of the
    polymorphism puzzle is the use of virtual functions.
    If class A defines a virtual member function, say,

    virtual void A::doSomething() { x = 0; }

    and the derived classes, Bi, override doSomething() with
    their own versions that do something different:

    void B1::doSomething() { x = 1; }
    void B2::doSomething() { x = 2; }

    then calling doSomthing() through pointer_to_A:

    pointer_to_A->doSomething();

    doesn't necessarily call A's version, A::doSomething(),
    (which would set x to 0), but depends on to which type
    of object pointer_to_A actually points. So if
    pointer_to_A points to a B1, then x will get set to 1
    rather than to 0.)

    > Ideally A would be a virtual (non instantiable) class, but this
    > constraint is not realizable if we want to declare an array of objects
    > of type A.


    But this constraint is perfectly fine if you use an
    array of pointers. The conventional c++ jargon for
    your non-instantiatable base class is an "abstract base
    class." The derived classes that can be instantiated
    are typically called concrete classes.

    It's perfectly fine for A to be an abstract base class
    and have a variable of type pointer-to-A (or an array
    of pointers to A). These can point to instances of the
    concrete derived classes, Bi.

    > ...
    > Am I doing here something wrong or pointers are the only way to obtain
    > heterogeneous arrays?


    Yes, pointers are the way to go to get heterogeneous (in the
    sense of polymorphic) arrays. That's how c++ is designed to
    work.

    (Of course, as Paavo and Kevin mentioned, you might want
    to use some fancier and "better" container such as a
    std::vector instead of an array, and you might want
    to use a fancier and "smarter" pointer such as a
    std::shared_pointer instead of a raw pointer, but the
    basic issue -- that c++ uses pointers (and references)
    for polymorphism is still the same.)

    > ...
    > I would also be very interested in literature references on this topic
    > or similar topics, I am very new to C++ (but skilled in other languages,
    > some OO, some not) and do not know yet about all these good books…


    A good place to start, especially if you are new to c++, is
    Marshall Cline's C++ FAQ-Lite:

    http://www.parashift.com/c -faq-lite/

    His sections on inheritance, starting with:

    http://www.parashift.com/c -faq-lite/basics-of-inheritance.html

    are relevant, and he gives a synopsis of c++ polymorphism in:

    http://www.parashift.com/c -faq-lite/virtual-functions.html#faq-20.2

    (The C++ FAQ Lite is worth reading in general -- both to
    look up answers to specific questions, and to browse just
    for inspiration and general learning. It is not, however,
    in any sense complete, so it's not really a good tutorial,
    and is certainly not a comprehensive reference.)

    >
    > Thank you very much for your help!
    > --
    > Michael


    Good luck, and Happy Hacking!


    K. Frank
     
    K. Frank, Apr 24, 2012
    #3
  4. Hi Kavin & Paalo!

    Thank you very much for your detailed answers,
    I found them useful!

    Kevin McCarty wrote:
    > On Apr 24, 10:36 am, Michael Grünewald<> wrote:
    >> Dear newsgroup,
    >>
    >> I have a basis class A and derivatives B1…Bn and would like to build an
    >> array containing various Bi's, and was unable to do so. Your help would
    >> be much appreciated!

    >
    > * In the (quite unlikely) case that you know at compile-time what the
    > ordering of the derived types of your objects in your data structure
    > would be, you could in principle use boost:: (or in C++11, std::)
    > tuple<B1, B2, B3, ...> or whatever.


    Well, here you hit something very peculiar to my situation: B1…Bn are
    parsers.

    In a nicer world, B1…Bn would be instances of A, an object describing
    the interface of parsers and accepting sufficiently general description
    of the parsing task.

    Unfortunately, families of parsers are difficult to parametrise, even in
    simple cases, so I opted for the (rather clumsy) solution provided by
    specialisation of A into the Bi's.

    >> I would also be very interested in literature references on this topic
    >> or similar topics, I am very new to C++ (but skilled in other languages,
    >> some OO, some not) and do not know yet about all these good books…

    >
    > Letter-Envelope idiom is apparently described well (I say apparently,
    > since I don't have the book myself) in _Advanced C++ Programming
    > Styles and Idioms_, James Coplien, Addison Wesley (1991)... the book
    > overall would be pretty dated though.
    >
    > boost::shared_ptr and boost::ptr_array are described in the Boost
    > library documentation. std::shared_ptr and std::unique_ptr in, for
    > instance, _The C++ Standard Library_, 2nd Edition, Nicolai Josuttis
    > (2012). Book web site is at http://www.cppstdlib.com/


    Thank you for your input!
    --
    Michael
     
    Michael Grünewald, Apr 24, 2012
    #4
  5. He Frank!

    Thank you for your nice answer,

    K. Frank wrote:
    >> Ideally A would be a virtual (non instantiable) class, but this
    >> constraint is not realizable if we want to declare an array of objects
    >> of type A.

    >
    > But this constraint is perfectly fine if you use an
    > array of pointers. The conventional c++ jargon for
    > your non-instantiatable base class is an "abstract base
    > class." The derived classes that can be instantiated
    > are typically called concrete classes.
    >
    > It's perfectly fine for A to be an abstract base class
    > and have a variable of type pointer-to-A (or an array
    > of pointers to A). These can point to instances of the
    > concrete derived classes, Bi.


    This is good to know!

    However I feel this is a bit odd. On the one hand, pointers are tricky,
    and the shortest way to undefined behavior of a program. On the other
    hand, they allow a terse way to iterate over an array (with the ++
    operator(s)), that every C or C++ programmer likes. It seems to me that
    the introduction in the language of references (not so unsafe pointers
    that you can use as function arguments) and of the overloading (for the
    definition of smart containers understanding the ++ idiom) try to make
    pointers a not so crucial part of the language.¹ But somehow “banned”
    from the language, pointers “come back” with polymorphism!

    ¹ I finally managed to write a sentence that qualifies as bad example
    for manuals of style, sorry! :)


    > (Of course, as Paavo and Kevin mentioned, you might want
    > to use some fancier and "better" container such as a
    > std::vector instead of an array, and you might want
    > to use a fancier and "smarter" pointer such as a
    > std::shared_pointer instead of a raw pointer, but the
    > basic issue -- that c++ uses pointers (and references)
    > for polymorphism is still the same.)


    As I mentioned in my answer to Paavo & Kevin, the Bi's are parser and my
    array is constant…

    > (The C++ FAQ Lite is worth reading in general -- both to
    > look up answers to specific questions, and to browse just
    > for inspiration and general learning. It is not, however,
    > in any sense complete, so it's not really a good tutorial,
    > and is certainly not a comprehensive reference.)


    Wonderful, I will try to get acquainted to it, the sooner, the better.

    Thank you again!
    --
    Michael
     
    Michael Grünewald, Apr 24, 2012
    #5
  6. Michael Grünewald

    Ian Collins Guest

    On 04/25/12 03:35 PM, Paavo Helde wrote:
    >
    > Hey, I even wrote a "goto" into the code last week, as this was the
    > cleanest and easiest way to achieve the needed goal *in the situation at
    > hand*.


    Good lord, someone actually found a case where goto was appropriate! I
    guess there's a first time for everything.

    :)

    --
    Ian Collins
     
    Ian Collins, Apr 25, 2012
    #6
  7. Paavo Helde <> wrote:
    > Also note that a raw array like A T[n] is very rarely the right choice in
    > C++. One should normally use std::vector or some other STL container.


    It's the better choice when:

    1) The size of the array is known at compile time and never changes at
    runtime.

    2) The array needs to take as little RAM as possible, and be as efficient
    as possible. (Most typically this is the case with a very small array
    as a member of a class that gets instantiated a lot. In this situation
    using std::vector would kill performance.)

    Of course with C++11 it may be better to use std::array instead, which
    is basically the same thing, but wrapped in a class with utility member
    functions (and also directly copyable, which static arrays aren't, except
    if they are a member of a class/struct).
     
    Juha Nieminen, Apr 25, 2012
    #7
  8. Hi Paavo,

    thank you for your comments!

    Paavo Helde wrote:
    > Programming is an engineering discipline, not some politics or religion.


    I may have not been incredibly well inspired when I wrote “banned,” this
    was not to take too literally… I meant “one tries to avoid or find
    better solutions for pointers in the contexts where these drawbacks
    outweigh benefits” but your words are definitely better. :)
    --
    Michael
     
    Michael Grünewald, Apr 26, 2012
    #8
  9. Michael Grünewald

    Jorgen Grahn Guest

    On Tue, 2012-04-24, Michael Grünewald wrote:
    ....
    > However I feel this is a bit odd. On the one hand, pointers are tricky,
    > and the shortest way to undefined behavior of a program. On the other
    > hand, they allow a terse way to iterate over an array (with the ++
    > operator(s)), that every C or C++ programmer likes. It seems to me that
    > the introduction in the language of references (not so unsafe pointers
    > that you can use as function arguments) and of the overloading (for the
    > definition of smart containers understanding the ++ idiom) try to make
    > pointers a not so crucial part of the language.¹ But somehow "banned"
    > from the language, pointers "come back" with polymorphism!


    That pretty much sums it up for me. I like pointers as iterators, but
    I'm careful when I have to use them in the role of handles to some
    long-lived resource. (And it's not just for run-time polymorphism you
    need that.)

    It's usually not a big deal, though. Make the pointer a class member,
    make that class responsible for the lifetime of the pointed-to object,
    and you're safe. In my code, I still haven't seen a need to apply
    generic smart pointers yet, but YMMV.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
     
    Jorgen Grahn, May 5, 2012
    #9
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Colin Goudie
    Replies:
    6
    Views:
    473
    Victor Bazarov
    Jan 26, 2004
  2. Manuel
    Replies:
    8
    Views:
    640
    Manuel
    Jan 5, 2006
  3. Replies:
    6
    Views:
    373
  4. Replies:
    1
    Views:
    397
    myork
    May 23, 2007
  5. Replies:
    1
    Views:
    389
    Victor Bazarov
    May 23, 2007
Loading...

Share This Page