【问题标题】:How do I dynamically cast from a void * pointer generically?我一般如何从 void * 指针动态转换?
【发布时间】:2018-02-07 14:17:19
【问题描述】:
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* 指针动态转换为派生类指针时,都会出现以下错误:

不能将'vbase'('void*'类型)动态转换为'class DERIVED*'类型(源不是指向类的指针)

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

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

例如:

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

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

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

【问题讨论】:

  • 你所要求的基本上是不可能的。你为什么要这样做?也许还有另一种方法可以实现。
  • 答案是:你不知道。
  • void * 指针不能安全地转换为BASE *DERIVED *,因为它们不相关。您不能将static_castdynamic_cast 用于此目的。
  • 您可以将 void * 存储在列表中,您无需将其转换为任何内容。你需要一个类来存储 void *
  • 不,这很容易。您只是无法监视传递给您的数据。不要将 void* 用作节点,它是附加到节点的数据。

标签: c++ void-pointers dynamic-cast


【解决方案1】:

我认为存在设计或/和理解问题

正如解释的那样,您不能从void* 中获得dynamic_cast

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

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

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 类并检查:

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 
}

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

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

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

【讨论】:

    【解决方案2】:

    当您将指向对象类型的指针转​​换为指向 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);
    

    【讨论】:

      【解决方案3】:

      您不能在void * 上使用dynamic_cast

      根据规范,对于dynamic_cast&lt;T&gt;(v)

      如果T是指针类型,v应该是指向完整类类型的指针的prvalue,结果是T类型的prvalue。 ...

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

      【讨论】:

      • 这是给我的任务。我必须使用头文件实现各种链表功能。函数声明位于标头中,定义位于单独的 .cpp 文件中。插入新节点的函数必须采用 void 指针,因为用户可以使用 main() 函数将任何类型的对象作为节点在文件中具有不同数量的成员。我希望你明白我想说什么。 TL:博士。 3 个文件:一个用于保存函数声明的标头,一个用于定义函数的 .cpp 文件以及另一个用于实现函数的 .cpp 文件。
      • 您需要访问链表中的 void *,还是只有链表的用户需要知道 void * 中的内容?
      • 但是 void *vbase = pbase.它有 Base 指针对吗?还是我错了?
      • 如果您的链表需要访问 void * 中的某些函数,则将它们放在一个基类中,所有东西都从该基类继承。此处不能使用 void *,必须使用基 *。如果没有,list 的用户需要跟踪 void * 并使用适当的强制转换将其转换回来。但不是 dynamic_cast。
      • 类型信息在 C++ 中非常重要,使用 void * 你会丢失它。它们可能具有相同的内部价值,但行为不同。
      【解决方案4】:

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

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

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

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

      【讨论】:

      • 注意,调用者必须知道何时释放 void* 指向的数据。
      猜你喜欢
      • 2016-11-10
      • 2021-09-20
      • 1970-01-01
      • 2022-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-28
      相关资源
      最近更新 更多