C++, End to End
Introduction to Classes
Everything up to this point has been about data and functions as separate things. A class bundles both together: the data that describes an object, and the functions that operate on it, in one type.
A struct and a class in C++ are almost the same thing. The only difference is the default access level: members of a struct are public unless you say otherwise, members of a class are private unless you say otherwise. Everything else, constructors, member functions, inheritance, applies equally to both. The convention is to use struct for plain data with no invariants to protect, and class when you're bundling behavior and enforcing rules about what states the object can be in.
Member variables and member functions
A member variable belongs to each instance of the class. A member function is a function that has implicit access to a particular instance's member variables, without you needing to pass that instance in as an argument.
Inside distanceFromOrigin, x and y refer to the member variables of whichever Point object the function was called on. There's no explicit parameter for that object because it's passed implicitly.
The implicit this pointer
Every non-static member function secretly takes an extra parameter: a pointer to the object it was called on, named this. You almost never need to write this explicitly, but it explains a few things that otherwise look like magic: how the compiler knows which object's x you mean, and why a member function can return *this to support chaining.
Constructors
A constructor is a special member function that runs when an object is created. Its job is to establish the object's invariants, whatever must be true about its state for the rest of the class to work correctly. If a type has no sensible "default" state, don't give it a default constructor; make the caller supply what's needed.
Member initializer lists
The : width(w), height(h) part is a member initializer list. It runs before the constructor body and directly initializes each member. This isn't just style. Two categories of members can only be set this way, never by assignment inside the body:
constmembers: once aconstmember is default-constructed (left uninitialized), you can never assign to it, so it must be initialized in the list.- Reference members: a reference must be bound to something at the moment it's created; there's no such thing as an unbound reference to assign to later.
There's a second, subtler reason to prefer the initializer list even for ordinary members: members are always initialized in the order they're declared in the class, not the order they appear in the initializer list. If member B's initialization depends on member A already having a value, list order won't save you if the declaration order is wrong. Some compilers warn about this (-Wreorder), and it's worth keeping on.
Default and parameterized constructors, together
A class can have multiple constructors, overloaded like any other function. If you provide a parameterized constructor and still want a no-argument way to create the object, you have to write the default constructor explicitly. It's not there for free once you've added any other constructor.
Access specifiers
public, private, and protected control what code outside the class can touch. public members are part of the class's interface, anyone can use them. private members are implementation detail, only the class's own member functions (and friends, covered later) can touch them. protected is like private but also visible to derived classes; it matters once you get to inheritance.
Encapsulation isn't about hiding data for its own sake. It's about controlling the ways an object's state can change, so the class can guarantee its own invariants. If every member is public, any code anywhere can put the object into a state the class never intended, and there's no single place left to enforce correctness.
If balance were public, nothing would stop acc.balance = -1000; from running, and the class's promise that balance never goes negative through deposit would be meaningless, because there'd be a second door into the object that skips the check entirely.
Try it yourself: a class with a broken invariant›
The Temperature class below is supposed to only ever store a value in Celsius, and always report it correctly in both Celsius and Fahrenheit. Something's wrong. Find it, then fix it so the invariant actually holds no matter how the object is used.
The problem: celsius is public, so anything can set it to an impossible value, and the class has no way to reject it. Making celsius private and only allowing changes through a setter that validates the range (absolute zero is -273.15°C) closes the gap. This is exactly the encapsulation argument from above, not an abstract rule, an actual bug you can trigger.