为什么在这里使用静态强制转换而不是动态强制转换? 我从书中复制下面的文本,更有效的C++。< /P>
第31项:使多个对象的功能虚拟化。为什么在这里使用静态强制转换而不是动态强制转换? 我从书中复制下面的文本,更有效的C++。< /P>,c++,C++,第31项:使多个对象的功能虚拟化。 class GameObject { ... }; class SpaceShip: public GameObject { ... }; class SpaceStation: public GameObject { ... }; class Asteroid: public GameObject { ... }; 最常见的双重分派方法是通过if-then-else链将我们返回到虚拟函数仿真的无情世界。在这个严酷的世界中,我们首先发现其他对象的真实类型,然
class GameObject { ... };
class SpaceShip: public GameObject { ... };
class SpaceStation: public GameObject { ... };
class Asteroid: public GameObject { ... };
最常见的双重分派方法是通过if-then-else链将我们返回到虚拟函数仿真的无情世界。在这个严酷的世界中,我们首先发现其他对象的真实类型,然后根据所有可能性对其进行测试:
void SpaceShip::collide(GameObject& otherObject)
{
const type_info& objectType = typeid(otherObject);
if (objectType == typeid(SpaceShip)) {
SpaceShip& ss = static_cast<SpaceShip&>(otherObject);
process a SpaceShip-SpaceShip collision;
}
else if (objectType == typeid(SpaceStation)) {
SpaceStation& ss =
static_cast<SpaceStation&>(otherObject);
process a SpaceShip-SpaceStation collision;
}
...
}
void SpaceShip::collide(游戏对象和其他对象)
{
const type_info&objectType=typeid(其他对象);
if(objectType==typeid(太空船)){
宇宙飞船&ss=静态投射(其他物体);
处理飞船碰撞;
}
else if(objectType==typeid(SpaceStation)){
空间站与太空船=
静态投影(其他对象);
处理飞船与空间站的碰撞;
}
...
}
问题是:
Q1>为什么我们在这里使用静态强制转换而不是明显的动态强制转换
问题2> 在这种情况下它们是一样的吗
多谢各位
//更新//
事实上,我对问题2更感兴趣
比如说,
class base {};
class subclass : public base {};
base *pSubClass = new subclass;
subclass *pSubClass1 = static_cast<subClass*> (pSubClass);
类基{};
类子类:公共基{};
base*pSubClass=新的子类;
子类*pSubClass1=静态强制转换(pSubClass);
//在这种情况下,虽然我知道我们应该在这里使用dynamic\u cast,但是静态\u cast是否正确地完成了工作?似乎是一个非常可靠的答案。基本上,静态强制转换速度更快,但不进行运行时类型检查。您自己已经验证了类型,因此不需要使用动态强制转换。Dynamic_cast将自动为您检查类型。关于记录,以下是执行此操作的惯用方法:
void SpaceShip::collide(GameObject& otherObject)
{
if (SpaceShip* ss = dynamic_cast<SpaceShip*>(&otherObject)) {
// process a SpaceShip-SpaceShip collision;
}
else if (SpaceStation* ss = dynamic_cast<SpaceStation*>(&otherObject)) {
// process a SpaceShip-SpaceStation collision;
}
// ...
}
void SpaceShip::collide(游戏对象和其他对象)
{
if(太空船*ss=动态投影(&otherObject)){
//处理飞船碰撞;
}
else if(SpaceStation*ss=动态投影(&otherObject)){
//处理飞船与空间站的碰撞;
}
// ...
}
它比较短,表现出相同的性能特征,最重要的是,惯用的C++不会让其他程序员从头到脚,想知道这是什么意思。
编辑(响应OP的编辑): 是的,这是定义明确的行为。以下是C++03标准第5.2.9/8节的内容: 如果存在从“指向
D
的指针”到“指向B
的指针”的有效标准转换,则可以将类型为“指向cv1B
”的右值转换为类型为“指向cv2D
”的右值,其中D
是从B
派生的类,cv2与cv1相同或大于cv1,并且B
不是D
的虚拟基类。空指针值将转换为目标类型的空指针值。如果“指向cv1B
”类型的右值指向实际上是D
类型的对象的子对象的B
,则生成的指针指向D
类型的封闭对象。否则,强制转换的结果是未定义的
如果
dynamic\u cast
失败,一些编译器将生成抛出std::bad\u cast
的代码。因此,在这种情况下,这两种方法是不同的。使用dynamic\u cast
可能看起来像
try {
SpaceShip& ship = dynamic_cast<SpaceShip&>(otherObject);
// collision logic
return;
} catch (std::bad_cast&) {}
try {
SpaceStation& station = dynamic_cast<SpaceStation&>(otherObject);
// collision logic
return;
} catch (std::bad_cast&) {}
试试看{
宇宙飞船和飞船=动态投影(其他物体);
//冲突逻辑
返回;
}catch(std::bad_cast&){
试一试{
空间站和空间站=动态投影(其他对象);
//冲突逻辑
返回;
}catch(std::bad_cast&){
这看起来真的很糟糕。为什么他们选择以这种方式实现它,而不是更传统的动态转换
我不能说,但这两个选项的行为不一定相同。如前所述,该代码只考虑参数的实际类型,而dynamic\u cast
考虑参数在继承树中的位置。考虑:
struct Base { virtual ~Base() { } };
struct Intermediate : Base { };
struct Derived : Intermediate { };
int main() {
Intermediate i;
Base* p_i = &i;
Derived d;
Base* p_d = &d;
assert(typeid(*p_i) == typeid(Intermediate)); //1
assert(dynamic_cast<Intermediate*>(p_i)); //2
assert(typeid(*p_d) == typeid(Intermediate)); //3
assert(dynamic_cast<Intermediate*>(p_d)); //4
}
struct Base{virtual~Base(){};
结构中间:基{};
结构派生:中间{};
int main(){
中间产物Ⅰ;
基准*p_i=&i;
导出d;
基准*p_d=&d;
断言(typeid(*p_i)=typeid(Intermediate));//1
assert(dynamic_cast(p_i));//2
断言(typeid(*p_d)=typeid(中间));//3
assert(dynamic_cast(p_d));//4
}
(1) 和(2)都通过了它们的断言,但是(3)失败,而(4)成功<代码>p_d指向一个派生的
对象,因此type_id
生成一个派生的
对象的信息,该信息与中间的
对象的信息不相等。但是Derived
派生自Intermediate
,因此dynamic\u-cast
将愉快地将指向Derived
的指针转换为指向Intermediate
的指针
用原始问题中使用的术语来说,如果其他物体
是一艘护卫舰
,它源自宇宙飞船
,它将不会使用“宇宙飞船”碰撞例程。很可能这不是预期的行为;您可能希望Frigate使用该代码,但您必须手动添加新类型的显式检查
当然,如果您只检查从未从中继承的类型,那么这种差异就消失了。或者如果你只是不想要多态行为(尽管这会使标题有点误导)。在这种情况下,这可能更有效,但这是一个巨大的实现细节,我当然不会在实践中投入资金
如果类型不是多态性的,则会出现另一个小的、基本上无关紧要的差异。在我上面的代码中,如果从
Base
中删除虚拟析构函数,(2)和(4)现在将显示未定义的行为。