可能重复:
Regular cast vs. static_cast vs. dynamic_cast
Undefined, unspecified and implementation-defined behavior
我面临一个奇怪的问题。在下面的代码片段中,我定义了一个类
class NewClass
{
public:
void Test()
{
cout<<"NewClass Test"<<endl;
}
};
在我的main()方法中,我写道:
void main()
{
int *ptr = new int();
NewClass *n = ((NewClass *)ptr);
n->Test();
}
并显示“NewClass Test”。我不明白它是如何键入任何指向NewClass的指针的,并且仍然有效。
提前致谢!
答案 0 :(得分:3)
这是静态派遣工作。在这种情况下,this
实际上是不必要的(例如NewClass::Test()
中没有使用或依赖它。)
转换为NewClass *n = ((NewClass *)ptr);
是按地址类型转换,并且在此上下文中没有类型检查。换句话说,您不是在任何地方创建新的NewClass
实例,而只是将int*
指定的地址处的内存视为NewClass*
。这是一种危险的转换,应该避免。如果您需要通过丢失类型安全性的地址(例如void*
)汇集对象,请始终确保两端都知道发送和接收的内容。幸运的是,擦除类型安全性变得越来越不常见。
结果未定义,但在大多数情况下您应该预期会产生不良副作用,并且应该不惜一切代价避免重新解释数据。
在这种情况下,编译器可能会插入结果,因为它知道它们。此外,没有出现任何错误,因为在这种情况下没有实际依赖于对象的地址或状态:Test()
不依赖于this
的state / data / members / dynamic methods / vtable
如果你要将std::string
的成员添加到NewClass
并打印出来......那么你可以期待事情比现在更快爆发:)
如果危险不明显:这是极其危险的转换 - int*
支持的所有数据都被重新解释为NewClass*
,以及它的所有内部存储器和结构(例如vtable和magic cookies)会相应地重新解释。不久之后你的程序会出现错误,要么超出分配结束时的读数(int*
),要么将int
视为完全不相关的类型 - 在这种情况下,请考虑具有vtable或数据的类的内存布局,例如向std::string
添加一些NewClass
,以及读取和写入这些成员。
答案 1 :(得分:1)
在你开始考虑复杂的原因之前,为什么它不应该工作,考虑一个简单的场景来帮助你尝试和想象它。
类是附加了方法的数据结构。当然,编译器是不同的,因此行为可以被认为是未定义的,但暂时忽略它。
你有一个空数据结构(即没有数据),但仍然附加了一个方法 - Test()。
因此,当您声明指向某个东西的指针(在您的护理中为int)时,指针仅指向某个内存。现在你有一个新的Int(),因此ptr指向的内存是整数大小。
由于你的类没有数据,并且它没有内部结构,需要内存中的对象以特定方式(例如虚拟方法)在内存中布局,你可以认为你指向任何东西或者事实没什么,因而可以称呼你的方法。
创建一个这样的类,看看会发生什么:
class NewClass
{
private int i;
public:
void Test()
{
cout<<"NewClass Test i="<< i << endl;
}
};
void main()
{
int *ptr = new int();
*ptr = 10;
NewClass *n = ((NewClass *)ptr);
n->Test();
}
看看它打印出来的东西。
如果你理解这一点 - 试着阅读你的编译器如何列出对象。这将告诉您很多关于您的平台上存在此行为的原因。
答案 2 :(得分:0)
这似乎是未定义的行为。但是,您总是可以在c ++中使用reinterpret-cast
来执行此操作。重新使用reinterpret_cast运算符可能很容易导致不安全。除非所需的转换本质上是低级别的,否则您应该使用其他一个转换运算符。
reinterpret_cast运算符可用于转换,例如char *到int *,或One_class *到Unrelated_class *,这些转换本质上是不安全的。
答案 3 :(得分:0)
您的方法未声明为虚拟。这意味着对它的调用完全由编译器解决,就像它是一个非方法函数一样,除了你必须在一个正式为NewClass
的变量上调用它。< / p>
您的编译器可能使用virtual method tables来调度对虚拟方法的调用,如果该方法是虚拟的,您可能最终会使用垃圾代替VMT,然后您就会开始崩溃。
也就是说,行为是不明确的,这意味着在任何一种情况下都会发生任何事情。