문제

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, i.e. 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 (i.e. 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 * (i.e. 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.

  1. *AnimalCreation This means AnimalCreation is a pointer
  2. (*AnimalCreation)() This means *AnimalCreation is a function taking no arguments, so AnimalCreation is a pointer to function taking no arguments
  3. 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 (i.e. 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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top