一般如何从 void * 指针动态转换?

How do I dynamically cast from a void * pointer generically?

class BASE {
public:
    virtual ~BASE() {}
    void lamp() {
        cout << "\nBASE CLASS";
    }
};

class DERIVED : public BASE {
public:
    void fun();
};

void DERIVED::fun() {
    cout << "\nDERIVED CLASS!";
}

int main() {

    BASE * pbase = new DERIVED; //BASE CLASS POINTER
    void * vbase = pbase;       //VOID POINTER TAKING BASE POINTER 
    DERIVED * pder;             //DERIVED CLASS POINTER

    //pder = static_cast<DERIVED *>(vbase);  //THIS WORKS
    pder = dynamic_cast<DERIVED *>(vbase);   //THIS DOESN'T
    pder->lamp();
    pder->fun();

    return 0;
}

每当我尝试将 void* 指针动态转换为派生的 class 指针时,我都会收到以下错误:

cannot dynamic_cast 'vbase' (of type 'void*') to type 'class DERIVED*' (source is not a pointer to class)

我搜索了 Whosebug 并按照建议在基 class 中实现了一个虚函数来避免错误。我究竟做错了什么? 这可能吗?

我的总体意图是使用 void* 指针将任何传入的对象类型转换为派生的 class 类型。希望你明白我的意思。

例如:

void dynamicCast(void * vptr)
{
    BASE * pbase = new DERIVED;
    DERIVED * pder;

    pder = dynamic_cast<DERIVED *>(vbase);
}

我应该能够将任何类型的指针传递给 dynamicCast 函数,并且它应该被转换为派生的 class 指针。

您不能在 void * 上使用 dynamic_cast

根据规范,dynamic_cast<T>(v)

If T is a pointer type, v shall be a prvalue of a pointer to complete class type, and the result is a prvalue of type T. ...

你应该做的是让你所有的 classes 派生自同一个多态基 class(至少有一个虚函数)BASE,并使用 BASE * 而不是 void *.

你的通用链表应该是这样的:

class Node
{
    Node* next;
    void* vdata;
}

这是唯一严格要求的数据结构,但您可能需要一个循环列表,或一个跟踪列表末尾的基节点,或一个双向链表。

调用方传递给您一个 void*,您创建一个新节点,设置 "vdata" 并将该节点添加到您的列表中。

当您将指向对象类型的指针转​​换为指向 void 的指针时, 该 void 指针的有效转换是返回其原始类型。您不能在指向 void 的指针上使用 dynamic_cast,因为 void 不是多态类型,所以您可以使用 static_cast 来实现。像这样:

BASE *pbase = new DERIVED;
void *vbase = pbase; // Ok; implicit conversion to void*
BASE *pbase1 = static_cast<BASE*>(vbase); // the cast is required

一旦你得到了指向BASE的指针,当然,你可以使用dynamic_cast将其转换为指向派生类型的指针:

DERIVED *pder = dynamic_cast<DERIVED*>(pbase1);

我认为有一个设计or/and理解问题

如前所述,您不能 dynamic_cast 来自 void*

为什么?因为 dynamic_cast 需要一些 运行-时间类型信息 (RTTI) 来进行转换(请参阅 this link 了解更多详细信息)。仅从 void* 开始,C++ 代码就没有机会知道此信息在哪里。最低限度是使用指向具有此类 RTTI 信息的对象的指针。

创建此信息并将其绑定到任何具有至少一个 虚方法 的 class。如果没有虚拟方法,则不包括此信息。这就是这个 工作的原因:

struct A
{
};

struct B : A
{
};

int main()
{
  B  b;
  A *a = &b;

  dynamic_cast<B *>(a);  // YOUR COMPILE TIME ERROR
}

一个解决方法是给 A 添加一个虚方法。这里我添加了一个虚析构函数 this is generally a good thing

struct A
{
   virtual ~A() = default;
};

struct B : A
{
};

int main()
{
  B  b;
  A *a = &b;

  dynamic_cast<B *>(a);  // OK
}

另请注意,dynamic_cast 允许您检查转换是否合法。

例如我们可以添加 C class 并检查:

struct C 
{
};

int main()
{
  B  b;
  A *a = &b;

  assert(dynamic_cast<B *>(a)!=nullptr); // OK
  assert(dynamic_cast<C *>(a)==nullptr); // OK can not cast A to C 
}

这种运行-time的操作不是免费的。如果你搜索最大速度可以使用这个技巧:

assert(dynamic_cast<B *>(a)!=nullptr);
B* b=static_cast<B*>(a);

在调试模式下,您将检查是否一切正常,在发布模式下,使用 -DNDEBUG 标志删除 assert 您将只使用 static_cast