AlgoMaster Logo

Access Modifiers (public, private, protected)

High Priority14 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

Access modifiers are the three keywords (public, private, protected) that decide who is allowed to touch a class member. They are how C++ lets you draw a line between the inside of a type (its data and helpers) and the outside (the rest of the program that uses it). This chapter covers what each keyword means on its own, how the labels group members inside a class, the default access rules for class versus struct, and the compiler errors you get when code tries to access a member it is not allowed to see.

The Three Keywords

Every member of a class or struct sits in one of three access categories. The category is set by a label inside the class body and applies to every member that follows it, until another label changes it.

KeywordWhat it allows
publicAny code that has a reference, pointer, or instance of the class can read or write this member.
privateOnly member functions of the same class (and friends, covered later) can touch this member.
protectedSame as private from the outside world's point of view. Members of derived classes get extra access. The inheritance side of this is covered in the Inheritance section.

A simple example: a Product class with one field of each kind.

The two commented-out lines are not bad style. They are real compile errors. Uncomment either one and the program refuses to build. The compiler is enforcing the access policy that the class author wrote.

That is the entire idea behind access modifiers. The class author decides which members are part of the public surface and which are internal details. Everyone else has to live within that decision.

Default Access: class Is Private, struct Is Public

C++ has two keywords for declaring user types, and the only language-level difference between them is what the default access is when you do not write any label at all.

A class starts out in "private" mode. Anything you list before the first access label is private. A struct starts out in "public" mode, so the same members would be public unless you say otherwise.

This is the same struct-versus-class distinction. Past the default-access rule, the two are interchangeable. You can put access labels in a struct, and you can put public: first in a class. C++ does not care.

The convention most codebases follow:

  • Use struct for plain data bags where every field is public anyway.
  • Use class for types that hide some state behind an interface.

This convention is not enforced by the language. It is purely a hint to the reader about what kind of type they are looking at.

Access Labels Group Members

The label syntax (public:, private:, protected:) is a bit unusual. It is not part of a specific member's declaration. It is a switch that applies to every member declared after it, until another label flips the switch.

The private: label affects both stock and reorderLevel, because there is no other label between them. One label, two members. The second public: label switches the access mode back, so isOnSale is public even though it is declared further down.

You can have as many labels as you want, in any order. The compiler reads them top to bottom and assigns each member to whichever category was active when it appeared.

This compiles. It is also painful to read. Most codebases prefer to put all public members together and all private members together, so a reader can scan the public surface without jumping around. A common pattern looks like this:

The functions at the top describe what the class can do. The fields at the bottom describe how it stores its state. Anyone reading the header sees the interface first and the implementation details only if they look further. This ordering is a style choice, but it is the dominant one in modern C++.

What private Actually Blocks

The word "private" is sometimes misread as "this code cannot see the member at all." That is not what it means. The member exists. The compiler knows about it. The memory is laid out for it. What private blocks is access from outside the class, which means everything except the class's own member functions and any friends it declares.

The class's own printPrice function reads price without issue. The same access from main would be a compile error. The fence runs along the class boundary, not the variable name.

This is also true the other way around. A Product member function can read another Product's private fields, as long as both instances are of the same class:

Access control is per-class, not per-instance. A class trusts itself with its own private state, no matter which instance the access flows through.

Why Information Hiding Matters

A fair question at this point: why bother making anything private at all? If price is just a number, what is the harm in letting the rest of the program write to it?

The reason is not paranoia. It is change control. Once a member is public, every line of code in the program is free to depend on it. If you decide later that price should be stored in cents instead of dollars, or computed from a base price plus tax, or fetched from somewhere else, you cannot make that change without breaking every caller that reaches in directly.

If price is private, the class controls every read and every write. You can change the internal representation, add validation, log every modification, or compute the value lazily, all without touching any code outside the class. The promise the class makes to the outside world (the public surface) stays the same, even when the implementation behind it changes.

The role of access modifiers is to make that controlled exposure possible in the first place.

Access modifiers are a compile-time concept. They do not generate runtime checks, do not add a single byte to the object, and do not slow anything down. The compiler enforces them while reading source code and then forgets they existed. There is no performance reason to prefer public over private.

Compiler Errors When You Cross the Line

When code tries to reach a private member from outside its class, the compiler stops you. The exact wording depends on the compiler, but the error always names the member and points out that access is blocked.

For example:

Compile it with g++ -std=c++17 product.cpp -o product and you get:

Two pieces of information come back. The line that tried the bad access, and the line in the class where the member was declared private. The second pointer is easy to overlook. The compiler is telling you exactly which private: label is blocking you, so you can decide whether the fix is to make the member public, add a public function that does what you wanted, or change your approach entirely.

Clang's wording is slightly different ('price' is a private member of 'Product'), but the structure is the same: the call site, then the declaration. Both compilers refuse to build the program.

A common variation of the same mistake is reading instead of writing:

Private blocks both reading and writing. If you cannot reach it to assign, you cannot reach it to print either. Both directions of access go through the same gate.

A Word on protected

The protected keyword sits between public and private. From the outside world's view, it acts exactly like private: code in main or in unrelated classes cannot touch a protected member.

So far, protected looks identical to private. The difference shows up when one class inherits from another. Member functions of a derived class can read and write their parent's protected members, but they cannot touch the parent's private ones. That is the entire reason protected exists.

The diagram shows the access fence from three viewpoints. The class itself sees everything. A derived class sees protected and public but not private. Outside code sees only public. For this chapter, the piece to remember is the rightmost column: from outside, protected and private are both off-limits, and public is the only door.

The mechanics of how derived classes get their extra access, and when picking protected over private is the better call, belong to the Inheritance section. Until then, treat protected as "private, with a future role covered later."

Putting It Together

A larger example uses all three modifiers in one class, plus a member function. This previews the shape that classes take in the rest of the OOP section, without getting into encapsulation principles or getter/setter conventions.

The orderId is public, so main writes to it directly. The customerEmail, total, and isPaid fields are off-limits from main, which is why the output shows an empty email, a zero total, and "no" for paid. The member function printSummary runs inside the class, so it sees everything and prints it. The three commented-out lines all fail to compile, each for a different access reason.

This is the picture for the rest of the OOP section. The class decides what its public surface looks like (orderId, printSummary), keeps its real state private (total, isPaid), and uses member functions to bridge the two safely. The chapters that follow add the tools for doing that bridging well.

Quiz

Access Specifiers Quiz

10 quizzes