Who Gets to Touch Your Data
When you wrote your first class you probably exposed every member to the outside world. That works, but it throws away one of the main reasons classes exist: encapsulation - hiding a class's internal state so the rest of the program can only interact with it through a controlled surface. Access specifiers are how you draw that boundary.
There are exactly three: public, private, and protected. Each one labels the members that follow it, and the label decides which code is allowed to read or write them. Get this right and your class enforces its own rules; get it wrong and any bug anywhere can corrupt your object's state.
The Three Specifiers
A specifier is a keyword followed by a colon. Every member declared after it - up to the next specifier - falls under that access level.
Uncomment the last line and the compiler refuses to build: balance is private, so main cannot poke at it directly. That's the point - the only way to change the balance is through deposit, which means you can later add validation (no negative deposits, logging, limits) in one place and trust it's always applied.
Here's the full breakdown:
// reachable from...
// public anywhere (any code that has the object)
// private only the same class's own members (+ friends)
// protected the same class's members AND derived classes (+ friends)
class vs struct: the Default
You can write as many specifier blocks as you like, in any order. What members get before you write the first one depends on whether you used class or struct:
- In a
class, members areprivateby default. - In a
struct, members arepublicby default.
That default is the only language-level difference between the two keywords. A struct can have methods, constructors, and private sections just like a class.
Convention is to use struct for plain bundles of public data and class when you want behavior and hidden state - but the compiler doesn't enforce that, only the default differs.
Encapsulation with Getters and Setters
The everyday pattern is: data goes in a private section, and a public method gives controlled access. A read-only getter returns the value; a setter validates before assigning. This is where private pays off.
Because celsius is private, there's no way to sneak an invalid value in - every write must go through setCelsius, which guards the invariant. Notice the getters are marked const: they promise not to modify the object, so you can call them on const Temperature objects too.
protected and Inheritance
protected only matters once inheritance enters the picture. It behaves like private for outside code, but a derived class can reach it. Use it for members a subclass legitimately needs but the public still shouldn't.
A common beginner mistake is reaching for protected on every data member "just in case a subclass needs it." That quietly widens your class's contract - now every subclass can depend on that field, and you can't change it freely. Prefer private and only promote to protected when a derived class genuinely needs the access.
The friend Escape Hatch
Sometimes a single outside function or class legitimately needs to see your internals - a classic case is operator overloading for <<, which you can't make a member. The friend keyword grants that one named entity access to your private and protected members, and nothing else.
friend is a deliberate, surgical exception - the class itself names exactly who it trusts, so nobody can grant themselves access from the outside. Use it sparingly; if you find yourself adding lots of friends, your members probably shouldn't have been private in the first place, or your design needs rethinking.
Common Mistakes to Avoid
- Making every member
public. It feels easy, but you lose all the validation and invariants encapsulation buys you. Default toprivatedata withpublicmethods. - Forgetting the
classdefault.class Foo { int x; };makesxprivate, sofoo.x = 5won't compile. If you meant a plain data bundle, usestructor add apublic:label. - Overusing
protected. It's a weaker boundary thanprivateand only relevant with inheritance. Reaching for it everywhere couples subclasses to fields you may want to change. - Expecting
privateto be a security feature. It's a compile-time rule that prevents accidental access, not encryption. The bytes are still in memory;privateis about clean design, not secrecy.
Next: Structs
You've now seen that struct is really just a class whose members are public by default. The next page, structs, digs into when that public-by-default default is exactly what you want - lightweight aggregates for grouping related values - and how struct is used in idiomatic C++ alongside full-featured classes.
Frequently Asked Questions
What is the difference between public, private, and protected in C++?
public members are reachable from anywhere. private members are reachable only from inside the same class (and its friends). protected is like private but also lets derived classes reach the member. By convention you keep data private and expose behavior through public methods.
Are members private by default in C++?
In a class, yes - everything is private until you write an access specifier. In a struct, the default is public. That single default is the only real difference between class and struct in C++; both can have methods, constructors, and access specifiers.
What does the friend keyword do in C++?
friend grants one specific function or class access to your private and protected members. It is a deliberate, narrow exception to encapsulation - the class names exactly who it trusts, so access is never granted implicitly.