Dataslope logoDataslope

Structs and Aggregates

Bundling related values into a single named type — the gateway to object-oriented programming.

Once your programs grow past a few variables, you'll find yourself passing the same group of values around together: a point's x and y, a person's name, age, email. C++ lets you give that group a name — a struct — and then treat it as one thing.

A struct is a named bundle

struct Point {
    double x;
    double y;
};

That's it. Point is now a type. You can create one, copy it, return it, store it in a vector, anything you can do with a built-in type.

Code Block
C++ 20 (202002L)

In memory, a Point is just the two doubles laid out one after the other:

The compiler may insert padding to align fields properly. Don't worry about that yet — just know sizeof(Point) may be slightly larger than the sum of its parts.

Initialization shapes

Point a;                  // default-initialized; fields are uninitialized!
Point b{};                // value-initialized; both fields zero
Point c{3.0, 4.0};        // aggregate init in declaration order
Point d{.x = 3.0, .y = 4.0};   // designated initializers (C++20)

The third and fourth forms are the safest and most readable. Always explicitly initialize structs — uninitialized fields are a classic source of silent bugs.

Passing structs to functions

Structs follow the same value/reference rules as built-in types.

double length(const Point& p) {   // const ref: no copy, can't modify
    return std::sqrt(p.x * p.x + p.y * p.y);
}

void scale(Point& p, double k) {  // non-const ref: modify caller
    p.x *= k;
    p.y *= k;
}

Point midpoint(Point a, Point b) {  // by value: small struct, fine
    return {(a.x + b.x) / 2.0, (a.y + b.y) / 2.0};
}

Rule of thumb: pass by const& unless the struct is two or three fundamental fields, in which case by value is fine and may even be faster.

Aggregates of aggregates

Structs compose. A Rectangle can be made of two Points:

Code Block
C++ 20 (202002L)

This is the seed of object-oriented design: small, focused types combined into larger ones.

std::pair and std::tuple

For one-off groupings where naming a struct would be overkill, the standard library provides:

Code Block
C++ 20 (202002L)

For anything more than two or three values, or anything that will appear in more than one function, define a real struct. It's far more readable.

struct vs class

In C++, struct and class are almost the same. The only language-level difference is the default access level:

  • struct members are public by default.
  • class members are private by default.

Conventionally:

  • Use struct for plain data bundles (no invariants, no methods, or trivial methods).
  • Use class when you have invariants to protect (data that must always satisfy some condition) — which leads us straight into the object-oriented chapter.

Challenges

Challenge
C++ 20 (202002L)
Average of points

Given a hard-coded list of three Points in main, compute the centroid (average of x, average of y) and print it as x y with no extra formatting. Expected output: 3 4.

Test your understanding

QuestionSelect one

In C++, what is the only language-level difference between struct and class?

struct cannot have methods.

class is allocated on the heap, struct on the stack.

The default member access: struct members are public by default, class members are private by default.

struct does not support inheritance.

QuestionSelect one

Why is Point p; (default-initialized) potentially dangerous?

It allocates memory on the heap.

The fields hold whatever bytes were already in that memory, so reading them is undefined behavior until you assign meaningful values.

It is forbidden by the C++ standard.

It causes the compiler to emit a warning.

Next: the leap from passive data bundles to active objects with behaviour — classes.

On this page