【问题标题】:Zero-sized struct零大小的结构
【发布时间】:2018-11-12 09:48:02
【问题描述】:

根据 C++ 标准(从 C 继承),空结构的大小仍然非零。这样做的原因(可怜的恕我直言)是两个不同的变量应该有不同的地址。现在,继承一个空结构并不总是“膨胀”对象。但在某些情况下确实如此。

我有一个非常复杂的类架构,涉及到激烈的模板巫术。结果,最终类(我需要创建的实例)可能继承了几个空结构。由于这个事实,它们中的一部分最终可能会膨胀。而最糟糕的是,它们的内存布局实际上取决于继承顺序。

如果可能的话,我想摆脱这一切。

是否可以配置 C++ 编译器来消除这种空间浪费,但实际上会破坏标准?

编辑:

我的意思是:

struct Empty1 {};
struct Empty2 {};

struct NonEmpty {
    int Value;
};

struct MyClass1
    :public NonEmpty
    ,public Empty1
    ,public Empty2
{
};

struct MyClass2
    :public Empty1
    ,public NonEmpty
    ,public Empty2
{
};

struct MyClass3
    :public Empty1
    ,public Empty2
    ,public NonEmpty
{
};

STATIC_ASSERT(sizeof(MyClass1) == 8);
STATIC_ASSERT(sizeof(MyClass2) == 4);
STATIC_ASSERT(sizeof(MyClass3) == 8);

不仅空结构使对象膨胀(当多个这样的东西被继承时),而且结果取决于空结构的继承顺序。

【问题讨论】:

  • 语言标准明确允许空基优化。 “它们的内存布局实际上取决于继承顺序”有点令人担忧,因为无法保证基类子对象的位置。你有一个你想要实现的例子吗?
  • 您是否实际证明了这是您当前编译器的问题?我希望用作基类的空类不会在派生类中占用空间;一个具体的例子会很有帮助。
  • 原因并不可怜。您只是没有真正引用它;真正的原因要复杂得多。标准可能没有这个限制(我不确定),但这需要一个复杂的证明来证明这总是会导致格式良好的程序。
  • 改变继承顺序并不是一个微妙的改变。如果您的类型不是 POD,那么您的代码应该以这样的方式编写,即基类的命名顺序或 EBO 是否发生都无关紧要。当您关心内存布局时,您应该使用 POD 类型。出于所有意图和目的,不保证非 POD 类型。
  • 我打赌你是在 MSVC 下编译的?这是多重继承的一个已知问题:connect.microsoft.com/VisualStudio/feedback/details/100686/… 解决方案包括不用担心,切换到“更好”的编译器,并避免多重继承。

标签: c++


【解决方案1】:

空基优化 允许空基不会“膨胀”对象,正如您所说的那样。但是,您必须小心,不要让一个对象从同一个空基中继承两次,否则将不允许这种优化。避免这种情况的一种方法是将空基模板化,实例化它们,这样就不会多次继承相同的空模板实例化。

如果您的基类用于标记具体类,那么您可以考虑将您的设计更改为非侵入式设计。

【讨论】:

    【解决方案2】:

    您会发现,在大多数现代 c++ 编译器中:

    struct a { }; // empty struct
    struct b : a { int x}; // inherits from empty struct.
    
    assert(sizeof(b)==sizeof(int)); // despite sizeof(a) >0
    

    实际上,这是否可以缓解您的担忧?

    【讨论】:

    • +1 只是因为 valdo 可以轻松编写一些测试来确保进行优化。
    • @Chris:这实际上是C++11's standard layout rules要求的。只有当您有多个具有 NSDM 的对象时才会出现问题。
    【解决方案3】:

    是否有可以配置的 C++ 编译器来消除这种空间浪费,...

    是的。 gcc-4.3.4

    ...实际上以违反标准为代价?

    不,标准允许你想要的行为。

    【讨论】:

    • 非常感谢,这回答了我的问题。是否需要特殊配置才能启用此功能?是否也可以消除空对象不需要的空间?也就是说,仅由空基组成的对象。
    • @valdo:不,您不能将它们放入数组中(因为数组元素的地址与sizeof(T) 不同)。由于您不能将基类 subobject 本身放在数组中,因此它可以有零空间。
    • @MSalters:我想到了一个具有各种成员变量(对象)的类。其中一些最终可能会评估为空结构。
    • @valdo:成员变量是另一种情况,不属于基类异常。请参阅boost::compressed_pair 了解创造性地使用基类异常的类
    【解决方案4】:

    g++ 支持零大小数组,您可以使用以下之一:

    struct Empty1 {
      int dummy[0];
    };
    

    struct Empty2 {
      int dummy[];
    };
    

    只有在使用 '-pedantic' 标志时才会生成警告。

    【讨论】:

    • 在标准或其他地方有没有关于这个宝石的阅读?它的便携性如何?
    【解决方案5】:

    一个基类可以是非零大小,如果,如您已经说过的,否则它与同一对象中相同类型的另一个实例具有相同的地址。

    要“解决”这个问题,你需要确保你继承另一个类的空类或结构没有任何共同的祖先。

    或者您可以自己手动布置类,但听起来您正在做一些事情,例如从 DirectX 顶点声明枚举创建 DirectX 顶点类型,所以这并没有真正的帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-24
      • 1970-01-01
      • 2012-12-07
      • 1970-01-01
      • 1970-01-01
      • 2015-02-17
      相关资源
      最近更新 更多