Are there practical uses for dynamic-casting to void pointer?

后端 未结 7 1239
隐瞒了意图╮
隐瞒了意图╮ 2020-11-27 14:00

In C++, the T q = dynamic_cast(p); construction performs a runtime cast of a pointer p to some other pointer type T that must

相关标签:
7条回答
  • 2020-11-27 14:32

    Expanding on @BruceAdi's answer and inspired by this discussion, here's a polymorphic situation which may require pointer adjustment. Suppose we have this factory-type setup:

    struct Base { virtual ~Base() = default; /* ... */ };
    struct Derived : Base { /* ... */ };
    
    template <typename ...Args>
    Base * Factory(Args &&... args)
    {
        return ::new Derived(std::forward<Args>(args)...);
    }
    
    template <typename ...Args>
    Base * InplaceFactory(void * location, Args &&... args)
    {
        return ::new (location) Derived(std::forward<Args>(args)...);
    }
    

    Now I could say:

    Base * p = Factory();
    

    But how would I clean this up manually? I need the actual memory address to call ::operator delete:

    void * addr = dynamic_cast<void*>(p);
    
    p->~Base();              // OK thanks to virtual destructor
    
    // ::operator delete(p); // Error, wrong address!
    
    ::operator delete(addr); // OK
    

    Or I could re-use the memory:

    void * addr = dynamic_cast<void*>(p);
    p->~Base();
    p = InplaceFactory(addr, "some", "arguments");
    
    delete p;  // OK now
    
    0 讨论(0)
  • 2020-11-27 14:35

    Casting pointers to void* has its importance since way back in C days. Most suitable place is inside the memory manager of Operating System. It has to store all the pointer and the object of what you create. By storing it in void* they generalize it to store any object on to the memory manager data structure which could be heap/B+Tree or simple arraylist.

    For simplicity take example of creating a list of generic items(List contains items of completely different classes). That would be possible only using void*.

    standard says that dynamic_cast should return null for illegal type casting and standard also guarantees that any pointer should be able to type cast it to void* and back from it with only exception of function pointers.

    Normal application level practical usage is very less for void* typecasting but it is used extensively in low level/embedded systems.

    Normally you would want to use reinterpret_cast for low level stuff, like in 8086 it is used to offset pointer of same base to get the address but not restricted to this.

    Edit: Standard says that you can convert any pointer to void* even with dynamic_cast<> but it no where states that you can not convert the void* back to the object.

    For most usage, its a one way street but there are some unavoidable usage.

    It just says that dynamic_cast<> needs type information for converting it back to the requested type.

    There are many API's that require you to pass void* to some object eg. java/Jni Code passes the object as void*.
    Without type info you cannot do the casting.If you are confident enough that type requested is correct you can ask compiler to do the dynmaic_cast<> with a trick.

    Look at this code:

    class Base_Class {public : virtual void dummy() { cout<<"Base\n";} };
    class Derived_Class: public Base_Class { int a; public: void dummy() { cout<<"Derived\n";} };
    class MostDerivedObject : public Derived_Class {int b; public: void dummy() { cout<<"Most\n";} };
    class AnotherMostDerivedObject : public Derived_Class {int c; public: void dummy() { cout<<"AnotherMost\n";} };
    
    int main () {
      try {
        Base_Class * ptr_a = new Derived_Class;
        Base_Class * ptr_b = new MostDerivedObject;
        Derived_Class * ptr_c,*ptr_d;
    
            ptr_c = dynamic_cast< Derived_Class *>(ptr_a);
            ptr_d = dynamic_cast< Derived_Class *>(ptr_b);
    
            void* testDerived = dynamic_cast<void*>(ptr_c);
            void* testMost = dynamic_cast<void*>(ptr_d);
            Base_Class* tptrDerived = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testDerived));
            tptrDerived->dummy();
            Base_Class* tptrMost = dynamic_cast<Derived_Class*>(static_cast<Base_Class*>(testMost));
            tptrMost->dummy();
            //tptrMost = dynamic_cast<AnotherMostDerivedObject*>(static_cast<Base_Class*>(testMost));
            //tptrMost->dummy(); //fails
    
        } catch (exception& my_ex) {cout << "Exception: " << my_ex.what();}
        system("pause");
      return 0;
    }
    

    Please correct me if this is not correct in any way.

    0 讨论(0)
  • 2020-11-27 14:36

    Don't do that at home

    struct Base {
        virtual ~Base ();
    };
    
    struct D : Base {};
    
    Base *create () {
        D *p = new D;
        return p;
    }
    
    void *destroy1 (Base *b) {
        void *p = dynamic_cast<void*> (b);
        b->~Base ();
        return p;
    }
    
    void destroy2 (void *p) {
        operator delete (p);
    }
    
    int i = (destroy2 (destroy1 (create ())), i);
    

    Warning: This will not work if D is defined as:

    struct D : Base {
        void* operator new (size_t);
        void operator delete (void*);
    };
    

    and there is no way to make it work.

    0 讨论(0)
  • 2020-11-27 14:43

    it is usefull when we put the storage back to memory pool but we only keep a pointer to the base class. This case we should figure out the original address.

    0 讨论(0)
  • 2020-11-27 14:50

    This might be one way to provide an Opaque Pointer through an ABI. Opaque Pointers -- and, more generally, Opaque Data Types -- are used to pass objects and other resources around between library code and client code in such a way that the client code can be isolated from the implementation details of the library. There are other ways to accomplish this, to be sure, and maybe some of them would be better for a particular use case.

    Windows makes a lot of use of Opaque Pointers in its API. HANDLE is, I believe, generally an opaque pointer to the actual resource you have a HANDLE to, for example. HANDLEs can be Kernel Objects like files, GDI objects, and all sorts of User Objects of various kinds -- all of which must be vastly different in implementation, but all are returned as a HANDLE to the user.

    #include <iostream>
    #include <string>
    #include <iomanip>
    using namespace std;
    
    
    /*** LIBRARY.H ***/
    namespace lib
    {
        typedef void* MYHANDLE;
    
        void        ShowObject(MYHANDLE h);
        MYHANDLE    CreateObject();
        void        DestroyObject(MYHANDLE);
    };
    
    /*** CLIENT CODE ***/
    int main()
    {
        for( int i = 0; i < 25; ++i )
        {
            cout << "[" << setw(2) << i << "] :";
            lib::MYHANDLE h = lib::CreateObject();
            lib::ShowObject(h);
            lib::DestroyObject(h);
            cout << "\n";
        }
    }
    
    /*** LIBRARY.CPP ***/
    namespace impl
    {
        class Base { public: virtual ~Base() { cout << "[~Base]"; } };
        class Foo   : public Base { public: virtual ~Foo() { cout << "[~Foo]"; } };
        class Bar   : public Base { public: virtual ~Bar() { cout << "[~Bar]"; } };
    };
    
    lib::MYHANDLE lib::CreateObject()
    {
        static bool init = false;
        if( !init )
        {
            srand((unsigned)time(0));
            init = true;
        }
    
        if( rand() % 2 )
            return static_cast<impl::Base*>(new impl::Foo);
        else
            return static_cast<impl::Base*>(new impl::Bar);
    }
    
    void lib::DestroyObject(lib::MYHANDLE h)
    {
        delete static_cast<impl::Base*>(h);
    }
    
    void lib::ShowObject(lib::MYHANDLE h)
    {
        impl::Foo* foo = dynamic_cast<impl::Foo*>(static_cast<impl::Base*>(h));
        impl::Bar* bar = dynamic_cast<impl::Bar*>(static_cast<impl::Base*>(h));
    
        if( foo ) 
            cout << "FOO";
        if( bar )
            cout << "BAR";
    }
    
    0 讨论(0)
  • 2020-11-27 14:51

    The dynamic_cast<void*>() can indeed be used to check for identity, even if dealing with multiple inheritance.

    Try this code:

    #include <iostream>
    
    class B {
    public:
        virtual ~B() {}
    };
    
    class D1 : public B {
    };
    
    class D2 : public B {
    };
    
    class DD : public D1, public D2 {
    };
    
    namespace {
        bool eq(B* b1, B* b2) {
            return b1 == b2;
        }
    
        bool eqdc(B* b1, B *b2) {
            return dynamic_cast<void*>(b1) == dynamic_cast<void*>(b2);
        }
    };
    
    int
    main() {
        DD *dd = new DD();
        D1 *d1 = dynamic_cast<D1*>(dd);
        D2 *d2 = dynamic_cast<D2*>(dd);
    
        std::cout << "eq: " << eq(d1, d2) << ", eqdc: " << eqdc(d1, d2) << "\n";
        return 0;
    }
    

    Output:

    eq: 0, eqdc: 1
    
    0 讨论(0)
提交回复
热议问题