dynamic_cast转换为相同类型不会检查对象的类型

dynamic_cast to the same type does not check type of object

本文关键字:检查 对象 类型 同类型 cast 转换 dynamic      更新时间:2023-10-16

我正在尝试确定由T*指针指向的对象是否真的是T对象,或者其他一些不相关的类型。我尝试了dynamic_cast,但它并没有那么无用,它返回指针本身而不是null,即使很明显它没有指向有效的T对象:

Object* garbage = reinterpret_cast<Object*>(0x12345678);
if( dynamic_cast<Object*>(garbage) == NULL ){
    cout << "Expected behaviour (by me)" << endl;
}else{
    cout << "You've got to be kidding me" << endl;
}

有没有解决办法,或者其他解决方案?我曾尝试在dynamic_cast之前转换void*和char*,但没有成功,typeid也不够,因为我也想接受子类。

一些上下文:我正在编写一个自定义Array类,实现不同类型的Array(如Array<Object*>Array<String*>(之间的浅层转换,我希望通过在每个元素访问时进行动态类型检查来保证最小的类型安全性,例如:

#define DEBUG
Array<String*> v(10);
Array<Object*> o = v;
o[0] = new Integer(1);      //  this is technically illegal but no idea how to check
//Array<String*> w = o;     //  this fails with an exception
String* str = v[0];         //  but this should fail horribly as well
cout << str << endl;

在很多情况下,强制转换为Object*,然后对Object*进行类型检查是有效的,但在Array<Object*>的情况下失败了,尽管我不确定是否可以在不使用repret_cast的情况下将非Object插入到Array<Object*>中。

根据您的示例,听起来您有一个浅拷贝数组,有人可以欺骗它来包含不同的类型。我认为这个问题的"正常"解决方案是让用户很难做到这一点(即不提供Array<T>Array<U>之间的转换(。但是,如果你有自己的想法,我认为这会奏效:

template<typename Subclass>
class Array {
public:
    // ...
    Subclass *operator [] (size_t index) {
        assert( index < size_ );
        assert( dynamic_cast<Subclass*>(static_cast<Object*>(internal_[index])) != NULL );
        // ...
    }
    // ...
private:
    size_t size_;
    Subclass **internal_;
};

你可以做一些模板元魔术和一个静态断言来确保Subclass真的是Object的一个子类(这完全是一个不同的主题(。一旦解决了这个问题,向下投射到Object*,然后用dynamic_cast返回到Subclass应该可以实现您的目标。

让我看看我是否满足了您的需求,并在此过程中提出了一些建议。。。

Array<String*> v(10);

似乎这是为了给您一个数组,其中10个String*s初始化为NULL/0。

Array<Object*> o = v;

创建一个v.size()/10个Object*的数组,每个都从v中的String*s复制。

o[0] = new Integer(1); //  technically illegal but no idea how to check

如果这是非法的,那么您显然希望防止覆盖Object* s,因为这会更改运行时类型。。。

  • 您需要截取operator=来实现before/after类型比较
  • 要拦截operator=,需要o[0]返回一个可以指定其operator=的类型
    • o[0]返回Object*永远不会起作用,因为指针不是用户定义的类:您不能修改operator=的行为
    • 您必须让o[0]返回一个代理对象——这里几乎是一个迭代器,尽管语义和赋值类型断言与标准容器迭代器不同

这就引出了:

Array<String*> w = o;     //  this fails with an exception

我认为这只是失败了,因为上面的o[0] = new Integer()并没有首先失败,而且例外是您故意测试元素类型是否符合预期:如果您使用所讨论的代理对象来阻止Integer进入Array<Object*>,那么这里没有问题。

String* str = v[0];     //  should fail horribly as well

同样,我猜这应该会失败,因为您之前的Integer作业没有,而且这里没有新的问题。

cout << str << endl;

因此,代理对象似乎是关键。如果你不知道如何写,请告诉我,但我猜你知道…

在C++中,从同一类型到同一类型的dynamic_cast被定义为无操作,因此它不能在您的示例中"失败"。您可以改用typeid运算符。

例如,这个程序很可能崩溃(这是从随机地址的对象中获取类型信息的"预期"结果(:

int main()
{
    Object* garbage = reinterpret_cast<Object*>(0x12345678);
    if (typeid(*garbage) == typeid(Object))
        cout << "Your program thinks this garbage is an actual object!" << std::endl;
}

只需引入一个带有虚拟析构函数的公共基类。对于空基优化,这可能不会增加任何开销,并且会使dynamic_cast正常工作。