Protected Members
Master the protected access specifier to control inheritance while maintaining encapsulation
Understand how protected access control enables controlled sharing between base and derived classes while maintaining encapsulation.
A Simple Example
#include <iostream>
#include <string>
class GameCharacter {
protected:
std::string name;
int health;
int maxHealth;
int level;
void levelUp() {
level++;
maxHealth += 20;
health = maxHealth;
std::cout << name << " leveled up to " << level << "!" << "\n";
}
bool takeDamageInternal(int damage) {
health -= damage;
if (health < 0) health = 0;
return health > 0;
}
private:
int experience;
public:
GameCharacter(std::string name, int hp)
: name{name}, health{hp}, maxHealth{hp}, level{1}, experience{0} {
}
void displayStatus() {
std::cout << name << " [Lv" << level << "] HP: " << health << "/" << maxHealth << "\n";
}
void gainExperience(int exp) {
experience += exp;
if (experience >= 100) {
levelUp();
experience = 0;
}
}
};
class Warrior : public GameCharacter {
private:
int armor;
public:
Warrior(std::string name) : GameCharacter{name, 150}, armor{50} {
std::cout << "Warrior " << name << " created with " << armor << " armor" << "\n";
}
void takeDamage(int damage) {
int reducedDamage = damage - (armor / 10);
if (reducedDamage < 0) reducedDamage = 0;
std::cout << name << " (Warrior) takes " << reducedDamage << " damage (reduced from " << damage << ")" << "\n";
takeDamageInternal(reducedDamage);
}
void battleCry() {
std::cout << name << " shouts a battle cry!" << "\n";
health += 10;
if (health > maxHealth) health = maxHealth;
}
};
class Mage : public GameCharacter {
private:
int mana;
public:
Mage(std::string name) : GameCharacter{name, 80}, mana{100} {
std::cout << "Mage " << name << " created with " << mana << " mana" << "\n";
}
void takeDamage(int damage) {
std::cout << name << " (Mage) takes " << damage << " damage" << "\n";
takeDamageInternal(damage);
}
void castHeal() {
if (mana >= 20) {
mana -= 20;
int healAmount = level * 10;
health += healAmount;
if (health > maxHealth) health = maxHealth;
std::cout << name << " heals for " << healAmount << " HP" << "\n";
} else {
std::cout << "Not enough mana!" << "\n";
}
}
};
int main() {
Warrior warrior{"Thorin"};
warrior.displayStatus();
warrior.takeDamage(30);
warrior.battleCry();
warrior.gainExperience(100);
warrior.displayStatus();
std::cout << "\n";
Mage mage{"Gandalf"};
mage.displayStatus();
mage.takeDamage(20);
mage.castHeal();
mage.displayStatus();
// warrior.health = 1000; // ERROR: protected members not accessible here!
// warrior.levelUp(); // ERROR: protected method not accessible!
return 0;
}
Breaking It Down
What Protected Members Are
- Protected members are accessible within the class and in all derived classes
- External code (like main()) cannot access protected members directly
- It creates a middle ground between private (completely hidden) and public (accessible anywhere)
- Remember: Protected is about family trust - children can access, but strangers cannot
Protected Data Members
- In the example, name, health, maxHealth, and level are protected
- Both Warrior and Mage can directly access these members (like accessing health in battleCry)
- External code cannot access them: warrior.health would cause a compiler error
- Remember: Use protected for state that derived classes need to manipulate
Protected Member Functions
- Methods like levelUp() and takeDamageInternal() are protected
- Derived classes can call these methods to reuse base class logic
- This allows code sharing without exposing implementation details publicly
- Remember: Protected methods define the interface between base and derived classes
When to Use Protected vs Private
- Use private by default - only promote to protected when derived classes genuinely need access
- Too much protected access breaks encapsulation like too much public access
- Protected is for implementation inheritance - sharing how something works
- Remember: Make it private first, promote to protected only when necessary
Why This Matters
- Protected members solve a critical design problem: how do you share functionality with derived classes without exposing implementation details to the world?
- It is like giving your children the keys to the house (they live there) without giving keys to strangers.
- Protected access is essential for creating extensible class hierarchies while maintaining strong encapsulation.
- It is the backbone of frameworks and libraries where you design base classes that others will extend.
Critical Insight
Protected is about trust - you are giving derived classes access to your internals, but you are trusting them to use it correctly. This is why protected should be used carefully. A good rule of thumb: make it private first, only promote to protected if derived classes genuinely need it. Too much protected access can break encapsulation just like too much public access.
Think of it like house keys: public is giving keys to everyone, private is keeping all keys yourself, and protected is giving keys to your family members who live with you.
Best Practices
Make it private first: Default to private members and only promote to protected when derived classes genuinely need access. This minimizes coupling.
Minimize protected interface: Keep the protected interface small and well-documented. Every protected member is a promise to derived classes that you must maintain.
Use protected methods over data: Prefer protected methods that provide controlled access rather than exposing data members directly.
Document protected members: Add comments explaining why members are protected and how derived classes should use them.
Common Mistakes
Overusing Protected: Making too many members protected can break encapsulation just like making them public.
Confusing Protected with Public: Protected does not mean "accessible everywhere." External code still cannot access it.
Forgetting Access in Inheritance: Using private inheritance makes even public base members inaccessible outside the derived class.
Protected Data Members: Exposing data members as protected allows derived classes to break invariants. Prefer protected methods.
Debug Challenge
This derived class is trying to access a base class member. Fix the access specifier in the base class:
Quick Quiz
- Who can access protected members?
- Can external code (like main()) access protected members?
- What is the difference between private and protected?
Practice Playground
Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.
Output:
Error:
Lesson Progress
- Fix This Code
- Quick Quiz
- Practice Playground - run once