I am trying to create some classes which only contain data members (no functions) but I would like them to be polymorphic - by which I mean I will be passing around the objects by a pointer to the base class, and I need the ability to dynamic_cast
them to a specific derived type (and have the resulting value be NULL
if the instance is not of the given type.)
By way of example, I have an item:
struct Item {
int x, y;
}
I also have an item which moves, and another which contains text:
struct MovingItem: virtual public Item {
int speedX, speedY;
}
struct TextItem: virtual public Item {
std::string text;
}
Presumably I have to use virtual inheritance above, because I also want an item that moves and has text, but I only want the one set of coordinates from the top-level Item
:
struct MovingTextItem: virtual public MovingItem, virtual public TextItem {
}
These can all be defined correctly, but when I try to dynamic_cast
an Item *
to see what type it is, my compiler complains that the source types aren't polymorphic.
void example(Item *i) {
MovingTextItem *mti = dynamic_cast<MovingTextItem *>(i); // error!
}
This would work if I reimplemented the whole thing using virtual functions instead of data members, but this seems like a waste as I never need to override anything.
The only workaround I can think of is to add a type
member to the base Item
class, and check that instead of using dynamic_cast
, and if it's of the correct type then use static_cast
instead. (The drawback there being I have to know about all object types somewhere so the assigned type
values don't conflict.)
Is this the best solution, or is there another way?
For arguments' sake, imagine I am writing each object type to a file. MovingItem
goes to one file, TextItem
goes to a different file, and MovingTextItem
goes to both files. So having a base class which implements every interface won't work, unless I can somehow tell which interfaces are in use so they get written to the correct files.
Languages that are not object-oriented provide forms of polymorphism which do not rely on inheritance (i.e parametric polymorphism). Some of this is possible in Java through generics.
Polymorphism is possible in C language. Explanation: It is possible to implement polymorphism in C language, even though it doesn't support class. We can use structures and then declare pointers which in turn points to some function.
(*) Genericity, the type of polymorphism provided by templates, doesn't need pointers nor references.
A class that declares or inherits a virtual function is called a polymorphic class. Note that despite of the virtuality of one of its members, Polygon was a regular class, of which even an object was instantiated ( poly ), with its own definition of member area that always returns 0.
If you add a virtual function, the classes will get a vtable so that dynamic_cast
works. A no-op virtual destructor will do. (As Torsten points out, this even might be necessary in your case as you have non-POD members.)
However, from my own experience (and I have been resisting this for years!) I would strongly advise against inheritance in this particular case. Use aggregation instead. Prefer composition over inheritance? You may have to create a couple of forwarders, but the flexibility gained by this pays off (in terms of being able to later change implementations without influencing the whole system).
Think of an item that has a speed and, in addition, a text and a position, instead of being an item that somehow has speed and text and by some magic inherits the position. What if you later want an item that has speed but no known position?
To achieve your results in an aggregation scenario, define "interfaces" that constitute the contract concerning a particular feature. No need for virtual inheritance, too: The "interfaces" are just classes with only pure virtual functions. See also: What does it mean to "program to an interface"? (Another important rule if you want maintainable software.)
You could define four "interfaces" (classes with only pure virtual functions): Position
, Speed
, Text
and Item
. Item
inherits from the three former and defines no functions itself. You provide "reference implementations" for the first three interfaces. The "reference impl." for Item
has three data members (the ref. impl. of the first three interfaces) and forwards to these implementations. Now you can use e.g. Position
wherever you need something that has a position, without having to know what exactly it is. dynamic_cast
works, too.
You could also define an interface ItemBase
with no virtual methods and have Position
, Speed
and Text
inherit from this interface. This will allow you to access all featurettes through the same base type and dynamically test for the availability of a subinterface. Still no need for virtual
inheritance, I think...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With