C ++专家:在这些条件下,成员变量与其类常量的偏移量是多少?

时间:2008-12-19 22:55:40

标签: c++

给定foo类型的变量FooClass*以及该类名为bar的成员变量,foo&(foo->bar)之间的距离是相同的任何有一些限制的情况:

  1. FooClass是非POD类型。

  2. 我们知道foo将始终指向FooClass的实例,而不是其中的某个子类型。

  3. 我们只关心单个编译器和单个编译的行为;也就是说,在gcc下可能导致的值从未在使用MSVC编译的代码中使用,并且永远不会保存以便在编译之间重复使用。它以二进制计算并在二进制文件中使用,即它。

  4. 我们不使用自定义new,尽管该类的某些实例可能是堆栈分配的,也可能是堆分配的。

  5. ctor没有明确的FooClass;它依赖于编译器生成的一个(FooClass中的每个字段都是POD或默认构造的。)

  6. 我无法在标准中找到这方面的保证(我也没想到),但是我用gcc进行的基本测试让我相信它总会如此。我也知道这个保证是针对POD类型的,但我们假设这种类型不能是POD。

    更新/澄清:这仅适用于单个二进制文件的单个编译;计算出的偏移量永远不会离开那一次执行。基本上,我希望能够在静态地图中唯一地标识类的字段,然后能够在某个宏/模板/ EVIL欺骗中查找该映射。这仅仅是为了我自己的娱乐,没有生命支持机器会依赖这个代码。

7 个答案:

答案 0 :(得分:5)

编译完程序后,是*

偏移量将保持不变。

但有一个非常重要的限制: foo必须专门指向FooClass对象。不是从FooClass派生的类,或其他任何东西。

C ++对成员偏移进行POD区分的原因是因为多重继承和vtable指针的位置(或缺少)可能会创建一个对象的地址与该对象的基地址不同的情况

答案 1 :(得分:3)

我不是专家,但无论如何我会尝试回答你:)

  1. FooClass是非POD类型。这意味着它可能包含privatepublicprotected的多个部分。在这样的部分中,顺序是成员定义的顺序,但是在这些部分中,顺序是任意的而且未指定。
  2. foo总是指向FooClass。那么我们保证没有完成偏移调整。至少在一次编译中,偏移量将是相同的(没有备份标准报价。但如果它们不同则无法工作。)
  3. 我们只关心单个编译器的行为。好吧,因为在访问修饰符的各个部分中未指定成员的顺序,并且允许编译器在成员之间添加填充,这不会给我们带来太大的好处。
  4. 我们只关心堆栈中的对象(自动存储持续时间)。好吧,我不知道这会如何改变对象布局的任何内容。
  5. 所以我认为你不能保证在整个编辑中偏移量是恒定的。对于一个编译中的考虑因素(因此,如果我们将使用其生成的代码使用随每个不同编译而变化的ABI的编译器),则偏移量不能相同。但即使您知道偏移量,也无法访问该成员。访问成员的唯一方法是使用成员访问运算符->.(在9.2 / 9中说过)。

    为什么不使用数据成员指针?它们允许安全地访问成员。这是一个例子:(looking up members by name)。

答案 2 :(得分:3)

在单个编译器中,编译器设置始终相同且没有任何内容添加到FooClass或从FooClass中删除,那么是,foo&(foo->bar)中存储的地址之间的距离将始终为是相同的,否则编译器将无法生成适用于编译单元的正确代码。

但是,一旦向类中添加任何内容或更改编译器设置,所有投注都将关闭。

答案 3 :(得分:3)

据我所知,这应该是这样的,POD课程与否。在编译时,基于编译器,体系结构,设置等,编译器确定类的大小和所有成员的偏移量。然后在编译单元中为类的所有实例修复此问题(如果保留了单定义规则,则扩展为链接单元)。

由于编译器按字面意思处理类型指针,即使基础类型错误(例如:指针已被错误地c样式转换),& foo和&(foo.bar)之间的计算距离将是同样,因为偏移量在编译时是静态已知的。

注意:这有效地完成了之前。例如,参见Microsoft的ATL数据绑定代码,使用它们的'offsetof'宏...

答案 4 :(得分:1)

我的头顶有两件事会影响对象的内部布局:

  • 成员对象的大小。希望如果更改成员定义,您将重新编译所有受影响的模块。
  • 打包pragma可能会更改成员之间添加的填充。确保所有使用该类的模块的包装相同,或者至少是定义类的部分。如果不这样做,那么你会遇到比不可预测的偏移更大的问题。

答案 5 :(得分:1)

底线:如果此类包含除POD之外的任何内容,则您绝对不能对偏移量做出任何假设。如果该课程只是公共POD的集合,那么您就是安全的。

这是一本优秀的中级C ++书中章节的一部分链接。如果你认真对待C ++,我建议大家阅读这本书。

这个特别的摘录解决了这里提出的问题的一部分:

http://my.safaribooksonline.com/0321321928/ch11?portal=oreilly

关于其他细节,请查看书籍。我上面的“底线”是对本章的简单概述。

答案 6 :(得分:1)

是的,偏移量是在编译时确定的,因此只要您不在比较编译器或编译器的偏移量,它就会始终保持不变。