Structs and classes

C++ · Essentials · 14 min

User-defined types are how C++ programs model their domain, and the class is the foundation for RAII and the rest of the language.

A struct or class defines a new type that groups data members and member functions. The only difference between the two keywords is the default access: members of a struct are public by default, members of a class are private. By convention, use struct for plain bundles of data and class for types that protect an invariant, a rule that must always hold, such as "balance is never changed except through deposit".

  • Data members can have default values: double x = 0;.
  • Member functions see the members directly; inside them this points to the current object.
  • A member function marked const after its parameter list promises not to modify the object, and is the only kind you can call on a const object.
  • public: and private: sections control who can touch what. Keep data private and expose operations, so the class can check every change.

A struct with only public data and no constructors is an aggregate, initialized with braces in member order: Point p{1.5, 2.0};. C++20 adds designated initializers, Point q{.x = 3, .y = 4};, which must follow declaration order but can skip members, which keep their defaults.

Classes with invariants use a constructor, a member function with the class's name that runs when the object is created, to establish them. Mark single-argument constructors explicit so a plain std::string can't silently convert into an Account.

Objects are values: Point b = a; copies all members, and each object has its own data. Member functions don't add to an object's size; only data members do.

Example

#include <iostream>
#include <string>
#include <utility>

struct Point {            // aggregate: public data, no invariants
    double x = 0;
    double y = 0;
};

class Account {           // class: members private by default
public:
    explicit Account(std::string owner) : owner_{std::move(owner)} {}
    void deposit(long cents) { if (cents > 0) balance_ += cents; }
    long balance() const { return balance_; }
    const std::string& owner() const { return owner_; }
private:
    std::string owner_;
    long balance_ = 0;    // invariant: only changed through deposit()
};

int main() {
    Point p{1.5, 2.0};
    Point q{.x = 3, .y = 4};          // C++20 designated initializers
    Account acc{"Lin"};
    acc.deposit(500);
    acc.deposit(-100);                // ignored
    // acc.balance_ = 1'000'000;      // error: balance_ is private
    std::cout << p.x + q.y << ' ' << acc.owner() << ' ' << acc.balance() << '\n';  // 5.5 Lin 500
}

Remember

  • struct and class differ only in default access
  • Keep data private when the type has an invariant; expose operations
  • Aggregates take {...} initialisation; C++20 adds .member = value

Watch out for

  • Making every member public with getters and setters for each — that protects nothing; expose meaningful operations instead.

Check yourself

  1. What is the only language difference between `struct S { int a; };` and `class S { int a; };`?
  2. Why mark `long balance() const`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: en.cppreference.com/w/cpp/language/classes