Reputation: 13
My C++ is a bit rusty and I don't remember everything in the standard.
I have a void*
. In one specific function it is either a class that inherits Alpha or one that inherits Beta. Both base classes have virtual functions. However I can't seem to tell which is which
class Alpha {
public:
virtual void Speak() { printf("A"); }
};
class Beta {
public:
virtual void Speak() { printf("B"); }
};
int main(){
//BAD CODE WILL PRINT TWICE
void *p = new Alpha;
Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);
Beta*b = dynamic_cast<Beta*>((Beta*)p);
if(a)
a->Speak();
if(b)
b->Speak();
return 0;
}
How do I figure out which class is which? There are 100's of classes in this codebase that gets converted to void. Most of them inherit 5 base classes however I'm not eager to find out. Is the only solution inheriting from something like class Dummy {public: virtual void NoOp(){}};
and cast to Dummy before using dynamic cast? Is this safe? I'm hoping there's a better solution but I can't think of anything else.
Upvotes: 1
Views: 1429
Reputation: 2403
I feel this could be explained more simply than what is said already.
Basically, casting from a raw void*, none of the "smarts" of dynamic_cast can apply because it has nothing to go on, it just gives you back the same pointer. You need to have some shared parent class type or something as your pointer type (*p) so it knows how to read the memory and determine actual type.
In your case, you are "lucking out" that the memory layout of Alpha and Beta is the same so calling speak on Beta* ends up calling Alpha::speak(). As others said this is UB and if the classes were more different you would likely seg-fault or corrupt the stack.
Upvotes: 0
Reputation: 234695
The only thing you can do with a void*
pointer is to cast it back to exactly the same type as the pointer that was cast to void*
in the first place. The behaviour on doing anything else is undefined.
What you could do in your case is define
class Base
{
public:
virtual ~Base() = default; // make me a polymorphic type and make
// polymorphic delete safe at the same time.
};
and make this the base class for Alpha
and Beta
. Then pass a Base*
pointer around rather than a void*
one, and take your dynamic_cast
s directly on p
.
Note further that if you declared virtual void Speak() = 0;
in Base
, then your code in main
would become simply
int main(){
Base* p = new Alpha;
p->Speak();
delete p; // ToDo - have a look at std::unique_ptr
}
As a rule of thumb, casts of any kind are undesirable.
Upvotes: 6
Reputation: 1639
If you use a C-style cast to convert to Alpha*
, similar to a static_cast before using dynamic cast, then the dynamic cast as no effect. here your code runs because the two classes have the same interface but in reality this is undefined behaviour.
Usually, you want to use dynamic cast to upcast/downcast from/to a base class to/from it's derived class.
For example, if we add a base interface, then convert the void *
pointer to this base class and then use dynamic cast to attempt Up-casting, the code works as expected and only print once.
#include <stdio.h>
class Speaker {
public:
virtual void Speak() = 0;
};
class Alpha: public Speaker {
public:
virtual void Speak() { printf("A"); }
};
class Beta: public Speaker {
public:
virtual void Speak() { printf("B"); }
};
int main(){
void *p = new Alpha;
// Convert to base type, using static_cast
Speaker *s = static_cast<Speaker *>(p);
// Attempt Upcasts
Alpha*a = dynamic_cast<Alpha*>(s);
Beta*b = dynamic_cast<Beta*>(s);
// See if it worked
if (a)
a->Speak();
if (b)
b->Speak();
return 0;
}
Outputs: A
Upvotes: 1
Reputation: 238341
You must know the type from which the void pointer was converted from. If you don't know the dynamic type, then you must create the void pointer from the pointer to base. If you don't know the type of the pointer which the void pointer was created from, then you cannot use the void pointer.
Given that the void pointer was converted from Alpha*
, you can convert it back using a static cast:
auto a_ptr = static_cast<Alpha*>(p);
You can then use dynamic_cast
to convert to a derived type.
if(auto d_ptr = dynamic_cast<DerivedAlpha*>(a_ptr)) {
// do stuff with DerivedAlpha
In case the dynamic type isn't DerivedAlpha
, the dynamic cast would safely return null. You cannot dynamic cast away from a type hierarchy. Since Alpha
and Beta
are not related by any inheritance structure, they cannot be dynamically casted mutually.
Upvotes: 0
Reputation: 29022
The expression Alpha*a = dynamic_cast<Alpha*>((Alpha*)p);
first casts p
to Alpha*
with an explicit c style cast. Then, that resulting Alpha*
is passed through dynamic_cast<Alpha*>
. Using dynamic_cast<T*>
on a T*
pointer (a pointer of the same type as you are trying to cast to) will always provide the input pointer. It cannot be used to confirm that the pointer is valid. From cppreference for dynamic_cast<new_type>(expression)
:
If the type of
expression
is exactlynew_type
or a less cv-qualified version ofnew_type
, the result is the value ofexpression
, with typenew_type
.
As a result, the code will always compile and run and the type system will not protect you. But the resulting behavior is undefined. In the case of Beta*b = dynamic_cast<Beta*>((Beta*)p);
you tell the compiler to trust that p
is a Beta*
but this is not true. Dereferencing the resulting pointer is undefined behavior and dynamic_cast
cannot protect you from this mistake.
If you try to remove the explicit type conversion, you will get a compiler error. dynamic_cast
requires a pointer or reference to a complete type, and void
is not a complete type. You will have to find a way to track the actual type pointed to yourself and explicitly convert p
to that pointer type before using dynamic_cast
. Though at that point, if you already know the type to cast to, it may no longer be necessary.
Consider using a common base type instead or maybe using std::variant
or std::any
if need be.
Upvotes: 1