Grouping related data

Imagine building a simple recipe application. Each recipe needs a name, prep time in minutes, cooking time, and number of servings. Using separate variables:

std::string recipeName{};
int prepMinutes{};
int cookMinutes{};
int servings{};

Now add a second recipe. You need four more variables with different names:

std::string recipe2Name{};
int recipe2PrepMinutes{};
int recipe2CookMinutes{};
int recipe2Servings{};

This approach breaks down quickly. Nothing connects these variables conceptually. Passing a recipe to a function means passing four separate arguments. Returning a recipe from a function is impossible since functions return only one value. Adding more recipes multiplies the problem.

Structures bundle variables together

A struct (short for structure) lets you define a custom type that groups multiple variables into one unit:

struct Recipe
{
    std::string name{};
    int prepMinutes{};
    int cookMinutes{};
    int servings{};
};

Breaking this down:

  • struct tells the compiler we're defining a new type
  • Recipe is the type name (capital letter by convention for user-defined types)
  • Inside the braces, we list the variables this type contains
  • Each variable inside is called a data member or member variable
  • The empty braces {} after each member ensure they're initialized to safe defaults
  • The semicolon after the closing brace is required

This definition creates a type, not a variable. Think of it as a blueprint.

Creating struct variables

Once defined, use your struct type like any built-in type:

Recipe pancakes{};    // Creates a Recipe variable named pancakes
Recipe omelette{};    // Creates another Recipe variable

Each variable contains its own independent copy of all the members. Changing pancakes.servings doesn't affect omelette.servings.

The member selection operator

Access individual members using the dot operator (.):

#include <iostream>
#include <string>

struct Recipe
{
    std::string name{};
    int prepMinutes{};
    int cookMinutes{};
    int servings{};
};

int main()
{
    Recipe pancakes{};

    pancakes.name = "Fluffy Pancakes";
    pancakes.prepMinutes = 10;
    pancakes.cookMinutes = 15;
    pancakes.servings = 4;

    std::cout << pancakes.name << '\n';
    std::cout << "Total time: " << (pancakes.prepMinutes + pancakes.cookMinutes) << " minutes\n";

    return 0;
}

Output:

Fluffy Pancakes
Total time: 25 minutes

The syntax pancakes.prepMinutes means "the prepMinutes member of the pancakes object." Members behave like regular variables - you can assign to them, use them in expressions, pass them to functions, and so on.

Working with multiple objects

Each struct variable maintains its own member values:

#include <iostream>
#include <string>

struct Recipe
{
    std::string name{};
    int prepMinutes{};
    int cookMinutes{};
    int servings{};
};

int main()
{
    Recipe breakfast{};
    breakfast.name = "Scrambled Eggs";
    breakfast.prepMinutes = 5;
    breakfast.cookMinutes = 8;
    breakfast.servings = 2;

    Recipe dinner{};
    dinner.name = "Pasta Carbonara";
    dinner.prepMinutes = 15;
    dinner.cookMinutes = 20;
    dinner.servings = 4;

    // Compare cooking times
    if (breakfast.cookMinutes < dinner.cookMinutes)
        std::cout << breakfast.name << " cooks faster\n";

    // Calculate total prep time for both meals
    int totalPrep{ breakfast.prepMinutes + dinner.prepMinutes };
    std::cout << "Total prep time: " << totalPrep << " minutes\n";

    // Double the dinner servings
    dinner.servings *= 2;
    std::cout << dinner.name << " now serves " << dinner.servings << '\n';

    return 0;
}

Output:

Scrambled Eggs cooks faster
Total prep time: 20 minutes
Pasta Carbonara now serves 8

Notice how breakfast.name and dinner.name are clearly distinguished. The struct provides context that separate variables lack.

What makes a member

The term member appears constantly in C++. A member is anything declared inside a struct (or class) definition:

struct Point
{
    double x{};    // data member
    double y{};    // data member
};

Here x and y are members of the Point type. Every Point object you create has its own x and y.

Members can only be accessed through an object of that type. You can't write Point.x - you need an actual variable like origin.x or destination.y.

Struct definitions don't create objects

A common point of confusion: the struct definition itself creates no variables:

struct Color
{
    int red{};
    int green{};
    int blue{};
};  // No Color objects exist yet

Color background{};  // Now one Color object exists
Color foreground{};  // Now two Color objects exist

The definition tells the compiler what a Color looks like. Only when you declare variables of type Color do actual objects come into existence.

Where to define structs

Struct definitions typically appear:

  • At file scope (outside any function), making them available throughout the file
  • In header files when multiple source files need the same struct
#include <iostream>

// Struct definition at file scope
struct Temperature
{
    double celsius{};
    double fahrenheit{};
};

int main()
{
    Temperature today{};
    today.celsius = 22.0;
    today.fahrenheit = today.celsius * 9.0 / 5.0 + 32.0;

    std::cout << today.celsius << "C = " << today.fahrenheit << "F\n";

    return 0;
}

Defining inside a function limits the struct's scope to that function, which is rarely useful.

Why structs matter

Structs solve real organizational problems:

  1. Conceptual grouping: Related data stays together
  2. Simpler function interfaces: Pass one struct instead of many parameters
  3. Return multiple values: Functions can return a struct containing several pieces of data
  4. Scalability: Adding a hundred recipes means a hundred struct variables, not four hundred separate variables
  5. Consistency: All recipes have the same member names, reducing errors

The next lesson covers how to initialize struct members more elegantly than assigning each one individually.

Summary

  • Structs are user-defined types that bundle multiple variables into a single unit
  • Data members are the variables declared inside a struct definition
  • The member selection operator (.) accesses individual members of a struct object
  • Struct definitions create types, not objects - you must declare variables separately
  • Each struct variable contains its own independent copy of all members
  • Members behave like regular variables and support all normal operations
  • Define structs at file scope or in headers so they're available where needed