specialized containment???

M

Mr Dyl

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<SpecialPerson*> 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 "manipulator"
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!
 
A

Alf P. Steinbach

* Mr Dyl:
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<SpecialPerson*> m_People;
}

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<Person const*> persons() const;
};

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

class LongPersonHome: public Home
public:
addPerson( LongPerson* );
std::vector<LongPerson 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
// MutableShortPersonHome
// LongPersonHome
// MutableLongPersonHome

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

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

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

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

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

class MutableLongPersonHome: 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
 
M

Mr Dyl

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<Person const*> persons() const

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

std::vector<LongPerson const*> persons() const;

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

Alf P. Steinbach

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

std::vector<Person const*> persons() const

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

std::vector<LongPerson const*> persons() const;

How are you able to cast a std::vector<Person const*> to
std::vector<LongPerson const*>?

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<LongPerson const*> to std::vector<Person 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.

I think maybe there's something fundamental here that I'm not seeing.

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

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

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top