关于static_cast的问题

Questions about static_cast

本文关键字:问题 cast static 关于      更新时间:2023-10-16

我写了一段代码,但我对它的输出感到困惑:

#include <iostream>
using namespace std;
class B{
public:
    virtual void foo() {cout << "B::foo" << endl;}
};
class D:public B{
public:
    virtual void foo() {cout << "D::foo" << endl;}
    void disp() {cout << "D::disp" << endl;}
};
void func(B *pb){
    D *pd1 = static_cast<D*>(pb);
    pd1->foo();
    pd1->disp();
}
int main(int argc, char *argv[])
{
    B* pb = new B();
    func(pb); 
    return 0;
}

输出为:

B::foo
D::disp

但据我所知,pb指向类型B。其中没有名为disp()的函数?那么,为什么它可以访问D类中的disp()函数呢?

由于disp()不访问类的任何成员,原则上它与在全局命名空间中而不是在类中声明的一样,因此即使实例不是正确的类,调用它也没有负面影响。

您正在做的是将基类的指针向下转换为派生类的指针,即使它没有初始化为派生类。如果disp()试图访问D中但不在B中的类成员,则可能会遇到segfault。

最重要的是:不要使用static_cast进行下转换,除非您绝对确定指针实际上指向派生类的实例。如果您不确定,可以使用dynamic_cast,它在不匹配的情况下会失败(但存在RTTI的开销,因此如果可以的话,请避免它)。

如果强制转换不正确或抛出std::bad_castdynamic_cast将返回nullptr如果它强制转换引用,则会出现异常,这样您就可以确切地知道它失败的原因,而不是可能的内存损坏错误。

行:

D *pd1 = static_cast<D*>(pb);

将进行强制转换,而不管源指针是B*还是D*。在您的情况下,结果将是指向错误类型的对象的指针。方法disp将工作,因为它没有使用类D的任何数据成员或虚拟函数。在更复杂的情况下,这将导致不稳定的行为或崩溃。

你的物体是变形的。您应该使用dynamic_cast

我认为,在这种情况下,重要的是成员函数disp()并没有隐藏在D类型的所有对象中。这是一个存在于一个地方的单一功能。并且是否有任何对象将尝试调用disp()由代码决定。

static_cast将生成编译器认为指向D的指针,而不管您传递给它的指针是什么。一旦您有了指向D的指针,编译器将允许您尝试调用disp()

换句话说,static_cast不会保护您不正确地投射指针

将指向分配为B的对象的指针强制转换为指向派生类D的指针时,您做了一些非常糟糕的事情。标准是这样说的,重点是我的:

5.2.9静态铸造

如果类型为"pointer to cv1 B"的右值指向一个实际上是D类型对象的子对象的B,则结果指针指向D类型的封闭对象。否则,强制转换的结果是未定义的

您通过执行static_cast调用了未定义的行为。编译器和运行时可以做任何事情,并且在程序调用未定义的行为时仍然是兼容的。

您需要了解各种类型的C++类型转换之间的区别。

你在这里使用的是静态演员阵容,这与说"我真的不在乎它是否真的是那种类型——尽你最大的努力"是一样的。

在这种情况下,你想知道你的指针是否真的是你将其转换为的派生类型。你应该使用dynamic_cast。只有当指针的类型正确时,此强制转换才会成功。这意味着,如果失败,它将返回一个NULL指针。

你看到的行为是当你没有为工作使用正确的演员阵容时会发生的事情,虽然你可以尝试解释它,但这是你真正应该避免的事情,因为它属于未定义行为的范畴。换言之,编译器之间甚至同一编译器的不同版本之间都不可能出现相同的副作用。换句话说,避免它。