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

huangapple go评论80阅读模式
英文:

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

问题

I am fairly new to C++ (& OOP). I am struggling to understand the following piece of code:

#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;
}

I am not sure, why & how the line d->B() works? even though the pointer was typecasted to Derived class, but the Base class object itself should not have that function in memory.

英文:

I am fairly new to C++ (& OOP). I am struggling to understand the following piece of code:

#include &lt;iostream&gt;

class Base {
public:
    Base() {
        std::cout &lt;&lt; &quot;In Base Constr: &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }

    virtual ~Base() {
        std::cout &lt;&lt; &quot;In Base Destr: &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }

    void A() {
        std::cout &lt;&lt; &quot;In Base func A &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }
};

class Derived : public Base {
    public:
    Derived() {
        std::cout &lt;&lt; &quot;In Derived Constr: &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }

    ~Derived() {
        std::cout &lt;&lt; &quot;In Derived Destr: &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }

    void B() {
        std::cout &lt;&lt; &quot;In Derived func B &quot; &lt;&lt; __FUNCSIG__ &lt;&lt; std::endl;
    }
};

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

int main() {
    Base *b = new Derived();
    std::cout &lt;&lt; &quot;In main&quot; &lt;&lt; std::endl;
    b-&gt;A();
    std::cout &lt;&lt; __LINE__ &lt;&lt; std::endl;

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

    delete b;
    delete bb;
}

I am not sure, why & how the line d-&gt;B() works? even though the pointer was typecasted to Derived class, but the Base class object itself should not have that function in memory.

答案1

得分: 3

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

你说得对!它 起作用!

(好吧,函数并不是存储在 "内存中" 的,但是...)

这个调用是无效的。static_cast 表示 "我保证这个 Base* 指向一个 Derived"。但这个承诺被打破了。

这个程序有未定义的行为。实际上,这可能意味着事情 "看起来" 能正常工作,特别是如果不存在的函数没有触碰到任何成员变量...

英文:

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

You're right! It doesn't work!

(Well, functions aren't stored "in memory", but&hellip;)

The call is invalid. The static_cast says "I promise that this Base* points to a Derived". That promise was broken.

The program has undefined behaviour. That can in practice mean that things "appear" to work, especially if no member variables were touched by the non-existent function...

答案2

得分: 2

以下是已翻译的内容:

"将对象强制转换为派生类,当对象实际上不是该派生类型时,这被视为未定义行为。但未定义行为意味着任何事情都可能发生,包括似乎可以工作(或似乎可以工作今天,然后在最糟糕的时刻失败)。

因此,从C++语言的角度来看,这就是官方解释的全部内容。

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

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

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

在您的示例中,“Derived::B”实际上根本不使用“this”,甚至不是隐式地使用,因此问题不太可能发生。但如果它尝试使用“Derived”的数据成员,情况会变得更加危险,可能会导致奇怪的结果、其他对象的更改或崩溃。"

英文:

It is undefined behavior to static_cast to a derived class when the object is not actually of that derived type. But undefined behavior means anything could happen, including seeming to work. (Or seeming to work today, then failing later at the worst possible time.)

So that's all there is for an official explanation from the point of view of the C++ language.

But as for why this probably does work for a typical real compiler and computer: The code for a member function isn't actually stored inside the objects, since that would be a lot of bytes. For a non-virtual function, there isn't even any pointer or such thing inside the object to the function. Instead, the compiler will implement the function Derived::B essentially like a non-member function:

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

Then every time the B function is called, it will just pass the correct pointer to become the "this" argument.

In your example, Derived::B doesn't actually use this at all, not even implicitly, so issues are unlikely. But if it attempted to use a data member of Derived, things would get much more risky, possibly causing strange results, changes to other objects, or crashes.

答案3

得分: 1

这不是有效的。这是未定义的行为。

问题在于编译器允许您编写这样的代码。从Base*Derived*的转换只有在指向的对象实际上是Derived类型时才是有效的。作为程序员,您需要确保它是有效的。

在C++中有许多情况下,您可以像这样自己给自己挖坑。这是语言的一部分。

越界访问、解引用悬空指针/引用以及无效的强制类型转换是其中一些最常见的情况之一。

英文:

It is not valid. It is Undefined Behavior.

The thing is that the compiler allows you to write such code. Casting from a Base* to a Derived* would be valid if the object pointed to was actually a Derived. It's up to you as the programmer to make sure it's valid.

There are many situations in C++ where you can shoot yourself in the foot like this. It's part of the language.

Out of bounds access, dereferencing dangling pointers/references and invalid casts to name a few of the most common ones.

huangapple
  • 本文由 发表于 2020年1月6日 23:03:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/59614373.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定