了解静态演员

时间:2014-03-14 12:00:55

标签: c++

我想了解static_cast。我有简单的代码,我有两个类。 我通过void指针将一个类对象分配给另一个类对象。

按照我的理解程序不应该工作

  #include <iostream>

  using namespace std;
  class Test
  {
    public:
          void play(){cout<<"Test::play"<<endl;}
  };
  class Base
  {
    public:

  };

  int main ()
  {
     Base b;
     void* vobj = &b;
     Test *t = static_cast<Test*>(vobj);
     t->play();
     return 0;
  }

运行此程序后,输出为

  Test::play

请帮助我理解这种行为。

4 个答案:

答案 0 :(得分:3)

这是非常危险的行为。您使用指针(void*)并使其指向已构造对象的地址。还行吧。但是,您使用指针使其成为一种新的对象...这不是正常的。类Base和Test的内存布局是相同的,但请考虑以下更改:

  #include <iostream>

  using namespace std;
  class Test
  {
    public:
          Test() : c(1) {}                            // <-- Change here
          void play(){cout<<"Test::play"<< c <<endl;} // <-- Change here
         int c;                                       // <-- here 
  };
  class Base
  {
    public:
       Base() : b(2) {}                          // <- here
    int b;                                  // <- here
 };

 int main ()
 {
    Base b;
    void* vobj = &b;
    Test *t = static_cast<Test*>(vobj);
    t->play();
    return 0;
 }

你是否认为会打印1?不会。它会打印2,因为它在Test obejct的记忆中充当Base:)

静态强制转换是不安全的,在这种情况下可能会被滥用,因为无论是否允许强制转换都不会进行运行时检查:(

答案 1 :(得分:2)

它的工作原因是:

  1. 变量vobj的类型为void*,可以接受从void*Test*的静态广播。因此,编译器不会为static_cast<Test*>(vobj)生成编译错误。

  2. 函数play在其类的层次结构中不是虚拟的,因此编译器可以直接跳转到其地址来替换对此函数的调用,而无需从调用的V-Table中读取它首先是对象。

  3. 函数play没有使用调用对象(this)的任何成员字段,因此调用对象的类型没有区别(实质上,它确切地说比如调用全局函数。)

答案 2 :(得分:1)

你的程序技术上格式正确,当然,虽然它不在实践中。

5.2.9 / 13州:

  

类型为“指向cv1 void的指针”的prvalue可以转换为类型为“指向cv2 T的指针”的prvalue,其中   T是对象类型,cv2与cv1具有相同的cv资格,或者更高的cv资格。 null   指针值转换为目标类型的空指针值。指向对象的类型指针的值   转换为“指向cv void的指针”并返回,可能具有不同的cv-qualification,应具有其原始值   值。

这就是你在做什么。您正在欺骗编译器(大约两个角落)进行非法转换。允许从void*到另一种类型的转换的目的当然是允许您进行任何演员表(无论是否合法),而是允许您合法地进行某些情况将void*投射到类知道它是正确的

它仍然“有效”,或者看起来如此,但这只是运气(或者说,运气不好......如果它崩溃会更好)。

答案 3 :(得分:0)

在C ++中,方法调用不会检查对象创建。例如,请考虑以下类

Class Test
{
  public:
    int x;
    void play(int y) 
    { 
      cout<<"X Value: "<<x<<endl; 
      cout<<"Y Value: "<<y<<endl;
    }
}

int main ()
{
   Test t = new Test();
   t->x = 5;
   t->play();
   return 0;
}

在上面的代码中,当我调用play方法时,在内部通过将类实例作为方法参数发送来执行该方法,如下所示。

void play(Test obj, int y)

如果我们在没有创建对象的情况下调用play方法,它将不会在调用行中失败。 c ++使用null参数调用play方法(void play(null,5))。因此,当方法尝试使用类对象访问其成员(int x)时,程序会抛出异常。 (注意:如果我们在方法中没有使用任何类成员,那么即使没有为类创建对象,该方法也会成功执行。)

考虑以下代码:

Class Test
{
  public:
    void play(int y) { cout<<"Y Value: "<<y<<endl; }
}

int main ()
{
   Test t;
   t->play(5);
   return 0;
}

在上面的例子中,我调用了Test类的Play方法而没有为类创建对象。使用null参数调用内部播放方法(void play(null,5))。但是,在类中我们没有使用类对象。因此,它不会抛出任何异常并愉快地打印“Y Value:5”字符串,程序将成功执行。

像这样,在您的代码中,当您尝试调用play方法时,它将使用null参数调用该方法。因为,您的方法没有使用任何类成员,您的方法将成功执行。 static_cast&lt;&gt;这里没有任何魔力。的static_cast&LT;&GT;仅用于以下目的

  • 将基类的指针转换为派生类的指针
  • 将枚举的数字数据类型转换为整数或整数

在您的代码中,static_cast不会将Base类转换为Test类。它只是返回了一些垃圾指针。如果您在play方法中使用了任何类成员,那么在您尝试使用类对象的行中调用将会失败。