【问题标题】:static_pointer_cast<Derived> pReallyABase = static_pointer_cast<Derived>(pBase) works! Why? [duplicate]static_pointer_cast<Derived> pReallyABase = static_pointer_cast<Derived>(pBase) 有效!为什么? [复制]
【发布时间】:2026-01-07 20:45:01
【问题描述】:

我不明白为什么会这样。 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

【问题讨论】:

  • 未定义的行为意味着它所说的。
  • 尝试让onlyForDerived 读取或写入Derived 成员,你可能不会有这样的运气:)

标签: c++ pointers inheritance downcast


【解决方案1】:

这是关于如何在 C++ 中调用成员函数(非静态和非虚拟),《Inside C++ 对象模型》一书对此进行了解释:

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

obj.magnitude();

变成

magnitude_7Point3dFv(&obj);

ptr->magnitude();

变成

magnitude_7Point3dFv(ptr);

所以和上面的例子一样,函数 onlyForDerived 没有使用 Derived 类的任何成员变量,所以它可以工作。但这是一个未定义的行为,我们不应该依赖它。

【讨论】:

  • 即使您在Derived 中定义int x=0 然后在onlyForDerived 中增加它,它似乎也可以编译。
  • 您可能应该提到这就是它可能在某些实现上起作用的原因,但行为是未定义的,因此任何事情都可能发生。
  • @BenHymers,你是对的
  • @vsoftco,程序运行在调试模式还是发布模式?
  • 使成员成为虚拟成员,即使不访问成员变量,它仍然可能会出现崩溃(无论如何,在任何合理的编译器上)。为虚拟和非虚拟方法调用生成的代码在反汇编中将非常有说服力。
【解决方案2】:

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

在这种情况下,由于编译器“知道”派生指针的静态类型是Derived,因此即使这样做是不合法的,它也可以调用函数。

它不会引发运行时错误,因为它不必(未定义就是未定义)。也就是说,如果函数是虚函数,它在大多数实现中可能会崩溃,如果派生类包含派生函数引用的数据,它可能会崩溃或生成意外输出。

【讨论】:

    最近更新 更多