在C ++中,T q = dynamic_cast<T>(p);
构造将指针p
的运行时强制转换为其他指针类型T
,这些指针类型必须出现在动态类型{{1}的继承层次结构中。为了成功。这一切都很好。
但是,也可以执行*p
,它只返回指向“最派生对象”的指针(参见C ++ 11中的5.2.7 :: 7)。我知道这个功能可能在动态转换的实现中免费提供,但它在实践中是否有用?毕竟,它的返回类型最多是dynamic_cast<void*>(p)
,那么这有什么用呢?
答案 0 :(得分:69)
dynamic_cast<void*>()
确实可用于检查身份,即使处理多重继承也是如此。
试试这段代码:
#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
答案 1 :(得分:7)
请记住,C ++允许你用旧的C方式做事。
假设我有一些API,我不得不通过类型void*
走私对象指针,但最终传递给它的回调将知道它的动态类型:
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));
}
错了!代码是错误的,因为它在存在多重继承时失败(并且不保证在没有的情况下也能工作)。
当然,API不是很C ++风格,如果我继承ActualType
,即使是“正确”的代码也可能出错。所以我不会声称这是dynamic_cast<void*>
的精彩用法,但它是一种用途。
答案 2 :(得分:4)
从{C}天开始,向void*
投射指针有其重要性。
最合适的地方是操作系统的内存管理器。它必须存储您创建的所有指针和对象。通过将其存储在void *中,它们将其概括为将任何对象存储到内存管理器数据结构中,该结构可以是heap/B+Tree
或简单arraylist
。
为简单起见,以创建list
通用项为例(List包含完全不同类的项)。只有使用void*
才能实现这一点。
标准说dynamic_cast应该为非法类型转换返回null,而且标准还保证任何指针都应该能够键入将它转换为void *并从它返回,只有函数指针除外。
void*
类型转换的正常应用程序级实际使用率非常低,但它在低级/嵌入式系统中广泛使用。
通常你会想要将reinterpret_cast用于低级别的东西,比如在8086中它用于偏移相同基数的指针来获取地址但不限于此。
修改强>
标准表示即使使用void*
,您也可以将任何指针转换为dynamic_cast<>
,但不能说明您无法将void*
转换回对象。
对于大多数用途而言,它是一条单行道,但有一些不可避免的用途。
它只是说dynamic_cast<>
需要类型信息才能将其转换回请求的类型。
有许多API要求您将void*
传递给某个对象,例如。 java / Jni Code将对象传递为void*
如果没有类型信息,则无法进行转换。如果您对所请求的类型是正确的有足够的信心,您可以要求编译器使用技巧执行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;
}
如果这种方法不正确,请纠正我。
答案 3 :(得分:1)
当我们将存储放回内存池时,它很有用,但我们只保留指向基类的指针。这种情况我们应该找出原来的地址。
答案 4 :(得分:1)
扩展@ BruceAdi的答案并受到this discussion的启发,这是一个多态的情况,可能需要指针调整。假设我们有这种工厂类型的设置:
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
答案 5 :(得分:0)
这可能是通过ABI提供Opaque Pointer的一种方法。不透明指针 - 更常见的是Opaque Data Types - 用于在库代码和客户端代码之间传递对象和其他资源,使客户端代码可以与库的实现细节隔离。有other ways来完成这一点,当然,也许其中一些对于特定用例会更好。
Windows在其API中大量使用了不透明指针。我相信,HANDLE
通常是指向您拥有HANDLE
的实际资源的不透明指针。 HANDLE
可以是内核对象,如文件,GDI对象,以及各种各样的用户对象 - 所有这些对象在实现中必须完全不同,但所有对象都以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";
}
答案 6 :(得分:0)
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*); };
并且没有办法让它发挥作用。