0

在很多场合我都看到过这种情况

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);    
    }
}

首先,那是正确的 C++ 代码吗?还是我错过了什么?我做了一些阅读,显然将指向类的指针转换为 void* 会导致信息丢失,并且在派生类上调用 getname() 可能是非法的。

如果我理解正确,他们的建议是这样的:

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 );    
}

我真的不明白,我该如何正确地做到这一点?

4

1 回答 1

3

哦,哇...不,您的第二个示例泄漏了 malloc() 分配的内存,并且从评论中,如果您遵循该建议,那么new Derived()当您认为自己是时,您将可以在 ThreadFun 中进行分配释放 malloc() 分配。

关于将指向类的指针转换为 void* 导致信息丢失......它没有。简单明了。它失去的是编译器对该指针含义的理解。如果知道它的含义,您总是可以将其转换回正确的类型并恢复其全部功能,这正是第一个示例所做的。当 ThreadFun() 函数在新线程中启动时,它将 void* 转换回其原始类型,即 Derived*。

我注意到,当您最初调用new Derived()分配时,您将返回指针分配给 type 的指针Base*。我假设class Derived继承自class Base,所以从这个角度来看,那里的赋值实际上会导致“信息丢失”,虽然它是一个类型的指针,但Base*你会丢失类的任何非多态行为Derived。但是当您在 ThreadFun() 中将其转换回其真实类型时,它会恢复其全部功能。请注意,如果您分配了一个new Base对象并使用它启动了新线程,并且在 ThreadFun() 中将其转换为Derived*,编译器会让你这样做,但你会有未定义的行为......可能会崩溃。因为(由于 pthreads 接口)您必须通过 void*,所以即使使用 C++ 样式转换,也没有类型检查安全性。因此,您可以将该 void* 转换为您想要的任何内容,编译器会允许您这样做。但当然,唯一真正有效的转换是(Base*)or (Derived*),或者继承层次结构中它们之间的任何东西。

我还应该提到,作为 void*,您不能删除对象并运行其析构函数。要删除该对象,您需要将该指针转换回它的任何类型,以便编译器知道要调用什么析构函数。还有另一种棘手的棘手情况,您可以使用指向Derived类型为Base*... 的类的指针。如果Base::~Base()析构函数不是virtual,如果您Derived通过调用deleteBase*对象来删除对象,则分配将被完全解除分配,但只有Base部分对象的析构函数将运行.....除非析构函数是用关键字定义的,否则即使只有一个对象virtual,您也可以删除它。我让自己完全不清楚了吗?DerivedBase*

于 2012-12-08T06:53:39.640 回答