specialized containment???

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

    #1

    specialized containment???

    Hello, maybe somebody can enlighten me? :)
    I wasn't really sure how to concisely describe the following scenario
    so it was tricky to search for help.


    Simplifying the problem...
    I have a home which contains people. I also want a SpecialHome which
    has to contain a set of SpecialPersons.


    class Home {
    Person* getPerson(id);

    vector<Person*> m_People;
    }

    class SpecialHome : public CHome {
    SpecialPerson* getPerson(id);
    vector<SpecialP erson*> m_People;
    }


    So obviously the above SpecialHome class isn't how I would want to
    implement this. There's nothing being inherited, the list of pointers
    are unnecessarily duplicated, etc. The problem is that a number of
    Home methods accept and return Persons. In my more complicated
    situation, I really want to avoid a templatized Home class (it would
    make for quite a confusing class diagram). I guess accepting Persons
    isn't such a problem thanks to polymorphism, but returning them is
    (I've also got a Person iterator class in an effort to abstract away
    the containment mechanism in Home).

    The approach I've tried taking is to remove all of these Person-related
    methods from Home and put them is a sort of templatized "manipulato r"
    class. This manipulator was made a friend of Home and SpecialHome so
    it more or less can do whatever it pleases. Naturally, there's a lot
    of dynamic_casting since the vector lives in the base class (for some
    reason dynamic_casting always makes me uncomfortable).

    I'm having trouble getting this to compile because of complications
    with the more convoluted scenario I'm dealing with.
    This lead me to take a step back and ask myself if there isn't a better
    way to do design the class structure. I would imagine the scenario I
    described above is a fairly common one.

    Any tips are greatly appreciated!
    cheers!

  • Alf P. Steinbach

    #2
    Re: specialized containment???

    * Mr Dyl:[color=blue]
    >
    > I have a home which contains people. I also want a SpecialHome which
    > has to contain a set of SpecialPersons.
    >
    >
    > class Home {
    > Person* getPerson(id);
    >
    > vector<Person*> m_People;
    > }
    >
    > class SpecialHome : public CHome {
    > SpecialPerson* getPerson(id);
    > vector<SpecialP erson*> m_People;
    > }[/color]

    That sketch isn't really enough to go on, because you haven't indicated
    access, and for this kind of problem what's private and what's public
    and what's const or not is key.

    Here's a more fleshed-out sketch that illustrates the fundamental
    problem you're up against:

    class Person {};
    class ShortPerson: public Person {};
    class LongPerson: public Person {};

    class Home
    {
    public:
    addPerson( Person* );
    std::vector<Per son const*> persons() const;
    };

    class ShortPersonHome : public Home
    public:
    addPerson( ShortPerson* );
    std::vector<Sho rtPerson const*> persons() const;
    };

    class LongPersonHome: public Home
    public:
    addPerson( LongPerson* );
    std::vector<Lon gPerson const*> persons() const;
    };

    int main()
    {
    ShortPersonHome home;
    Home* pHome = &home;
    LongPerson longPerson;

    pHome->addPerson( &longPerson ); // Oops, roof is too low!
    }

    As you can see it's a problem of mutability, not of downcasting the
    internal representation (although that's also a problem, but managable).

    What you need is something like

    // Home
    // MutableHome
    // ShortPersonHome
    // MutableShortPer sonHome
    // LongPersonHome
    // MutableLongPers onHome

    class Home
    {
    public:
    std::vector<Per son const*> persons() const;
    };

    class MutableHome: public Home
    {
    public:
    addPerson( Person* );
    };

    class ShortPersonHome : public Home
    public:
    std::vector<Sho rtPerson const*> persons() const;
    };

    class MutableShortPer sonHome: public ShortPersonHome
    {
    public:
    addPerson( ShortPerson* );
    };

    class LongPersonHome: public Home
    {
    public:
    std::vector<Lon gPerson const*> persons() const;
    };

    class MutableLongPers onHome: public LongPersonHome
    {
    public:
    addPerson( LongPerson* );
    };

    Now in ShortPersonHome you can safely assume that any Person in there is
    really a ShortPerson, because no other kinds of persons can be added.
    So you can simply provide an internal accessor function that downcasts.
    As I wrote, that part is a managable problem, also with other solutions:
    note that C++ does support covariance for function results.

    Hth.,

    - Alf

    --
    A: Because it messes up the order in which people normally read text.
    Q: Why is it such a bad thing?
    A: Top-posting.
    Q: What is the most annoying thing on usenet and in e-mail?

    Comment

    • Mr Dyl

      #3
      Re: specialized containment???

      Hey Alf,
      thanks for the reply.
      Interesting. At the moment I'd only been thinking about the
      relationship between parent and child classes so I hadn't even been
      thinking about the problem you pointed out.

      A question though,

      Home contains (I assume) a list of persons accessed through:

      std::vector<Per son const*> persons() const

      LongPersonHome also provides access to this list (but they are
      LongPersons now):

      std::vector<Lon gPerson const*> persons() const;

      How are you able to cast a std::vector<Per son const*> to
      std::vector<Lon gPerson const*>?
      I think maybe there's something fundamental here that I'm not seeing.
      Thanks again!
      Dylan

      Comment

      • Alf P. Steinbach

        #4
        Re: specialized containment???

        * Mr Dyl:[color=blue]
        >
        > Home contains (I assume) a list of persons accessed through:
        >
        > std::vector<Per son const*> persons() const
        >
        > LongPersonHome also provides access to this list (but they are
        > LongPersons now):
        >
        > std::vector<Lon gPerson const*> persons() const;
        >
        > How are you able to cast a std::vector<Per son const*> to
        > std::vector<Lon gPerson const*>?[/color]

        Note that the signature of 'persons' specifies a copy.

        A mutable collection of T isn't really convertible along with the type
        of T. That's a problem with Java arrays, for example, where Java is
        reduced to run-time type-checking, throwing an exception if the type of
        an element assigned to an array is of the wrong dynamic type,
        <url:
        http://java.sun.com/docs/books/jls/third_edition/html/arrays.html#10. 10>,
        which implies a run-time check of every assignment to a Java array.

        Note that that this concerns an array casted analogous to casting
        std::vector<Lon gPerson const*> to std::vector<Per son const*>, an upcast,
        which is opposite and many might think safer than the cast you mention.

        So, let 'persons' create a _copy_... ;-)

        Or, its return type is non-mutable collection, of a collection type that
        supports this kind of thing; that means essentially, a collection type
        you have defined.

        Or, represent it by a collection of member functions that provide
        iteration.

        Or, represent it by a general standard-library compatible iterator.

        [color=blue]
        > I think maybe there's something fundamental here that I'm not seeing.[/color]

        Yes, you're now up against the idea of "virtual data".

        <url:
        http://www.parashift.c om/c++-faq-lite/value-vs-ref-semantics.html# faq-31.2>.

        --
        A: Because it messes up the order in which people normally read text.
        Q: Why is it such a bad thing?
        A: Top-posting.
        Q: What is the most annoying thing on usenet and in e-mail?

        Comment

        Working...