【问题标题】:Casting to different Base classes gives different result. C++转换为不同的基类会产生不同的结果。 C++
【发布时间】:2018-08-03 15:53:21
【问题描述】:

也许我的问题没有完美形成,但我的代码会让一切都清楚。

#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)<<"\n\n";

    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 是错误的设计。我没有考虑设计,但行为是困扰我的。 谁能解释一下为什么第一次使用不同的方式会产生不同的结果,但第二次会产生相同的结果??

【问题讨论】:

  • 使用reinterpret_cast 几乎总是预示着糟糕的设计。避免它并重新考虑方法。
  • static_cast&lt;B*&gt;(pc) - “给我*pcB 部分的位置;reinterpret_cast&lt;B*&gt;(pc) - “将pc 视为B 的位置”。
  • @FrançoisAndrieux 这是实际的答案,你为什么不这样发布呢?
  • reinterpret_cast 和 c-style cast 基本一样吗?
  • @Zebrafish 不,c 风格的演员表可以是任何类型的演员表。 reinterpret_cast 在没有实际转换的情况下改变指针的类型(指针地址没有改变)。你只是用不同的方式解释相同的记忆。

标签: c++ inheritance casting reinterpret-cast static-cast


【解决方案1】:

定义一个类也意味着定义一个内存布局。在最简单的形式中,成员是连续排列的,例如

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 类型。

这就是为什么你得到相同的地址值的原因。

【讨论】:

    【解决方案2】:

    基本上,CAB 部分不能占用相同的空间。一个必须先于另一个。当您正确地将C* 转换为A* 时,您将获得一个指向原始指针指向的实例的A 部分的指针,同样适用于转换为B*。由于C (int A::n;) 的A 部分和C (int B::n;) 的B 部分必然位于不同的地址,因此这些转换的结果自然也彼此不同.这是可能的,因为编译器可以知道pc 指向的对象的布局,信息可以从它的类型推导出来。如果信息不可用,例如指针首先转换为 void*,这将不起作用。

    reinterpret_cast 给出相同地址的原因与您投射到的地址无关,因为这正是reinterpret_cast 所做的。它将指针或引用转换为另一种类型,同时忽略任何形式的类型安全。对于reinterpret_cast,指针是创建一个新类型的指针,其地址与提供的指针相同,而不管实际类型和类型安全性如何。

    小心使用reinterprect_cast,因为它本质上将事实注入到类型安全系统中。编译器必然会假设您所说的内容是正确的。如果这些“事实”不真实(例如 reinterpret_cast&lt;B*&gt;(pc) 的情况),您将面临未定义行为的风险。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-23
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多