private does not mean “only this object can access this member.”
It means “only this class and its friends can access this member.” That distinction matters because one object of a class can access the private members of another object of the same class.
The common assumption
When learning private, it is natural to think each object protects its own private data from every other object.
That mental model is slightly wrong.
Consider a class representing a bank account:
#include <iostream>
#include <string>
#include <string_view>
class BankAccount {
private:
std::string m_owner {};
double m_balance {};
public:
BankAccount(std::string_view owner, double balance)
: m_owner { owner }
, m_balance { balance }
{
}
void printComparison(const BankAccount& other) const{
std::cout << m_owner << " balance: $" << m_balance << '\n';
std::cout << other.m_owner << " balance: $" << other.m_balance << '\n';
}
};
int main(){
BankAccount alice { "Alice", 1500.0 };
BankAccount bruno { "Bruno", 900.0 };
alice.printComparison(bruno);
}
Terminal output:
Alice balance: $1500
Bruno balance: $900
This line is the important one:
std::cout << other.m_owner << " balance: $" << other.m_balance << '\\n';
m_owner and m_balance are private.
Yet alice.printComparison(bruno) can access bruno‘s private data because printComparison() is a member function of BankAccount.
Access is per class, not per object
C++ checks access based on the class that contains the code, not the specific object being accessed.
Inside a BankAccount member function, the code can access private members of any BankAccount object, thereby violating the private access specifier!
Both are allowed because both objects are instances of the same class.
This is the rule:
A member function of a class can access private members of any object of that same class.
It does not matter whether the object is *this, a parameter, a local object, or another instance returned from a function.
This does not break encapsulation
At first, this may look like a violation of private.
It is not.
The private members are still hidden from outside code:
int main(){
BankAccount alice { "Alice", 1500.0 };
// std::cout << alice.m_balance; // error: m_balance is private
}
Code outside the class still cannot access m_balance.
The access is only allowed inside the class’s own member functions. That means the class still controls how its data is used.
The class is trusted to manage its own invariants.
Why the rule is useful
Per-class access is useful when implementing operations that compare, copy, swap, or combine objects of the same type.
For example:
#include <iostream>
class Temperature {
private:
double m_celsius {};
public:
explicit Temperature(double celsius)
: m_celsius { celsius }
{
}
bool isHotterThan(const Temperature& other) const{
return m_celsius > other.m_celsius;
}
};
int main(){
Temperature room { 22.5 };
Temperature oven { 180.0 };
std::cout << std::boolalpha;
std::cout << oven.isHotterThan(room) << '\n';
}
Terminal output:
true
The function compares two Temperature objects:
return m_celsius > other.m_celsius;
This is clean and direct. If other.m_celsius were inaccessible, simple operations between objects of the same class would become unnecessarily awkward.
Takeaway
C++ access levels operate on a per-class basis.
A private member is hidden from outside code, but member functions of the same class can access that member in any object of that class.
The rule is simple: private protects a class from outside code, not one object from another object of the same class.