【问题标题】:Calling Derived class function from a Base class pointer after typecasting it to Derived class pointer在将基类指针类型转换为派生类指针后从基类指针调用派生类函数
【发布时间】:2020-01-06 15:03:08
【问题描述】:

我对 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-&gt;B() 行为什么以及如何工作?即使指针被类型转换为派生类,但基类对象本身不应该在内存中具有该函数。

【问题讨论】:

  • @Gupta - 你在想dynamic_cast这里
  • This question应该解释清楚了。
  • 我一直不明白为什么 OOP 会变得多元化。

标签: c++ inheritance polymorphism downcast static-cast


【解决方案1】:

我不确定,为什么以及如何使用 b->B() 行? [..] 基类对象本身不应在内存中具有该功能

你是对的!它不起作用

(嗯,函数并不存储在“内存中”,但是……)

调用无效。 static_cast 表示“我保证这个Base* 指向Derived”。那个承诺被打破了。

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

【讨论】:

    【解决方案2】:

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

    所以这就是从 C++ 语言角度的官方解释。

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      在 C++ 中有很多情况,你可以像这样在脚上开枪。这是语言的一部分。

      越界访问、取消引用悬空指针/引用和无效转换仅举几个最常见的例子。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-09-23
        • 1970-01-01
        • 1970-01-01
        • 2016-12-31
        • 2016-10-10
        • 2014-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多