铸造到不同的基础类别会产生不同的结果.C

Casting to different Base classes gives different result. C++

本文关键字:结果 基础类      更新时间:2023-10-16

也许我的问题不是完美的,但是我的代码将使一切清晰。

#include <iostream>
using namespace std;
struct A{int n;};
struct B{int n;};
struct C : A, B{};
int main()
{
    C c;
    C* pc = &c;
    std::cout<<"TEST1"<<std::endl;
    cout << static_cast<B*>(pc) << "n";
    cout << reinterpret_cast<B*>(pc)<<"nn";
    std::cout<<"TEST2"<<std::endl;
    cout << static_cast<A*>(pc) << "n";
    cout << reinterpret_cast<A*>(pc)<<"n";
}

,输出为:

TEST1
0042F830
0042F82C
TEST2
0042F82C
0042F82C

我知道使用Reinterpret_cast是不良的设计。我不是在考虑设计,而是困扰我的行为。任何人都可以解释为什么铸造不同的方式第一次给出不同的结果,但第二次相同的结果?

本质上, AB C的部分不能占据相同的空间。一个必须在另一个面前。当您将C*正确施放到A*时,您将获得一个指向原始指针指向的实例的A部分的指针,并且施放到B*也是如此。由于CA部分(int A::n;(和C(int B::n;(的B部分必然在不同的地址处,因此这些转换的结果自然而然。这是可能的,因为编译器可以知道pc指向对象的布局,因此可以从其类型中推论信息。如果没有信息,这将行不通,例如,如果指针被施加到void*

reinterpret_cast给出相同地址的原因,无论您使用什么,因为这是reinterpret_cast所做的。它在忽略任何形式的类型安全性的同时将指针或引用转换为其他类型。对于reinterpret_cast,指针是创建一个新型指针,其地址与所提供的地址相同,无论实际类型和类型的安全性如何。

提防使用reinterprect_cast,因为它实际上将事实注入了类型的安全系统。编译器必然会假设您所说的是正确的。如果这些"事实"不是正确的(例如reinterpret_cast<B*>(pc)的情况(,您就有不确定的行为。

定义类也意味着定义内存布局。以最简单的形式连续布置成员,例如

struct A {
    int n;
};

和内存中

| Address  | Size | Member |
|----------+------+--------+
| 0042F82C | 4    | n      |

基类也发生了同样的事情

struct C : A, B {
};

潜在内存布局

| Address  | Size | Member |
|----------+------+--------+
| 0042F82C | 4    | A::n   |
| 0042F830 | 4    | B::n   |

现在,您有一个指针pc到类型C的对象。使用static_cast考虑了对象中成员和基类的布局。

因此,您获得了AB部分的正确地址。

在另一侧使用reinterpret_cast,只需重用指针并假装指向另一种类型即可。

说明

与static_cast不同,但是像const_cast一样,reinterpret_cast表达式不会编译到任何CPU指令。它纯粹是一个编译器指令,它指示编译器将表达式的序列(对象表示(处理好,就好像它具有new_type。

这是原因,为什么您获得相同的地址值。