Modern C++ Programming
Learn the fundamentals and advanced features of Modern C++ to build efficient, maintainable, and high-performance applications.
Illustration of a simple physics simulation
Chapter 2 — Types, Structs, and Data Layout

Modern C++ gives you powerful tools for expressing data clearly, safely, and efficiently. This chapter teaches you how to think about types, structs, and data layout the way professional engineers do — especially in systems where performance, clarity, and modularity matter.

We will explore how data is represented in memory, how struct design affects performance, and how to build clean, predictable types that integrate naturally into larger systems.

Why Data Layout Matters

In many languages, data layout is abstracted away. In C++, it is part of the language’s identity.

Data layout affects:

Even if you never write low level code, understanding data layout makes you a better C++ engineer.

The Three Kinds of Types in Modern C++

Modern C++ encourages you to think in terms of three broad categories:

  1. Value Types
    Small, self contained objects that behave like mathematical values.
    Examples:
    • int, float, bool
    • std::string
    • std::array<T, N>
    • Custom math types (vectors, matrices)
    Value types are cheap to copy, cheap to move, and easy to reason about.
  2. Aggregate Types (Structs)
    Collections of values grouped together.
    Examples:
    • struct transform { vec3 pos; vec3 rot; vec3 scale; };
    • struct vertex { vec3 pos; vec3 normal; vec2 uv; };
    Aggregates define data layout explicitly.
  3. Object Types (Classes with invariants)
    Types that enforce rules, invariants, or behavior.
    Examples:
    • File (must close handle)
    • Texture (must release GPU resource)
    • Window (must destroy OS handle)
    Object types use RAII and encapsulation.

This chapter focuses on value types and aggregate types, the foundation of clean data design.

Structs vs Classes (Modern Perspective)

In modern C++:

That’s the only difference.

Use struct when representing plain data:

struct vec3
{
    float x, y, z;
};

Use class when representing behavior or invariants:

class Texture
{
public:
    Texture(const std::string& path);
    ~Texture();

private:
    GLuint id;
};

This simple convention makes code more readable and intentional.

Designing Clean Structs

A good struct:

Example:

struct transform
{
    vec3 position;
    vec3 rotation;
    vec3 scale;
};

This struct is predictable, easy to serialize, and easy to use.

Alignment and Padding

C++ aligns data for performance. This can introduce padding between members.

Example:

struct Example
{
    char a;   // 1 byte
    int b;    // 4 bytes
    char c;   // 1 byte
};

Memory layout might be:

a _ _ _ b b b b c _ _ _

Padding ensures proper alignment for int.

What this affects:

How to reduce padding: Group members by size:

struct ExampleOptimized
{
    int b;
    char a;
    char c;
};

This produces tighter layout:

b b b b a c _ _
POD Types (Plain Old Data)

A POD type:

POD types are ideal for:

Example:

struct vertex
{
    float x, y, z;
    float nx, ny, nz;
    float u, v;
};

This struct is POD and safe to memcpy.

Engine Friendly Math Types

Math types are the backbone of graphics and simulation.

Example:

struct vec3
{
    float x, y, z;

    vec3() = default;
    vec3(float x, float y, float z)
        : x(x), y(y), z(z) {}
};

These types should be:

Avoid heap allocation inside math types.

Passing Types: Value, Reference, or Pointer?

Modern C++ uses clear rules:

Pass by value
When the type is small and cheap to copy.
void Move(vec3 delta);
Pass by const reference
When the type is large or expensive to copy.
void SetTransform(const transform& t);
Pass by pointer
When the argument may be null or optional.
void AttachChild(Node* child);
Pass by reference
When the argument must exist.
void Update(transform& t);

These conventions make APIs predictable.

Designing Types for Clarity

Good type design:

Example of clear design:

struct Color
{
    float r, g, b, a;
};

Example of unclear design:

class Color
{
private:
    float* data; // heap allocation
public:
    Color();
    ~Color();
};

The second version is harder to reason about and unnecessary.

Summary

In this chapter, we saw:

These principles form the foundation for the architectural chapters that follow.