Access Specifiers In Depth
Part of the Object Oriented Programming section of Coddy's C++ journey — lesson 35 of 104.
Now that you understand the three access specifiers, let's explore how they behave when you have multiple objects of the same class. A common misconception is that private means "only this object" - but it actually means "only this class."
Objects of the same class can access each other's private members:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Accessing another object's private member - allowed!
std::cout << other.secret;
}
};This works because access control is enforced at the class level, not the object level. The readSecret method is part of the Person class, so it can access private members of any Person object. This design enables operations like copy constructors and comparison operators to work with the internal state of other objects.
You can also switch between access specifiers multiple times within a class:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // Back to public
private:
void privateHelper(); // Private again
};While this is valid C++, it's generally better to group members by access level for readability. Most style guides recommend listing public members first (since they form the class interface), followed by protected, then private.
Challenge
EasyLet's build a wallet comparison system that demonstrates how objects of the same class can access each other's private members. This is a key insight about C++ access control — it's enforced at the class level, not the object level.
You'll create two files to organize your code:
Wallet.h: Define aWalletclass that stores private financial data but allows wallet-to-wallet comparisons. Your class should have:- Private members:
ownerName(string) andbalance(double) - A constructor that takes the owner name and initial balance
- A public getter
getOwnerName()that returns the owner's name - A method
hasMoreThan(const Wallet& other)that compares this wallet's balance with another wallet's private balance, returningtrueif this wallet has more money - A method
combinedBalance(const Wallet& other)that returns the sum of both wallets' private balances - A method
transferTo(Wallet& other, double amount)that moves money from this wallet to another — it should directly modify both wallets' private balances (only if amount is positive and doesn't exceed this wallet's balance)
- Private members:
main.cpp: Demonstrate cross-object private member access. Read two owner names and two balances from input (four lines: name1, balance1, name2, balance2), then:- Create two
Walletobjects with the input values - Print
"Combined wealth: "Combined wealth: $<amount>"lt;amount>"usingcombinedBalance() - Compare the wallets and print
"<name> has more money"for whichever owner has the higher balance (usehasMoreThan()to determine this) - Transfer
100.0from the first wallet to the second usingtransferTo() - Print
"After transfer:" - Print
"Combined wealth: "Combined wealth: $<amount>"lt;amount>"again (should be unchanged) - Compare again and print
"<name> has more money"(the result may have changed)
- Create two
The key concept here is that methods like hasMoreThan() and transferTo() can directly access another Wallet object's private balance member — no getters needed. This works because access control in C++ is class-based: any code inside the Wallet class can access private members of any Wallet object.
Format all monetary values with two decimal places using std::fixed and std::setprecision(2) from <iomanip>. Convert balance inputs using std::stod().
Cheat sheet
Access control in C++ is enforced at the class level, not the object level. This means objects of the same class can access each other's private members:
class Person {
std::string secret;
public:
Person(std::string s) : secret(s) {}
void readSecret(const Person& other) {
// Accessing another object's private member - allowed!
std::cout << other.secret;
}
};The readSecret method can access the private secret member of any Person object because it's part of the Person class. This design enables operations like copy constructors and comparison operators.
You can switch between access specifiers multiple times within a class:
class Mixed {
public:
void publicMethod1();
private:
int privateData;
public:
void publicMethod2(); // Back to public
private:
void privateHelper(); // Private again
};However, it's better practice to group members by access level for readability. Most style guides recommend listing public members first, followed by protected, then private.
Try it yourself
#include <iostream>
#include <string>
#include <iomanip>
#include "Wallet.h"
using namespace std;
int main() {
// Read input: name1, balance1, name2, balance2
string name1, name2;
string balanceStr1, balanceStr2;
getline(cin, name1);
getline(cin, balanceStr1);
getline(cin, name2);
getline(cin, balanceStr2);
double balance1 = stod(balanceStr1);
double balance2 = stod(balanceStr2);
// Set output formatting for monetary values
cout << fixed << setprecision(2);
// TODO: Create two Wallet objects with the input values
// TODO: Print combined wealth using combinedBalance()
// Format: "Combined wealth: $<amount>"
// TODO: Compare wallets using hasMoreThan() and print who has more money
// Format: "<name> has more money"
// TODO: Transfer 100.0 from first wallet to second using transferTo()
// TODO: Print "After transfer:"
// TODO: Print combined wealth again
// TODO: Compare again and print who has more money
return 0;
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesC++ Build & CompilationHeader Files & Source FilesNamespaces & ScopeIntroduction to OOP in C++Classes vs ObjectsThe 'this' PointerMethods (Member Functions)Attributes (Data Members)Ctors & Dtors BasicsRecap - Simple Calculator4Class Properties
Instance vs Static MembersGetters and SettersConst Member FunctionsMutable KeywordStatic Methods and VariablesFriend Functions & ClassesRecap - Bank Account Manager7Inheritance
Basic InheritanceInheritance Access LevelsCtor & Dtor Call OrderMethod OverridingVirtual Functions & VTableMultiple InheritanceVirtual InheritanceRecap - Employee Hierarchy2Memory Management
Stack vs Heap MemoryPointers and ReferencesDynamic Memory (new/delete)Smart Pointers in C++RAII in C++Recap - Dynamic Array Manager5Encapsulation
Access Specifiers in C++Access Specifiers In DepthInformation HidingStruct vs ClassNested & Inner ClassesRecap - Student Records System8Polymorphism
Compile vs Runtime PolymorphFunction OverloadingVirtual Functions RevisitedPure Virtual FunctionsAbstract ClassesInterface Design in C++Dynamic Casting & RTTIRecap - Shape Calculator3Constructors & Destructors
Default ConstructorParameterized ConstructorCopy ConstructorMove ConstructorConstructor Init ListsDelegating ConstructorsDestructor Deep DiveRule of Three / Five / ZeroRecap - String Class6Operator Overloading
Intro to Operator OverloadArithmetic Operator OverloadComparison Operator OverloadStream OperatorsAssignment Operator Overload[] and () Operator OverloadType Conversion OperatorsRecap - Matrix Class9Templates
Function TemplatesClass TemplatesTemplate SpecializationVariadic TemplatesSFINAE & Type Traits BasicsRecap - Generic Container