我想了解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
请帮助我理解这种行为。
答案 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)
它的工作原因是:
变量vobj
的类型为void*
,可以接受从void*
到Test*
的静态广播。因此,编译器不会为static_cast<Test*>(vobj)
生成编译错误。
函数play
在其类的层次结构中不是虚拟的,因此编译器可以直接跳转到其地址来替换对此函数的调用,而无需从调用的V-Table中读取它首先是对象。
函数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方法中使用了任何类成员,那么在您尝试使用类对象的行中调用将会失败。