Reputation: 6008
In many occasions I have seen this situation
class Foo
{
static void* ThreadFun(void* p)
{
Derived* args = (Derived*)p;
//do something with args.
//example
//cout << args->getname();
}
void function()
{
Base* args = new Derived();
args->setname("test");
pthread_t id;
int ret = pthread_create(&id, NULL, ThreadFun, (void*) args);
}
}
First, is that proper C++ code? or am I missing something? I have done some readings and apparently casting a pointer to class to a void* causes loss of information and calling getname() on the derived class can be illegal.
What they suggest if I understood correctly is something like this:
void function()
{
Base* args = new Derived();
args->setname("test");
void* pargs = (Base*)malloc(sizeof(*args)); // to be freed in ThreadFun
pargs = args;
pthread_t id;
int ret = pthread_create(&id, NULL, ThreadFun, pargs );
}
I don't get it really, how do I need to this properly?
Upvotes: 0
Views: 159
Reputation: 7873
Oh, wow... No, your 2nd example leaks the memory allocated by malloc(), and from the comment, if you follow that advice, you'll be free()'ing your new Derived()
allocation in ThreadFun when you think you're freeing the malloc() allocation.
With regard to casting a pointer to a class to void* causing a loss of information.... It doesn't. Plain & simple. What it loses is the compiler's understanding of what that pointer means. If you know what it means, you can always cast it back to its proper type & get its full functionality back, which is exactly what the first example does. When the ThreadFun() function starts in the new thread, it casts the void* back to its original type, which is Derived*.
I'm noticing that when you initially called for the new Derived()
allocation, you assigned the return pointer to a pointer of type Base*
. I'm assuming class Derived
inherits from class Base
, so the assignment there actually causes a "loss of information" from the standpoint that while it's a pointer of type Base*
you lose any non-polymorphic behavior of the Derived
class. But by the time you cast it back to its real type in ThreadFun(), it regains its full functionality. Note that if instead you allocated a new Base
object and started up your new thread with that, and in ThreadFun() you casted that to a Derived*
, the compiler would let you do that, but you'd have undefined behavior... probably a crash. Because (due to the pthreads interface) you have to go through a void*, there's no type-checking safety to be had, even with C++ style casts. So you could convert that void* to anything you wanted, and the compiler would let you. But of course the only truly valid casts are either (Base*)
or (Derived*)
, or anything in between them in the inheritance hierarchy.
I should also mention that as a void*, you can't delete the object and have its destructor run. To delete the object, you need to cast that pointer back to whatever type it is in order for the compiler to know what destructor to call. There's another tricky sticky situation you can get into with pointers to Derived
classes being of type Base*
.... If the Base::~Base()
destructor is NOT virtual
, if you delete your Derived
object by calling delete
on a Base*
to that object, the allocation will be fully deallocated, but only the Base
portion of the object will have its destructor run..... UNLESS the destructors were defined with the virtual
keyword, which allows you to delete a Derived
object even with only a Base*
to it. Have I made myself perfectly unclear?
Upvotes: 3