There are 4 classes: A, B, C, and D. Class A "HAS A" B, and class C "HAS A" D. As it happens, C "IS A" A, and D "IS A" B. What's the best way to model these relationship开发者_运维技巧s in C++?
The B that A has is the same D that C has.
Example: A building has an entrance, and a house has a door. A house is a building, and a door is an entrance.
class B {
};
class A {
B b; // A "has a" B
};
class C : public A { // C "is a" A
D d; // C "has a" D
};
class D : public B { // D "is a" B
};
I think there is a flaw in your design. If I understand correctly, you want to restrict the type of a base class member variable:
struct Entrance {};
struct Door : public Entrance {};
struct Building
{
Entrance * entrance; // raw pointer just for the sake of clarity
};
struct House : public Building
{
// Building::entrance must always be a door
};
You could achieve that using accessors (getter/setter) to entrance
and checking the type, throwing an exception if the entrance is not a door, but that would break the Liskov substitution principle: you would not be able to manipulate House
s as if they were Building
s:
struct Drawbridge : public Entrance {};
House house;
Drawbridge bridge;
Building & building = house;
building.setEntrance(bridge);
// Oups, I'm trying to install a drawbridge on my house!
Some libraries perform this kind of restrictions (for example, ReadOnlyCollection<T>
throws an exception when one try to modify its content), but in my opinion, this is not a clean design. If collection's interface states that I can add elements to the collection, then a read only collection is not a collection (since it does not support adding elements).
The same reasoning can be applied here: a House
is not a Building
since it cannot contain all kind of Entrance
s.
A building has an entrance, and a house has a door. A house is a building, and a door is an entrance.
struct Entrance {};
struct Door : Entrance {};
struct Building {
virtual Entrance& getEntrance() = 0;
};
struct House : Building {
virtual Entrance& getEntrance() {
return entrance;
}
private:
Door entrance;
};
typedef Building A;
typedef Entrance B;
typedef House C;
typedef Door D;
This is the nearest approximation, using polymorphism. Note in particular that Building
cannot be instantiated because it is "abstract".
Otherwise, you cannot have A
has-a B
and C
has-a D
, and have both the B
and the D
be the same object.
If you get tempted to attempt to have the base hold a reference, avoid it:
struct Entrance {};
struct Door : Entrance {};
struct Building {
Building(Entrance& e) : e(e) {}
Entrance& e;
};
struct House : Building {
House() : Building(e) {}
Door e;
};
typedef Building A;
typedef Entrance B;
typedef House C;
typedef Door D;
Since House
's Building
constructor is invoked before its Door
is created, this is questionable at best (and undefined at worst.. I'd have to look it up).
Luc is right, though, that this modelling is flawed.
C extends A and D extends B. A holds a B object in its definition and C holds a D object. [Has-a] [Is-a]
精彩评论