Do these members have unspecified ordering?
Your colleague is correct for C++03:
[C++03: 9.2/12]:
Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). [..]
But you are correct for C++11:
[C++11: 9.2/14]:
Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11). [..]
(Spot the difference.)
Does public and private have any influence on the memory layout of an object?
The answer depends on the language version, because this has changed from C++03 to C++11.
In C++03, the rule was:
Members within the same access control block (that is, from one of
public
,protected
,private
keywords to the next one from that set) are to be allocated in order of declaration within class, not necessarily contiguously.
In C++11, the rule was changed to this:
Members with the same access control level (public, protected, private) are to be allocated in order of declaration within class, not necessarily contiguously.
So in C++03, you could guarantee this (I use @
to mean the offset of a member within the class):
@m_ac < @m_scp
@m_i1 < @m_i2 < @m_b1 < @m_b2
In C++11, you have a few more guarantees:
@m_ac < @m_scp
@m_sc < @m_i1 < @m_i2 < @m_b1 < @m_b2
@m_name < @m_b3
In both versions, the compiler can re-order the members in different chains as it sees fit, and it can even interleave the chains.
Note that there is one more mechanism which can enter into the picture: standard-layout classes.
A class is standard-layout if it has no virtuals, if all its non-static data members have the same access control, it has no base classes or non-static data members of non-standard-layout type or reference type, and if it has at most one class with any non-static data members in its inheritance chain (i.e. it cannot both define its own non-static data members and inherit some from a base class).
If a class is standard-layout, there is an additional guarantee that the address of its first non-static data member is identical to that of the class object itself (which just means that padding cannot be present at the beginning of the class layout).
Note that the conditions on being standard-layout, along with practical compilers not making pessimising choices, effectively mean that in a standard-layout class, members will be arranged contiguously in order of declaration (with padding for alignment interspersed as necessary).
Struct Reordering by compiler
Why is the compiler forbidden (by the standard) from reordering the struct?
The basic reason is: for compatibility with C.
Remember that C is, originally, a high-level assembly language. It is quite common in C to view memory (network packets, ...) by reinterpreting the bytes as a specific struct
.
This has led to multiple features relying on this property:
C guaranteed that the address of a
struct
and the address of its first data member are one and the same, so C++ does too (in the absence ofvirtual
inheritance/methods).C guaranteed that if you have two
struct
A
andB
and both start with a data memberchar
followed by a data memberint
(and whatever after), then when you put them in aunion
you can write theB
member and read thechar
andint
through itsA
member, so C++ does too: Standard Layout.
The latter is extremely broad, and completely prevents any re-ordering of data members for most struct
(or class
).
Note that the Standard does allow some re-ordering: since C did not have the concept of access control, C++ specifies that the relative order of two data members with a different access control specifier is unspecified.
As far as I know, no compiler attempts to take advantage of it; but they could in theory.
Outside of C++, languages such as Rust allow compilers to re-order fields and the main Rust compiler (rustc) does so by default. Only historical decisions and a strong desire for backward compatibility prevent C++ from doing so.
Why does the C++ standard specifically grant leeway regarding memory layout of class data members with different access specifiers?
N2062 is the first C++ paper that deals with changes to C++98/03's POD definition. It was written as a means to resolve core issue 568, which is about PODs and type layouts. It represents the beginning of the design that leads to C++11's standard layout and trivial copyability definitions.
And yet, N2062 never even considers defining the layout of members with different access controls. It doesn't even give justification for why this restriction is in place. Nor does the final version of that proposal, which actually gives us trivially-copyable and standard-layout definitions. All versions of these proposals take the access control limitation as an fait accompli, rather than something that could have been changed.
All this suggests that the writer of the proposal had knowledge of at least one compiler/ABI that changes the order of members based on access controls.
Order of In Class Initialization versus Constructor Initialization List
This is guaranteed by the standard that non-static data members will be initialized in the order of their declarations in the class definition. How they're initialized (via default member initializer or member initializer list) and the order of these initializers don't matter.
[class.base.init]#13.3
(13.3) - Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
[ Note: The declaration order is mandated to ensure that base and member subobjects are destroyed in the reverse order of initialization. — end note ]
That means, the initialization order will always be ptr
-> m1
-> m2
.
Class layout in C++: Why are members sometimes ordered?
This freedom makes it impossible in theory to link binaries created by different compilers.
It's impossible for a number of reasons, and structure layout is the most minor. vtables, implementations of operator new
and delete
, data type sizes...
So what are the remaining reasons for the strict in-section ordering?
C compatibility, I would have thought, so that a struct defined in C packs the same way it does in C++ for a given compiler set.
And does the new C++
0911 standard provide a way to fully determine object layouts "by hand"?
No, no more than the current standard does.
For a class
or struct
with no vtable and entirely private (or public) fields, though, it's already possible if you use the [u]int[8|16|32|64]_t
types. What use case do you have for more than this?
Related Topics
Assembly Adc (Add with Carry) to C++
Generate Include File Name in a MACro
Enable a Single Warning in Visual Studio
How Performing Multiple Matrix Multiplications in Cuda
Spirit-Qi: How to Write a Nonterminal Parser
I Want to Generate the Nth Term of the Sequence 1,3,8,22,60 ,164 in Order(1) or Order of (Nlogn)
Why Would Anyone Use Set Instead of Unordered_Set
List of Common C++ Optimization Techniques
C++ Gdb Python Pretty Printing Tutorial
Why Does a Push_Back on an Std::List Change a Reverse Iterator Initialized with Rbegin
How to Compile for Windows Xp with Visual Studio 2012
Detecting Simulated Keyboard/Mouse Input
C++ Concept That Requires a Member Function with an Outputiterator as Parameter