4

可能重复:
常规强制转换、静态强制转换和动态强制转换
未定义、未指定和实现定义的行为

我面临一个奇怪的问题。在下面的代码片段中,我定义了一个类

   class NewClass
   {
      public:
         void Test()
         {
             cout<<"NewClass Test"<<endl;
         }
   };

在我的 main() 方法中,我写道:

        void main()
        {
           int *ptr = new int();
           NewClass *n = ((NewClass *)ptr);
           n->Test();
        }

它显示“NewClass 测试”。我不明白如何对任何指向 NewClass 的指针进行类型转换并且仍然可以正常工作。

提前致谢!

4

4 回答 4

3

那是工作中的静态调度。this在这种情况下确实是不必要的(例如,在 内不使用或依赖它NewClass::Test())。

强制NewClass *n = ((NewClass *)ptr);转换是按地址进行类型转换,并且在此上下文中没有类型检查。换句话说,您没有在NewClass任何地方创建新实例,您只是将位于指定地址的内存int*视为NewClass*. 这是一个危险的转换,应该避免。如果您需要通过丢失类型安全性的地址(例如void*)来汇集对象,请始终确保两端都知道发送和接收的内容。幸运的是,擦除类型安全变得越来越少。

结果是不确定的,但在大多数情况下,您应该预料到会产生不良的副作用,并且应该不惜一切代价避免重新解释数据。

在这种情况下,编译器可能会插入结果,因为它知道它们。此外,由于在这种情况下对对象的地址或状态没有实际依赖关系,因此没有出现错误:Test()不依赖this.

如果您要添加诸如std::strings之类的成员NewClass并将其打印出来……您可以预期事情会比现在更快地爆发:)

如果危险不明显:这是一种极其危险的转换——由 支持的所有数据int*都被重新解释为NewClass*,并且它的所有内部存储器和结构(例如 vtables 和魔术 cookie)都被重新解释。您的程序段错误很快就会出现,或者通过读取超出分配(int*)的末尾,或者通过将 anint视为完全不相关的类型 - 在这种情况下,请考虑具有 vtable 的类的内存布局或数据,例如将一些std::strings 添加到NewClass,以及读取和写入这些成员。

于 2012-05-01T06:33:28.247 回答
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(); 
    } 

看看它打印出来的东西。

如果您理解这一点 - 请尝试阅读您的编译器,了解它是如何布置对象的。这将告诉你很多关于为什么在你的平台上存在这种行为。

于 2012-05-01T06:41:56.153 回答
0

这似乎是未定义的行为。但是,您始终可以reinterpret-cast在 c++ 中使用 reinterpret_cast 运算符的误用很容易变得不安全。除非所需的转换本质上是低级的,否则您应该使用其他强制转换运算符之一。reinterpret_cast 运算符可用于 char* 到 int* 或 One_class* 到 Unrelated_class* 等转换,这些转换本质上是不安全的。

于 2012-05-01T06:36:06.560 回答
0

您的方法未声明为虚拟方法。这意味着对它的调用完全由编译器解析,就像它是一个非方法函数一样,除了你必须在一个正式的变量上调用它NewClass

您的编译器可能使用虚拟方法表来分派对虚拟方法的调用,如果该方法是虚拟的,您最终可能会用垃圾代替 VMT,然后您会开始崩溃。

也就是说,行为是未定义的,这意味着在任何一种情况下都可能发生任何事情。

于 2012-05-01T06:38:20.873 回答