在将其类型转换为 Derived class 指针后,从 Base class 指针调用 Derived class 函数

Calling Derived class function from a Base class pointer after typecasting it to Derived class pointer

我是 C++(和 OOP)的新手。我正在努力理解以下代码:

#include <iostream>

class Base {
public:
    Base() {
        std::cout << "In Base Constr: " << __FUNCSIG__ << std::endl;
    }

    virtual ~Base() {
        std::cout << "In Base Destr: " << __FUNCSIG__ << std::endl;
    }

    void A() {
        std::cout << "In Base func A " << __FUNCSIG__ << std::endl;
    }
};

class Derived : public Base {
    public:
    Derived() {
        std::cout << "In Derived Constr: " << __FUNCSIG__ << std::endl;
    }

    ~Derived() {
        std::cout << "In Derived Destr: " << __FUNCSIG__ << std::endl;
    }

    void B() {
        std::cout << "In Derived func B " << __FUNCSIG__ << std::endl;
    }
};

void test(Base* b) {
    Derived* d = static_cast<Derived*>(b);
    d->A();
    d->B();              // How is this valid??
}

int main() {
    Base *b = new Derived();
    std::cout << "In main" << std::endl;
    b->A();
    std::cout << __LINE__ << std::endl;

    Base *bb = new Base();
    std::cout << __LINE__ << std::endl;
    test(bb);

    delete b;
    delete bb;
}

我不确定,d->B() 行为何以及如何工作?即使指针被类型转换为 Derived class,但 Base class 对象本身不应在内存中具有该函数。

无效。这是未定义的行为。

问题是编译器允许您编写这样的代码。如果指向的对象实际上是 Derived,则从 Base* 转换为 Derived* 将是有效的。由您作为程序员来确保它是有效的。

在C++中有很多情况你可以像这样搬起石头砸自己的脚。它是语言的一部分。

越界访问、取消引用悬挂 pointers/references 和无效强制转换,仅举几个最常见的例子。

当对象实际上不是该派生类型时,static_cast 到派生 class 是未定义的行为。但是未定义的行为意味着任何事情都可能发生,包括看似有效。 (或者今天似乎工作,然后在最糟糕的时间失败。)

官方从C++语言的角度解释到此为止。

但至于为什么这可能适用于典型的真实编译器和计算机:成员函数的代码实际上并未存储在对象内部,因为那会占用很多字节。对于非虚函数,对象内部甚至没有任何指向该函数的指针或类似的东西。相反,编译器将像非成员函数一样实现函数 Derived::B

void __mangled_Derived_B(Derived const* this) { /*...*/ }

然后每次调用B函数时,它都会传递正确的指针成为“this”参数。

在您的示例中,Derived::B 实际上根本没有使用 this,甚至没有隐式使用,因此不太可能出现问题。但如果它试图使用 Derived 的数据成员,事情就会变得更加危险,可能会导致奇怪的结果、对其他对象的更改或崩溃。

I am not sure, why & how the line b->B() works? [..] the Base class object itself should not have that function in memory

你说得对!它没有 工作!

(嗯,函数没有存储 "in memory",但是…)

调用无效。 static_cast 表示 "I promise that this Base* points to a Derived"。这个承诺被打破了。

程序有未定义的行为。这在实践中可能意味着事情 "appear" 可以工作,特别是如果没有成员变量被不存在的函数触及...