我使用 C++ 进行嵌入式编程。
假设我必须实现一个严格定义(即逐字节)的类类型,我可以向它添加一个构造函数和一些其他非虚拟方法,而不需要在字节级别更改该类型的对象吗?也就是说,我可以假设不会添加其他数据吗?
我假设 RTTI 已关闭。
我想确定 C++ 标准是否定义了这一点。
我使用 C++ 进行嵌入式编程。
假设我必须实现一个严格定义(即逐字节)的类类型,我可以向它添加一个构造函数和一些其他非虚拟方法,而不需要在字节级别更改该类型的对象吗?也就是说,我可以假设不会添加其他数据吗?
我假设 RTTI 已关闭。
我想确定 C++ 标准是否定义了这一点。
是的,如果您只添加构造函数和/或非虚拟方法,您将不会更改类的大小或布局,因为原始类和新类将是布局兼容的(9.2 类成员 [class.mem] #17 ),但前提是它们是标准布局类。
标准布局类定义为:
9类[类]
标准布局类是这样的类:
— 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
— 没有虚函数 (10.3) 和虚基类 (10.1),
— 对所有非静态数据成员具有相同的访问控制(第 11 条),
— 没有非标准布局的基类,
— 要么在最派生类中没有非静态数据成员,并且最多有一个具有非静态数据成员的基类,要么没有具有非静态数据成员的基类,并且
— 没有与第一个非静态数据成员相同类型的基类。
不,不是。
C++11 有这样的说法sizeof
:
[C++11: 5.3.5/2]:
[..] 当应用于引用或引用类型时,结果是引用类型的大小。当应用于一个类时,结果是该类的对象中的字节数,包括将该类型的对象放入数组中所需的任何填充。最派生类的大小应大于零 (1.8)。应用于sizeof
基类子对象的结果是基类类型的大小。当应用于数组时,结果是数组中的总字节数。这意味着包含n 个元素的数组的大小是元素大小的n倍。
......就是这样。它没有指定“将这种类型的对象放入数组中所需的内容”的含义,而是将其留给目标 ABI。
如果我没记错的话,最近从其以前的主页(grr) 中消失的安腾 ABI 确实提供了您正在寻找的那种保证,但这与 C++ 保证不同。完全不一样。
您可以使用编译器特定的打包/对齐选项来处理数据成员。但是由于虚拟功能而导致的空间增加超出了您的控制范围。您不会期望为非虚拟功能添加任何空间,但这也不是“保证”。
您可以static_assert(sizeof T == x, "T needs to be x bytes wide")
检测到何时通过代码更改破坏了某些假设。这是你能得到的最好的。
在 C++03 及之前版本中,没有。绝对不能保证布局。在 C++11 中,有布局兼容的概念,但我不确定它会走多远;我希望向类添加非虚拟函数不会破坏布局兼容性。另一方面,您提到嵌入式编程和关闭 RTTI;您可能指望编译器保证的许多其他事情,而不是标准。(例如,关闭 RTTI 的能力。)鉴于此,我建议寻找编译器的保证。我怀疑大多数针对嵌入式系统的编译器都提供了它们生成的布局的详细描述,您可以从那里开始。
您可以从工作草案 C++, n3242, 2011-02-28 中获得此保证
9.2 类成员
18 如果两个标准布局结构(第 9 条)类型具有相同数量的非静态数据成员并且相应的非静态数据成员(按声明顺序)具有布局兼容类型,则它们是布局兼容的类型 (3.9) .
这里只讨论数据成员,所以有两个类
struct A {
int d1;
float d2;
void f();
};
struct B {
int e1;
float e2;
int g() const;
double h();
};
应该是布局兼容的。
什么是标准布局类
9 类
7 标准布局类是这样的类:
— 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,
— 没有虚函数 (10.3) 和虚函数基类 (10.1),
— 对所有非静态数据成员具有相同的访问控制(第 11 条),
— 没有非标准布局的基类,
— 在最派生类中没有非静态数据成员,并且最多有一个基类具有非静态数据成员,或没有具有非静态数据成员的基类,并且
— 没有与第一个非静态数据成员相同类型的基类。 108