简体   繁体   中英

pthread_create confusion

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 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?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM