【问题标题】:Safety of casting between pointers of two identical classes?在两个相同类的指针之间进行转换的安全性?
【发布时间】:2011-10-14 04:09:46
【问题描述】:

假设我有两个不同的类,它们都以相同的内部方式表示二维坐标数据,如下所示:

class LibA_Vertex{
    public:
    // ... constructors and various methods, operator overloads
    float x, y
};

class LibB_Vertex{
    public:
    // ... same usage and internal data as LibA, but with different methods
    float x, y
};


void foobar(){
    LibA_Vertex * verticesA = new LibA_Vertex[1000];
    verticesA[50].y = 9;
    LibB_Vertex * verticesB = reinterpret_cast<LibB_Vertex*>( vertexA );
    print(verticesB[50].y); // should output a "9"
};

鉴于这两个类是相同的以及上面的函数,我能否可靠地指望这种指针转换在每种情况下都能按预期工作?

(背景是,我需要一种简单的方法在两个具有相同 Vertex 类的独立库之间交换顶点数组,并且我希望避免不必要地复制数组)。

【问题讨论】:

    标签: c++ pointers casting


    【解决方案1】:

    C++11 添加了一个名为 layout-compatible 的概念,适用于此。

    如果两个标准布局结构(第 9 条)类型具有相同数量的非静态数据成员和相应的非静态数据成员,则它们是布局兼容 (按声明顺序)具有 布局兼容 类型 (3.9)。

    在哪里

    标准布局类是这样的类:

    • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
    • 没有虚函数 (10.3) 和虚基类 (10.1),
    • 对所有非静态数据成员具有相同的访问控制(第 11 条),
    • 没有非标准布局基类,
    • 要么在派生最多的类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
    • 没有与第一个非静态数据成员相同类型的基类。

    standard-layout struct 是一个 standard-layout class,使用 class-key structclass 定义-key class.

    标准布局联合是一个标准布局类,用class-keyunion定义。

    终于

    指向布局兼容的 cv 合格和 cv 不合格版本 (3.9.3) 的指针 类型应具有相同的值表示和对齐要求 (3.11)。

    这保证reinterpret_cast 可以将指向一种类型的指针转​​换为指向任何布局兼容类型的指针。

    【讨论】:

    • 进行这些转换的另一种明确定义的方法是使用具有公共初始序列的联合(§9.2/19 允许)。
    • 哦!好吧,C ++ 11 来拯救。我只是希望这是 VS2010 在通过标准挑选樱桃时决定添加的内容之一。
    • @Clairvoire:这是在实践中一直有效的事情之一,即使被正式禁止。我不希望任何编译器编写者必须“添加”支持。
    • 它从来没有被“禁止”,它只是“未定义的行为”。只是我所知道的每个编译器的实际行为都是按照您的预期工作。 C++11 只是基本上定义了每个编译器都在使用的行为。
    【解决方案2】:

    我会将这种转换封装在一个类中(这样如果您需要更改平台或其他东西,它至少会在一个地方进行本地化)但是是的,它应该是可能的。

    你会想要使用reinterpret_cast,而不是static_cast

    【讨论】:

    • 没错,我错了!更正了问题
    【解决方案3】:

    理论上这是一个未定义的行为。但是,它可能适用于某些系统/平台。

    我建议您应该尝试将 2 个类合并为 1 个。即

    class Lib_Vertex{
    // data (which is exactly same for both classes)
    public:
    // methods for LibA_Vertex
    // methods for LibB_Vertex
    };
    

    class 中添加方法不会影响其大小。您可能需要稍微更改您的设计,但这是值得的。

    【讨论】:

    • 通常情况下,我也会使用这样的东西。不过,我仍然必须制作数组的副本。一个库(2d 物理库)使用其内部 Vertex 类返回整个顶点数组,然后我需要将其输入另一个库(2d 渲染库),该库接受其内部 Vertex 类的数组。
    【解决方案4】:

    从技术上讲,这是未定义的行为。实际上,如果使用相同的编译器来编译这两个类,如果字段以相同的顺序声明、具有相同的类型和相同的访问级别,那么它们在内存中将具有相同的布局。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      • 2019-09-18
      • 2014-11-28
      • 2011-02-08
      相关资源
      最近更新 更多