I have c++ experience for about a year or two but I code the same way I code in Java (simple oop stuff). Now I have this sample code which I don't understand. (it's quite big so I tried to make it shorter, I hope it's clear enough for you guys)
//in .h file
typedef void*(*AnimalCreation)();
//in .cpp
void foo(void* p)
{
AnimalCreation ac = (AnimalCreation)p;
Animal* current_animal = reinterpret_cast<Animal*>(ac());
current_animal->init();
}
//somewhere in another class foo is called
Dog* dog = new Dog(); //Dog is a subclass of Animal
foo((void*)&dog)
What is the purpose of AnimalCreation? And what's the difference between that and
typedef void(*AnimalCreation)();`//without asterisk after void
What's happening inside foo?
If the expected argument foo receives is always a subclass of Animal why does the programmer need to implement it like in the above and not just foo(Animal*)?
Thanks.
typedef void(*AnimalCreation)();
this declares "AnimalCreation" to be used as type-alias for a pointer to a function which doesn't return any value, while this
typedef void*(*AnimalCreation)();
declares it to be used as a type-alias for a pointer to a function which returns a void pointer, ie an address to something you don't know its type.
Inside foo
you're receiving such a "generic address" and you're C-casting (potentially unsafe, checked at runtime) it to a function pointer. This is at your own risk: you don't know what that received address is pointing to. And after that you're calling the function and receiving another void pointer which you reinterpret (dangerous) as an Animal
object. And then you use it.
A function pointer cannot be a subclass of anything so I don't think the argument in that code is an Animal subclass... rather the subclass to the Animal class is the object returned by that function . Assuming that is also a polymorphic class, you will then be able to call its methods with the virtual inheritance rules. If you intend to check the pointer received by the function call and you're unsure whether it is a subclass of the Animal
class, you'd rather be using dynamic_cast .
As a sidenote: converting between function pointers and void* is a bad practice in C++ since you lose valuable type information.
The typedef line is AnimalCreation being defined as a function pointer type
Function foo takes in a void * argument which it casts into an AnimalCreation type (ie into the function pointer type). It can then invoke the function via the function pointer. This invocation returns a void * (as per the typedef - the part before the firts bracket is the return type, hence void*) which is then casted to an Animal* by reinterpret_cast.
If you removed the asterisk from the typdef - it would still declare a function pointer type, but now the return value would be void instead of void * (ie nothing returned, rather than a pointer). You could still invoke the function via the function pointer, but it would not return anything.
All in all, this is a nice little function pointer tutorial.
EDIT : the big picture of what this code seems to be doing - this is one way of implementing a 'Factory Pattern' in C++ - abstracting the creation of an object, and returning a polymorphic base class pointer to a derived class. Casting between void * and function pointers and reinterpret_cast is not the nicest way to achieve this, for alternatives you could look here
First off, this is quite ugly C-style code.
typedef void*(*AnimalCreation)();
To interpret this, follow the general rule of C & C++ declaration reading: if you type the declaration as an expression, you'll get its type.
*AnimalCreation
This means AnimalCreation
is a pointer (*AnimalCreation)()
This means *AnimalCreation
is a function taking no arguments, so AnimalCreation
is a pointer to function taking no arguments void *(*AnimalCreation)()
This means (*AnimalCreation)()
is a void*
(= pointer to void
), so AnimalCreation
is a pointer to a function which takes no arguments and returns a void*
. If it was just typedef void (*AnimalCreation)();
, it would be a pointer to a function taking no arguments and returning no value (ie returning void
).
Now, foo()
.
That takes a void*
(pointer to anything) and interprets it as AnimalCreation
- as a pointer to function taking no arguments and returning a void*
. If the argument passed to foo
was actually of that type, all is well. If something else is passed in, the program will exhibit Undefined Behaviour, which means anything can happen. It would most likely crash, as it could be trying to interpret data as code, for example.
foo()
calls that function passed in, which returns a void*
. foo()
then interprets that as a pointer to Animal
. If that's what the function actually returned, great. If not, Undefined Behaviour again.
Finally, the call you're showing will force the Undefined Behaviour to happen, because it's passing in the address of a pointer to an object. But, as stated above, foo()
will interpret that as the address of a function, and try to call that function. Hilarity ensues.
To summarize, such code is bad and its author should feel bad. The only place you'd expect to see such code is interoperability with a C-style external library, and in such case it should be extremely well documented.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.