Skip to main content

Object Layout in Memory

How C++ objects are arranged in memory: data members, padding, vtables, base class subobjects. Understanding layout is crucial for binary compatibility and optimization.

Memory Organization

Objects = members + padding + vtable pointer (if virtual) + base class subobjects (if inheritance)

Simple Class Layout

class Simple {
int a; // 4 bytes
char b; // 1 byte
// 3 bytes padding
double c; // 8 bytes
};
// Total: 16 bytes

// Memory layout:
// [a: 4][b: 1][pad: 3][c: 8]

See Memory Alignment for padding rules.

Empty Classes

class Empty {};
sizeof(Empty); // 1 (not 0!)

// Why? Two objects must have different addresses
Empty arr[2];
// &arr[0] != &arr[1] requires non-zero size

Empty Base Optimization (EBO):

class Base {};
class Derived : Base {
int x;
};
sizeof(Derived); // 4, not 5! (Base optimized away)

Virtual Functions and vtable

class Base {
int data; // 4 bytes
public:
virtual void foo();
};
// Layout: [vptr: 8][data: 4][padding: 4] = 16 bytes

// vtable (separate):
// [ptr to foo()]

vtable pointer (vptr) added as hidden first member.

Object: [vptr][data][padding]
|
v
vtable: [&Base::foo]

Single Inheritance

class Base {
int base_data; // 4 bytes
public:
virtual void foo();
};

class Derived : Base {
int derived_data; // 4 bytes
};

// Layout:
// [vptr: 8][base_data: 4][derived_data: 4][padding: 4] = 20 bytes

// vtable:
// [&Derived::foo or &Base::foo]

Memory:

[Base subobject: vptr, base_data][Derived data]

Multiple Inheritance

class A {
int a;
public:
virtual void fA();
};

class B {
int b;
public:
virtual void fB();
};

class C : public A, public B {
int c;
};

// Layout (simplified):
// [A's vptr][a][B's vptr][b][c]
// ^ ^
// | |
// vtable A vtable B

Two vptrs for two bases with virtual functions!

Virtual Inheritance (Diamond Problem)

class Base {
int base;
public:
virtual void f();
};

class Left : virtual Base {
int left;
};

class Right : virtual Base {
int right;
};

class Bottom : Left, Right {
int bottom;
};

// Layout (complex):
// [Left part][Right part][shared Base][Bottom data]
// Uses vbase pointer to locate shared Base

Virtual base stored separately, accessed via offset.

Inspecting Object Layout

GCC/Clang

# Show class layout
g++ -fdump-lang-class file.cpp

# Or
clang++ -Xclang -fdump-record-layouts file.cpp

MSVC

cl /d1reportAllClassLayout file.cpp

Example Output

class Widget {
char c;
int i;
double d;
};

Output:

*** Dumping AST Record Layout
0 | class Widget
0 | char c
4 | int i
8 | double d
| [sizeof=16, align=8]

offsetof Macro

#include <cstddef>

struct Point {
int x;
int y;
};

size_t x_offset = offsetof(Point, x); // 0
size_t y_offset = offsetof(Point, y); // 4

See Padding and offsetof for details.

POD and Standard Layout

// POD (Plain Old Data) - C++03
struct POD {
int x;
double y;
// No virtuals, all public, no constructors
};

// Standard Layout - C++11 (more flexible)
struct StandardLayout {
int x;
private:
double y;
public:
StandardLayout() : x(0), y(0.0) {} // OK
// No virtuals, same access for all members
};

static_assert(std::is_standard_layout_v<StandardLayout>);

Standard layout guarantees compatible C layout for first member.

Compiler-Specific Layouts

Different compilers may use different layouts (ABI differences):

class Widget {
virtual void foo();
int data;
};

// GCC/Clang (Itanium ABI): [vptr][data]
// MSVC: may differ in alignment/padding

Controlling Layout

// Explicit packing (GCC/Clang)
struct __attribute__((packed)) Packed {
char c;
int i; // Misaligned!
}; // sizeof = 5 (no padding)

// MSVC
#pragma pack(push, 1)
struct Packed {
char c;
int i;
};
#pragma pack(pop)

Warning: Packed structs cause slow/unsafe memory access. See Alignment.

Summary

info

Objects contain:

  • vptr to vtable (if virtual functions) - 8 bytes
  • Data members (in declaration order)
  • Padding (alignment)
  • Base class subobjects (if inheritance)

  • Size formula: vptr + members + padding (aligned to largest member)
  • Multiple inheritance → multiple vptrs
  • Virtual inheritance → shared base at end

Use offsetof to query member positions. POD and standard layout types have predictable C-compatible layouts.