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?
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 typeBase*. I’m assumingclass Derivedinherits fromclass Base, so the assignment there actually causes a “loss of information” from the standpoint that while it’s a pointer of typeBase*you lose any non-polymorphic behavior of theDerivedclass. 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 anew Baseobject and started up your new thread with that, and in ThreadFun() you casted that to aDerived*, 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
Derivedclasses being of typeBase*…. If theBase::~Base()destructor is NOTvirtual, if you delete yourDerivedobject by callingdeleteon aBase*to that object, the allocation will be fully deallocated, but only theBaseportion of the object will have its destructor run….. UNLESS the destructors were defined with thevirtualkeyword, which allows you to delete aDerivedobject even with only aBase*to it. Have I made myself perfectly unclear?