是否有实际用途的dynamic铸造无效指针?

在C ++中, T q = dynamic_cast<T>(p); 构造执行一个指针p到一些其他指针typesT的运行时间转换,它必须出现在dynamictypes*p的inheritance层次结构中才能成功。 这一切都很好。

但是,也可以执行dynamic_cast<void*>(p) ,它将简单地返回一个指向“最大派生对象”的指针(参见C ++ 11中的5.2.7 :: 7)。 我明白,这个function可能在dynamic演员的执行中免费出来,但是在实践中有用吗? 毕竟,它的返回types至多是void* ,所以这有什么好处呢?

dynamic_cast<void*>()确实可以用来检查身份,即使处理多重inheritance。

试试这个代码:

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

输出:

 eq: 0, eqdc: 1 

请记住,C ++可以让你以旧C的方式来做事情。

假设我有一些API被迫通过void*types走私对象指针,但是最终传递给它的callback将知道它的dynamictypes:

 struct BaseClass { typedef void(*callback_type)(void*); virtual callback_type get_callback(void) = 0; virtual ~BaseClass() {} }; struct ActualType: BaseClass { callback_type get_callback(void) { return my_callback; } static void my_callback(void *p) { ActualType *self = static_cast<ActualType*>(p); ... } }; void register_callback(BaseClass *p) { // service.register_listener(p->get_callback(), p); // WRONG! service.register_listener(p->get_callback(), dynamic_cast<void*>(p)); } 

错的! 代码是错误的,因为它在多重inheritance的情况下失败(并且不保证在没有的情况下工作)。

当然,这个API并不是非常的C ++风格,如果我inheritance了ActualType那么即使是“正确的”代码也会出错。 所以我不会声称这是一个辉煌的使用dynamic_cast<void*> ,但它是一个用途。

投射指向void*指针自C天以来就具有其重要性。 操作系统内存pipe理器内部最合适的地方。 它必须存储你创build的所有指针和对象。 通过将其存储在void *中,它将其推广到将任何对象存储到可能是heap/B+Tree或简单数组列表的内存pipe理器数据结构上。

为了简单起见,创build一个通用项目list的例子(列表包含完全不同的类的项目)。 这将只能使用void*

标准说dynamic_cast应该为非法types转换返回null,标准还保证任何指针都能够将types转换为void *,并且只有函数指针除外。

对于void*types转换,正常应用程序的实际使用情况非常less,但是它被广泛用于低级/embedded式系统。

通常你会想使用reinterpret_cast作为低级别的东西,就像在8086中,它被用来抵消同一个基地址的指针来获取地址,但不限于此。

编辑:标准说,即使使用dynamic_cast<> ,您可以将任何指针转换为void* ,但不能在其中声明不能将void*转换回对象。

对于大多数用法,它是一条单行道,但有一些不可避免的用法。

它只是说dynamic_cast<>需要types信息将其转换回请求的types。

有很多API要求你将void*传递给某个对象。 java / Jni代码以void*传递对象。
没有types信息,你不能做铸造。 如果你足够自信,请求的types是正确的,你可以让编译dynmaic_cast<>做一个技巧。

看看这个代码:

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

请纠正我,如果这是不正确的任何方式。

当我们把存储放回内存池时,它是有用的,但是我们只保留一个指向基类的指针。 这个情况我们应该找出原来的地址。

根据@ BruceAdi的回答,并从这个讨论中得到启发,这里有一个可能需要指针调整的多态情况。 假设我们有这个工厂types的设置:

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

现在我可以说:

 Base * p = Factory(); 

但是,我将如何手动清理呢? 我需要实际的内存地址来调用::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 

或者我可以重新使用内存:

 void * addr = dynamic_cast<void*>(p); p->~Base(); p = InplaceFactory(addr, "some", "arguments"); delete p; // OK now 

不要在家里这样做

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

警告 :如果D被定义为:这将不起作用。

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

而且没有办法使它工作。

这可能是通过ABI提供不透明指针的一种方法。 不透明指针 – 更一般地说, 不透明数据types – 用于在库代码和客户端代码之间传递对象和其他资源,以便客户端代码可以与库的实现细节隔离。 还有其他 方法可以做到这一点,可以肯定的是,其中一些方法对于特定用例会更好。

Windows在其API中大量使用了不透明指针。 HANDLE ,我相信HANDLE通常是一个指向实际资源的不透明指针。 HANDLE可以是文件,GDI对象等内核对象,也可以是各种types的用户对象 – 所有这些对象在执行时必须有很大的不同,但都是作为HANDLE返回给用户的。

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