将C++中的int指针强制转换为类类型

5
可能会有重复:
普通转换 vs. static_cast vs. dynamic_cast
未定义、未指定和实现定义的行为

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

   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 并使其正常工作。

提前致谢!


9
未定义行为意味着“任何事情”都有可能发生,包括让你误以为它起作用。 - Ben Voigt
4个回答

3
这是静态分派的作用,这种情况下 this 是不必要的(例如在 NewClass::Test() 中没有使用或依赖它)。
ptr转换为NewClass *n = ((NewClass *)ptr); 是通过地址进行类型转换,在此上下文中没有类型检查。换句话说,您并没有在任何地方创建一个新的NewClass实例,您只是将指定的int*地址处的内存视为NewClass*。这是一种危险的转换,应该避免。如果你需要通过失去类型安全的地址传递一个对象(例如void*),那么一定要确保两端知道发送和接收到的内容。幸运的是,消除类型安全性变得越来越少见。
结果是未定义的,但在大多数情况下会有负面影响,因此您应该尽量避免重新解释数据。
在这种情况下,编译器很可能插入了结果,因为它知道它们。此外,因为在此情况下不存在对this的地址或状态的实际依赖,所以没有显示错误: Test() 不依赖于 this的状态/数据/成员/动态方法/vtable。
如果您添加了例如std::stringNewClass中并打印它们,您可以预期爆炸:)
如果上下文的危险性不明显,则这是一种极其危险的转换 - 所有由int*支持的数据都被重新解释为NewClass*,其所有内部内存和结构(例如 vtable 和 magic cookies)也被相应地重新解释。很快你的程序就会出现段错误,因为要么读取超出分配的结尾(int*),要么将一个 int 视为完全不相关的类型 - 在此情况下,请考虑有vtable或数据的类的内存布局,例如向NewClass添加一些std::string并从中读取和写入这些成员。

谢谢。我想知道在我的代码中NewClass对象是在哪里实例化的。我理解没有类型检查的部分,也明白将任何东西强制转换为任何东西都是一种不好的编程习惯。欢迎提供意见。干杯! - GeeKay
@GeeKay 这就是我所说的“地址类型转换”的意思。这里没有 NewClass -- 你只是将一个 int* 当作 NewClass* 来处理 :) 在这种情况下,你会发现并没有创建或销毁 NewClass。(继续回答…) - justin

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

看看它打印出什么。

如果你理解了这个 - 尝试阅读关于编译器如何布局对象的内容。这将告诉您许多关于为什么在您的平台上存在此行为的原因。


0

这似乎是未定义的行为。但是在C++中,您始终可以使用reinterpret-cast来执行此操作。滥用reinterpret_cast运算符很容易不安全。除非所需的转换本质上是低级别的,否则应使用其他转换运算符之一。 reinterpret_cast运算符可用于诸如char*到int*或One_class*到Unrelated_class*之类的转换,这些转换本质上是不安全的。


0

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

你的编译器可能使用虚拟方法表来分派对虚拟方法的调用,如果该方法是虚拟的,你可能会得到垃圾而不是VMT,然后你就会开始遇到崩溃。

话虽如此,行为是未定义的,这意味着任何情况下都可能发生任何事情。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接