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
thispoints to the current object. - A member function marked
constafter its parameter list promises not to modify the object, and is the only kind you can call on a const object. public:andprivate: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
structandclassdiffer 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.