简体   繁体   中英

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

Whenever I try to dynamically cast the void* pointer to the derived class pointer, I get the following error:

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

I've searched StackOverflow and followed advice by implementing a virtual function in the base class to avoid the error. What am I doing wrong? Is this possible at all?

My overall intention is to cast ANY incoming Object type into a Derived class type using a void* pointer. I hope you understand what I mean.

For example:

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

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

I should be able to pass any type of pointer to the dynamicCast function and it should be converted to the derived class pointer.

When you convert a pointer to an object type into a pointer to void, the only valid conversion for that void pointer is back to its original type . You can't use dynamic_cast on a pointer to void because void is not a polymorphic type, so you do it with a static_cast . Like this:

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

Once you've gotten back the pointer to BASE , of course, you can use dynamic_cast to convert it to a pointer to the derived type:

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

You cannot use dynamic_cast on a void * .

From the specification, for 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. ...

What you should do is to let all your classes derive from the same polymorphic base class (which has at least one virtual function) BASE , and use BASE * instead of void * .

Your generic linked list should be like this:

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

That's the only data structure strictly required, but you might want a circular list, or a base node to keep track of the end of the list, or a doubly-linked list.

Caller passes you a void*, you create a new node, set "vdata" and add the node to your list.

I think there is a design or/and comprehension problem

As explained you can not dynamic_cast from a void* .

Why? Because dynamic_cast needs some Run-Time Type Information (RTTI) to do the cast (see this link for further details). From void* alone, the C++ code has no chance to know where this information is. The minimum is to use a pointer to an object having such RTTI information.

This information is created and bounded to any class having at least one virtual method . If there is no virtual method this information is not included. That is the reason why this does not work:

struct A
{
};

struct B : A
{
};

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

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

A fix is to add a virtual method to A. Here I have added a virtual destructor as 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
}

Also note that dynamic_cast allows you to check that the conversion was legal.

For instance we can add a C class and check:

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 
}

This kind of run-time operations are not free. If you search for maximum speed to can use this trick:

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

In debug mode you will check if everything is ok and in release mode with the -DNDEBUG flag to remove the assert you will only use the static_cast

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