Menu

C++ Access Specifiers: public, private, protected

How public, private, and protected control who can touch a class's members in C++ - the foundation of encapsulation, with getters, setters, and the friend escape hatch.

This page includes runnable editors - edit, run, and see output instantly.

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 are private by default.
  • In a struct, members are public by 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 to private data with public methods.
  • Forgetting the class default. class Foo { int x; }; makes x private, so foo.x = 5 won't compile. If you meant a plain data bundle, use struct or add a public: label.
  • Overusing protected. It's a weaker boundary than private and only relevant with inheritance. Reaching for it everywhere couples subclasses to fields you may want to change.
  • Expecting private to be a security feature. It's a compile-time rule that prevents accidental access, not encryption. The bytes are still in memory; private is 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.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED