Chapter
1 stated that a
struct
written for a C compiler and later compiled with C++ would be unchanged. This
referred primarily to the object layout of the
struct,
that is, where the storage for the individual variables is positioned in the
memory allocated for the object. If the C++ compiler changed the layout of
C
structs,
then any C code you wrote that inadvisably took advantage of knowledge of the
positions of variables in the
struct
would break.
When
you start using access specifiers, however, you’ve moved completely into
the C++ realm, and things change a bit. Within a particular “access
block” (a group of declarations delimited by access specifiers), the
variables are guaranteed to be laid out contiguously, as in C. However, the
access blocks themselves may not appear in the object in the order that you
declare them. Although the compiler will usually lay the blocks out exactly as
you see them, there is no rule about it, because a particular machine
architecture and/or operating environment may have explicit support for
private
and
protected
that might require those blocks to be placed in special memory locations. The
language specification doesn’t want to restrict this kind of advantage.
Access
specifiers are part of the structure and don’t affect the objects created
from the structure. All of the access specification information disappears
before the program is run; generally this happens during compilation. In a
running program, objects become “regions of storage” and nothing
more. Thus, if you really want to you can break all the rules and access memory
directly, as you can in C. C++ is not designed to prevent you from doing unwise
things. It just provides you with a much easier, highly desirable alternative.
In
general, it’s not a good idea to depend on anything that’s
implementation-specific when you’re writing a program. When you must,
those specifics should be encapsulated inside a structure, so any porting
changes are focused in one place.