在将派生类指针类型转换为派生类指针后,从基类指针调用派生类函数

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

本文关键字:指针 派生 基类 调用 类函数 类型转换      更新时间:2023-10-16

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

我不确定,为什么 b->B(( 行是如何工作的?[..]基类对象本身不应在内存中具有该函数

你是对的!不行!

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

调用无效。static_cast说:"我保证这个Base*指向一个Derived"。这个承诺被打破了。

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

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

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

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

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

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

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

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

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

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

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