C ++ experts: is it a shift of a member variable into a class constant under these conditions?

Given a foo variable of type FooClass* and a member variable in this class named bar , this distance between foo and &(foo->bar) same in any situation with some restrictions:

  • FooClass is a non-POD type.

  • We know that foo will always point to an instance of FooClass , and not to some subtype.

  • We only care about behavior under one compiler and one compilation; that is, the value that gcc can lead to is never used in code compiled with MSVC, and is never saved for reuse between compilations. It is calculated in binary format and used in binary format, and that is it.

  • We do not use custom new , although some instances of the class can be allocated on the stack and allocated using the heap.

  • There is no explicit ctor for FooClass ; it relies on a compiler-generated one (and each of the fields in FooClass is either a POD or a default construct).

I cannot find a guarantee in this standard (and did not expect it), but my rudimentary testing with gcc makes me believe that this will always be so. I also know that this guarantee is for POD types, but suppose this type cannot be POD.

Update / clarification: this is only for one compilation of one binary file; Design offsets will never leave this one-time execution. Basically, I want to be able to uniquely identify the fields of a class in a static map, and then be able to look for some macro commands / templates / EVIL in this map. This is just for my own entertainment, and no life support machines will rely on this code.

+6
c ++
source share
7 answers

After you compiled your program, Yes * .

The offset will remain constant.

However, there is one very important limitation: foo must point specifically to the FooClass object . Not a class derived from FooClass, or anything else for that matter.

The reason C ++ makes a POD distinction regarding member offsets is that both multiple inheritance and the location (or absence) of the vtable pointer can create situations where the address of the object does not match the address of this base of objects.

+5
source share

I am not an expert, but I will try to answer anyway.

  • FooClass is a non-POD type. This means that it can have several private , public or protected sections. In this section, the order is determined by the definition of members, but through these sections the order is arbitrary and unspecified.
  • foo will always point to FooClass. So, we have a guarantee that offset adjustment is not performed. In at least one compilation, the offsets will be the same (they do not have a backup of the standard quote, but they cannot work if they were different).
  • We only care about behavior on one compiler. Well, since the order of members is not specified by access modifier sections, and the compiler is allowed to add additions between members, this will not buy us much.
  • We only need objects on the stack (time for automatic storage). Well, I donโ€™t see how this changes anything from the layout of the object.

So, in the end, I donโ€™t think you have a guarantee that the bias will be constant in compilations. For reasons inside one compilation (so if we play with a compiler whose generated code uses an ABI that changes with every other compilation), the offset simply cannot be different. But even if you know the bias, you cannot access the member. The only way to access a member is to use the member access operator -> and . (which is specified in 9.2 / 9).

Why not use data item pointers? They allow you to securely access members. Here is an example: ( search for members by name ).

+3
source share

Under one compiler, where the compiler options are always the same, and nothing is added or removed from FooClass, then yes, the distance between the address stored in foo and &(foo->bar) will always be the same, or the compiler will not be able to generate the correct code that worked with compilation units.

However, as soon as you add something to the class or change the compiler settings, all bets will be disabled.

+3
source share

As far as I know, it should always be, POD class or not. At compile time, based on the compiler, architecture, settings, etc., the Compiler determines the size of the class and the offsets of all its members. Then it is fixed for all instances of the class in the compilation module (and by extension of the associated module if the rule of one definition is saved).

Since the compiler treats type pointers literally, even if the base type is wrong (for example: the pointer was incorrectly entered in c-style), the calculated distance between & foo and & (foo.bar) will be the same, since the offset is known statically at compile time .

Note. This was done, first of all, effectively. See, for example, Microsoft ATL data binding code using the "offsetof" macro ...

+3
source share

On the top of the head, there are two things that affect the interior layout of an object:

  • size of member objects. We hope you recompile all affected modules if you change the definition of a member.

pragmas can change the addition added between members. Make sure the packaging is the same for all modules that use the class, or at least the section that defines the class. If you do not, you will have more problems than unpredictable biases.

+1
source share

Bottom line: if this class contains nothing but POD, then you cannot make absolutely any assumptions about offsets. If a class is just a collection of public PODs, then you are safe.

Here is a link to part of a chapter in an excellent C ++ intermediate book. I recommend everyone to read this book if you are serious about C ++.

This specific excerpt addresses part of the question presented here:

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

See the book for other details. My bottom line above is a simplified summary of this chapter.

+1
source share

Yes, the offset is determined at compile time, so until you compare offsets between compilers or compilers, it will always be constant.

+1
source share

All Articles