static_pointer_cast<Derived> pReallyABase = static_pointer_cast<Derived>(pBase) 有效!为什么?

static_pointer_cast<Derived> pReallyABase = static_pointer_cast<Derived>(pBase) works! Why?

本文关键字:static Derived gt pointer cast lt 有效 为什么 pReallyABase pBase      更新时间:2023-10-16

我不明白为什么会这样。pReallyABase是向下转换的shared_pointer<派生的>,指向基类实例。

我明白为什么编译器让我调用pReallyABase->onlyForDerived(),因为我将其定义为派生类指针,但为什么当我尝试使用该指针调用派生类函数时,我不会得到运行时错误?

class Base { 
    public:
        virtual string whatAmI() {
            return "I am a Base";
        }
};
class Derived : public Base {
    public:
        virtual string whatAmI() {
            return "I am a Derived";
        }
        string onlyForDerived() {
            return "I can do Derived things";
        }
};

int main(int argc, char *argv[]) { 
    shared_ptr<Base> pBase = shared_ptr<Base>(new Base);
    shared_ptr<Derived> pReallyABase = static_pointer_cast<Derived>(pBase);
    cout << pReallyABase->whatAmI() << endl;
    cout << pReallyABase->onlyForDerived() << endl;  //Why does this work?
    return 0;
}
结果

I am a Base
I can do Derived things

这是关于在c++中如何调用成员函数(非静态和非虚)的,《Inside c++ object model》一书对此有解释:

一个c++设计准则是非静态成员函数至少必须为与其类似的非成员函数一样有效。不应该有额外的开销用于选择成员函数实例。这是通过内部改造实现的将成员实例转换为等效的非成员实例。在这些转变之后它的每个调用也必须被转换:例如:

obj.magnitude();

magnitude_7Point3dFv(&obj);

ptr->magnitude();

magnitude_7Point3dFv(ptr);
正如上面的例子,函数onlyForDerived不使用类Derived的任何成员变量,所以它可以工作。但这是一个未定义的行为,我们不应该依赖它

通过将Base对象视为Derived,您将自动获得未定义行为,因此您看到的任何行为都是合法的。

在这种情况下,由于编译器"知道"派生指针的静态类型是Derived,因此它能够调用该函数,尽管这样做是不合法的。

它不会引发运行时错误,因为它不必这样做(undefined就是undefined)。也就是说,如果函数是虚拟的,它可能会在大多数实现中崩溃,如果派生类具有派生函数引用的数据,它可能会崩溃或生成意想不到的输出。