Foo* f = new Foo()
Bar *b = (Bar*)f;
The problem with this is its not safe. If Bar is not a base class of Foo then b is invalid and will do who knows what. There is a second possible problem too. If Foo has multiple base classes, say Bar and Baz, the cast might not work either. This depends on how the class was declared
class Foo : public Bar, public Baz
or
class Foo : public Baz, public Bar
If it was the first one, the code would have been okay. If it was the second then we just tried to cast a pointer to Baz into a pointer to Bar, which isn't okay. Casting to a base type is called down casting. How do you safely cast into Bar?
Welcome our good friend dynamic_cast. dynamic_cast knows how the classes were declared and will correct cast for you. It'll shift pointers around and return the right thing. And in case you tried to cast to something that is impossible, it returns a null. The one thing dynamic_cast wont do is cast from a void*. This is known as up casting, or casting from a base class to a derived class.
void* v = new Foo();
Bar* b = (Bar*)new Foo();
Foo* x = dynamic_cast(v); // returns null
Foo* y = dynamic_cast(b); // returns Foo*
There is no path to Bar from void, so dynamic_cast wont work here. Using void* as a generic pointer is pretty common, but unfortunately you can't dynamic_cast from it. What you need to do is create a common base class for everything to share. Lets call it Object
class Object{};
class Bar : public Object
{ };
class Baz : public Object
{ };
class Foo : public Bar, public Baz
{};
Now everything shares a common base class called Object. The problem is Foo has 2 Object base classes, one for Bar and one for Baz. If you drew out the memory, it might look something like this.
Foo
+-----
|Object
|Bar
+-----
+-----
| Object
| Baz
+-----
Now, if you tried to cast Foo to Object, which Object should it point to, the one in Bar or the one in Baz. Using a normal (Object*)new Foo() would get you the Object from bar. Using dynamic_cast would result in a compile warning of C4540 (dynamic_cast used to convert to inaccessible or ambiguous base) and it would return null. You can fix this by using a virtual base class.
class Object{};
class Bar : virtual public Object
{ };
class Baz : virtual public Object
{ };
class Foo : public Bar, public Baz
{};
Notice the "virtual public Object". This tells the compiler to only make one Object for Foo, and have Bar and Baz share it. Now dynamic_cast will be able to cast from Object to Foo and vise-versa. Now you can use Object* instead of void* as your generic pointer. This will allow you to dynamic_cast up and down all you want. The one thing you can't use this for is for primitives like int, char, float. But you can't derive anything from that anyway so it shouldn't matter.
One idea, you can use these for a Service Registry. Just make all your services have a virtual base class of IService.
class ServiceRegistry
{
public:
void Add(char* name, IService *i);
IService Find(char* name);
}
// cast to IService to add
reg->Add("my_service", dynamic_cast(my_service) );
// cast from IService to get
MyService* find = dynamic_cast( reg->Find("my_service") );
No comments:
Post a Comment