Module 03: Inheritance
Key Concepts:
- Base and derived classes
- Access specifiers with inheritance
- Constructor/destructor chaining
- Member access in inheritance
- Multiple inheritance (Diamond Problem)
Why This Matters
Section titled “Why This Matters”Inheritance is the foundation of object-oriented design. It enables code reuse and establishes relationships between types.
Why Inheritance? (The Problem: Code Duplication)
Section titled “Why Inheritance? (The Problem: Code Duplication)”Imagine you’re writing a game with different robot types:
// Without inheritance - lots of duplicated code!class ClapTrap { std::string _name; int _hitPoints; int _energyPoints; void attack(std::string target); void takeDamage(int amount); void beRepaired(int amount);};
class ScavTrap { std::string _name; // Duplicated! int _hitPoints; // Duplicated! int _energyPoints; // Duplicated! void attack(std::string target); // Similar logic! void takeDamage(int amount); // Duplicated! void beRepaired(int amount); // Duplicated! void guardGate(); // Only this is unique};
class FragTrap { std::string _name; // Duplicated AGAIN! // ... same duplication problem};This is painful:
- Same code written multiple times
- Bug fixes must be applied in multiple places
- Adding features means editing many classes
Inheritance solves this: Extract common code into a base class, then derive specialized classes that add unique features:
class ClapTrap { /* common code */ };class ScavTrap : public ClapTrap { void guardGate(); }; // Adds guardGateclass FragTrap : public ClapTrap { void highFivesGuys(); }; // Adds highFivesUnderstanding Inheritance Syntax (For Beginners)
Section titled “Understanding Inheritance Syntax (For Beginners)”This module introduces class inheritance. Here are the new operators and syntax you’ll encounter:
The : Operator (Inheritance Declaration)
Section titled “The : Operator (Inheritance Declaration)”class Derived : public Base { }; // Derived inherits from Base:(colon) introduces the inheritance list- It says “this class IS-A kind of that class”
publicmeans base class public members stay public in derived class- Read it as: “Derived inherits publicly from Base”
The virtual Keyword (Inheritance Modifier)
Section titled “The virtual Keyword (Inheritance Modifier)”class Middle : virtual public Base { }; // Virtual inheritancevirtualin inheritance solves the “diamond problem”- Without
virtual, a class inheriting from two classes that share a base gets TWO copies of the base - With
virtual, only ONE copy of the base class exists - Use this when multiple inheritance creates a diamond shape in the class hierarchy
Accessing Base Class Members with ::
Section titled “Accessing Base Class Members with ::”ClapTrap::attack(target); // Call base class versionBase::operator=(other); // Call base assignment operatorClassName::lets you explicitly call a base class method- This is necessary when you’ve overridden (redefined) a method in the derived class
- Use it to access the base class implementation from the derived class
The using Declaration
Section titled “The using Declaration”using ScavTrap::attack; // Bring ScavTrap's attack into current scopeusing Base::print; // Bring ALL Base::print overloads into scopeusingimports names from another scope into the current scope- In inheritance, it brings base class methods into the derived class scope
- Useful when you override some overloads but want to keep others accessible
- Without
using Base::print, if you overrideprint()(no parameters), theprint(int)overload becomes hidden
The protected Access Specifier
Section titled “The protected Access Specifier”class Base {protected: // Accessible in this class AND derived classes int _hitPoints;public: // Accessible everywhere void attack();};protectedis betweenprivateandpublic- Private: Only the class itself can access
- Protected: The class AND its derived classes can access
- Public: Everyone can access
- Use
protectedfor members that derived classes need to use but outsiders shouldn’t access
Constructor Chaining with Initialization List
Section titled “Constructor Chaining with Initialization List”ScavTrap(std::string name) : ClapTrap(name) { // Base constructor called first, then this runs}- The initialization list (
:before the{) chains to the base constructor ClapTrap(name)calls the base class constructor with the name parameter- Base class constructor runs BEFORE derived class constructor body
- You MUST call the base constructor if the base has no default constructor
Why base runs first? A derived object contains the base object “inside” it. The base part must be fully constructed before the derived part can use it. Think of building a house: you need the foundation (base) before you can add the upper floors (derived).
Calling Base Copy Constructor
Section titled “Calling Base Copy Constructor”ScavTrap(const ScavTrap& other) : ClapTrap(other) { // other is a ScavTrap, which IS-A ClapTrap}ClapTrap(other)calls the base copy constructor- Even though
otheris aScavTrap, it can be passed asClapTrap&because inheritance IS-A relationship - This copies the base class members; you copy derived class members in the body
1. Basic Inheritance
Section titled “1. Basic Inheritance”Syntax
Section titled “Syntax”class Base {protected: std::string _name; int _hitPoints;
public: Base(std::string name); void attack(const std::string& target);};
class Derived : public ClapTrap { // Derived inherits from ClapTrappublic: Derived(std::string name); void specialAbility();};The “Is-A” Relationship
Section titled “The “Is-A” Relationship”┌─────────────────────────────────────────────────────────────────┐│ Inheritance = "IS-A" Relationship │├─────────────────────────────────────────────────────────────────┤│ ││ Visual Model: ││ ┌───────────────┐ ││ │ ClapTrap │ ←── Base (parent) class ││ │ ───────── │ Defines common attributes ││ │ _name │ and behaviors ││ │ _hitPoints │ ││ │ attack() │ ││ └───────┬───────┘ ││ │ ││ │ "IS-A" ││ │ ││ ┌────┴────┐ ││ ▼ ▼ ││ ┌────────┐ ┌────────┐ ││ │ScavTrap│ │FragTrap│ ←── Derived (child) classes ││ │ IS-A │ │ IS-A │ Inherit from ClapTrap ││ │ClapTrap│ │ClapTrap│ + add specializations ││ │────────│ │────────│ ││ │guardG- │ │highF- │ ││ │ate() │ │ives() │ ││ └────────┘ └────────┘ ││ ││ Memory Layout: ││ ┌──────────────────────────────────────────────────────┐ ││ │ ClapTrap portion (inherited) │ ││ │ ┌─────────────┐ │ ││ │ │ _name │ │ ││ │ │ _hitPoints │ │ ││ │ │ _energy │ ←── Base class data │ ││ │ └─────────────┘ │ ││ ├──────────────────────────────────────────────────────┤ ││ │ ScavTrap portion (added) │ ││ │ ┌─────────────┐ │ ││ │ │ _gateMode │ ←── Derived class data │ ││ │ └─────────────┘ │ ││ └──────────────────────────────────────────────────────┘ ││ ││ Read it as: "ScavTrap IS-A ClapTrap" ││ - A ScavTrap can do everything a ClapTrap can do ││ - Plus ScavTrap adds its own special abilities ││ - Use inheritance for "is-a" relationships ││ - Use composition for "has-a" relationships ││ │└─────────────────────────────────────────────────────────────────┘What Gets Inherited?
Section titled “What Gets Inherited?”| Member Type | Inherited? |
|---|---|
| Public members | Yes (as public) |
| Protected members | Yes (as protected) |
| Private members | No (exist but inaccessible) |
| Constructors | No (but can be called) |
| Destructors | No (but automatically called) |
2. Access Specifiers in Inheritance
Section titled “2. Access Specifiers in Inheritance”Public Inheritance (Most Common)
Section titled “Public Inheritance (Most Common)”class Derived : public Base { // Base public -> Derived public // Base protected -> Derived protected // Base private -> inaccessible};Protected Inheritance
Section titled “Protected Inheritance”class Derived : protected Base { // Base public -> Derived protected // Base protected -> Derived protected // Base private -> inaccessible};Private Inheritance
Section titled “Private Inheritance”class Derived : private Base { // Base public -> Derived private // Base protected -> Derived private // Base private -> inaccessible};Visual Summary
Section titled “Visual Summary” In Derived Class Outside DerivedPublic Inheritance: Base public public accessible Base protected protected not accessible Base private - -
Protected Inheritance: Base public protected not accessible Base protected protected not accessible Base private - -
Private Inheritance: Base public private not accessible Base protected private not accessible Base private - -3. Constructor/Destructor Chaining
Section titled “3. Constructor/Destructor Chaining”Construction Order
Section titled “Construction Order”- Base class constructor runs FIRST
- Derived class constructor runs SECOND
class ClapTrap {public: ClapTrap(std::string name) { std::cout << "ClapTrap constructor" << std::endl; }};
class ScavTrap : public ClapTrap {public: ScavTrap(std::string name) : ClapTrap(name) { std::cout << "ScavTrap constructor" << std::endl; }};
// Creating ScavTrap prints:// ClapTrap constructor// ScavTrap constructorConstructor Chaining Flowchart
Section titled “Constructor Chaining Flowchart”┌─────────────────────────────────────────────────────────────────┐│ Constructor Execution Order in Inheritance │├─────────────────────────────────────────────────────────────────┤│ ││ Construction Order (Top-Down): ││ ┌────────────────────────────────────────────────────────┐ ││ │ │ ││ │ ┌─────────────┐ │ ││ │ │ 1. BASE │ ◄── ClapTrap constructor runs FIRST │ ││ │ │ CONSTRUCTOR │ Initialize inherited members │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ 2. MEMBER │ ◄── Member variables constructors │ ││ │ │ CONSTRUCTORS│ (if any members are objects) │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ 3. DERIVED │ ◄── ScavTrap constructor body runs │ ││ │ │ CONSTRUCTOR │ Initialize own members │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ [OBJECT READY] │ ││ │ │ ││ └────────────────────────────────────────────────────────┘ ││ ││ Destruction Order (Bottom-Up - REVERSE): ││ ┌────────────────────────────────────────────────────────┐ ││ │ │ ││ │ ┌─────────────┐ │ ││ │ │ 1. DERIVED │ ◄── ScavTrap destructor runs FIRST │ ││ │ │ DESTRUCTOR │ Clean up own resources │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ 2. MEMBER │ ◄── Member destructors │ ││ │ │ DESTRUCTORS │ │ ││ │ └──────┬──────┘ │ ││ │ │ │ ││ │ ▼ │ ││ │ ┌─────────────┐ │ ││ │ │ 3. BASE │ ◄── ClapTrap destructor runs LAST │ ││ │ │ DESTRUCTOR │ Clean up inherited resources │ ││ │ └─────────────┘ │ ││ │ │ ││ └────────────────────────────────────────────────────────┘ ││ ││ Remember: Construction is TOP-DOWN (base → derived) ││ Destruction is BOTTOM-UP (derived → base) ││ │└─────────────────────────────────────────────────────────────────┘Destruction Order (REVERSE)
Section titled “Destruction Order (REVERSE)”- Derived class destructor runs FIRST
- Base class destructor runs SECOND
class ClapTrap {public: ~ClapTrap() { std::cout << "ClapTrap destructor" << std::endl; }};
class ScavTrap : public ClapTrap {public: ~ScavTrap() { std::cout << "ScavTrap destructor" << std::endl; }};
// Destroying ScavTrap prints:// ScavTrap destructor// ClapTrap destructorCalling Base Constructor
Section titled “Calling Base Constructor”class ScavTrap : public ClapTrap {public: // MUST call base constructor in initialization list ScavTrap(std::string name) : ClapTrap(name) { // Base is already constructed here _hitPoints = 100; // Override base values _energyPoints = 50; _attackDamage = 20; }};Copy Constructor in Derived Classes
Section titled “Copy Constructor in Derived Classes”The copy constructor must explicitly call the base class copy constructor:
class ScavTrap : public ClapTrap {public: // Copy constructor - call base copy constructor ScavTrap(const ScavTrap& other) : ClapTrap(other) { // other is a ScavTrap, which IS-A ClapTrap // Base class copy constructor handles inherited members std::cout << "ScavTrap copy constructor" << std::endl; }};Assignment Operator in Derived Classes
Section titled “Assignment Operator in Derived Classes”The assignment operator should call the base class assignment operator:
class ScavTrap : public ClapTrap {public: ScavTrap& operator=(const ScavTrap& other) { if (this != &other) { // Call base class assignment operator ClapTrap::operator=(other);
// Assign derived class members (if any) // _derivedMember = other._derivedMember; } return *this; }};Complete Orthodox Canonical Form for Derived Class
Section titled “Complete Orthodox Canonical Form for Derived Class”class ScavTrap : public ClapTrap {public: // Default constructor ScavTrap() : ClapTrap() { _hitPoints = 100; _energyPoints = 50; _attackDamage = 20; }
// Parameterized constructor ScavTrap(std::string name) : ClapTrap(name) { _hitPoints = 100; _energyPoints = 50; _attackDamage = 20; }
// Copy constructor ScavTrap(const ScavTrap& other) : ClapTrap(other) { // Derived members copied here if any }
// Assignment operator ScavTrap& operator=(const ScavTrap& other) { if (this != &other) { ClapTrap::operator=(other); } return *this; }
// Destructor ~ScavTrap() { // Derived cleanup (base destructor called automatically) }};4. Overriding Functions
Section titled “4. Overriding Functions”Basic Override
Section titled “Basic Override”class ClapTrap {public: void attack(const std::string& target) { std::cout << "ClapTrap " << _name << " attacks " << target << std::endl; }};
class ScavTrap : public ClapTrap {public: void attack(const std::string& target) { std::cout << "ScavTrap " << _name << " attacks " << target << std::endl; }};
ClapTrap clap("Clappy");ScavTrap scav("Scavvy");
clap.attack("enemy"); // "ClapTrap Clappy attacks enemy"scav.attack("enemy"); // "ScavTrap Scavvy attacks enemy"Calling Base Class Function
Section titled “Calling Base Class Function”class ScavTrap : public ClapTrap {public: void attack(const std::string& target) { // Call base class version ClapTrap::attack(target);
// Add extra behavior std::cout << "...with extra ScavTrap power!" << std::endl; }};Name Shadowing
Section titled “Name Shadowing”When a derived class declares a member with the same name as a base class member, it shadows (hides) the base class member:
class Base {public: void print() { std::cout << "Base" << std::endl; } void print(int x) { std::cout << "Base: " << x << std::endl; }};
class Derived : public Base {public: void print() { std::cout << "Derived" << std::endl; } // print(int) is now HIDDEN!};
Derived d;d.print(); // OK: "Derived"d.print(42); // ERROR: print(int) is shadowed!d.Base::print(42); // OK: explicit scope resolutionThe using Declaration
Section titled “The using Declaration”Bring base class members into derived class scope:
class DiamondTrap : public ScavTrap, public FragTrap {public: // Bring ScavTrap's attack into DiamondTrap's scope using ScavTrap::attack;
// Now DiamondTrap::attack() calls ScavTrap::attack()};
// Also useful to un-hide overloaded functions:class Derived : public Base {public: using Base::print; // Bring ALL Base::print overloads void print() { std::cout << "Derived" << std::endl; }};
Derived d;d.print(); // OK: "Derived"d.print(42); // OK: Base::print(int) is now visible5. Protected Members
Section titled “5. Protected Members”Why Protected?
Section titled “Why Protected?”class ClapTrap {private: std::string _name; // Only ClapTrap can access
protected: int _hitPoints; // ClapTrap AND derived classes can access
public: void display(); // Everyone can access};
class ScavTrap : public ClapTrap {public: void specialAttack() { // _name = "X"; // ERROR: private in ClapTrap _hitPoints -= 10; // OK: protected is accessible }};Access Summary
Section titled “Access Summary”class Base {private: int _private; // Only Base methodsprotected: int _protected; // Base + Derived methodspublic: int _public; // Everyone};6. ClapTrap Exercise Structure
Section titled “6. ClapTrap Exercise Structure”ex00: ClapTrap (Base Class)
Section titled “ex00: ClapTrap (Base Class)”class ClapTrap {protected: std::string _name; int _hitPoints; // 10 int _energyPoints; // 10 int _attackDamage; // 0
public: ClapTrap(std::string name); ClapTrap(const ClapTrap& other); ClapTrap& operator=(const ClapTrap& other); ~ClapTrap();
void attack(const std::string& target); void takeDamage(unsigned int amount); void beRepaired(unsigned int amount);};ex01: ScavTrap (Inherits ClapTrap)
Section titled “ex01: ScavTrap (Inherits ClapTrap)”class ScavTrap : public ClapTrap {public: ScavTrap(std::string name); ScavTrap(const ScavTrap& other); ScavTrap& operator=(const ScavTrap& other); ~ScavTrap();
void attack(const std::string& target); // Override void guardGate(); // New ability};
// Constructor must initialize base with different values:// _hitPoints = 100, _energyPoints = 50, _attackDamage = 20ex02: FragTrap (Inherits ClapTrap)
Section titled “ex02: FragTrap (Inherits ClapTrap)”class FragTrap : public ClapTrap {public: FragTrap(std::string name); FragTrap(const FragTrap& other); FragTrap& operator=(const FragTrap& other); ~FragTrap();
void highFivesGuys(); // New ability};
// _hitPoints = 100, _energyPoints = 100, _attackDamage = 307. Multiple Inheritance and the Diamond Problem (ex03)
Section titled “7. Multiple Inheritance and the Diamond Problem (ex03)”The Diamond Problem
Section titled “The Diamond Problem” ClapTrap / \ ScavTrap FragTrap \ / DiamondTrapWithout virtual inheritance:
- DiamondTrap has TWO copies of ClapTrap
- Ambiguity: which ClapTrap’s _name?
Virtual Inheritance Solution
Section titled “Virtual Inheritance Solution”class ClapTrap { // ...};
class ScavTrap : virtual public ClapTrap { // ^^^^^^^ KEY WORD};
class FragTrap : virtual public ClapTrap { // ^^^^^^^ KEY WORD};
class DiamondTrap : public ScavTrap, public FragTrap { // Now only ONE ClapTrap subobject};DiamondTrap Implementation
Section titled “DiamondTrap Implementation”class DiamondTrap : public ScavTrap, public FragTrap {private: std::string _name; // Same variable name as ClapTrap::_name
public: DiamondTrap(std::string name); ~DiamondTrap();
// Uses ScavTrap's attack using ScavTrap::attack;
void whoAmI();};
DiamondTrap::DiamondTrap(std::string name) : ClapTrap(name + "_clap_name"), // Initialize virtual base ScavTrap(name), FragTrap(name), _name(name){ // Attributes from FragTrap except energy from ScavTrap _hitPoints = FragTrap::_hitPoints; // or just 100 _energyPoints = ScavTrap::_energyPoints; // or just 50 _attackDamage = FragTrap::_attackDamage; // or just 30}
void DiamondTrap::whoAmI() { std::cout << "I am " << _name << std::endl; std::cout << "My ClapTrap name is " << ClapTrap::_name << std::endl;}Virtual Base Class Construction
Section titled “Virtual Base Class Construction”With virtual inheritance, the MOST DERIVED class must initialize the virtual base:
DiamondTrap::DiamondTrap(std::string name) : ClapTrap(name + "_clap_name"), // DiamondTrap initializes ClapTrap ScavTrap(name), // ScavTrap's ClapTrap init is ignored FragTrap(name), // FragTrap's ClapTrap init is ignored _name(name){}8. Best Practices
Section titled “8. Best Practices”When to Use Inheritance
Section titled “When to Use Inheritance”- “Is-a” relationship: ScavTrap IS A ClapTrap
- Code reuse: Derived classes share base class code
- Polymorphism: Treat derived as base (Module 04)
When NOT to Use Inheritance
Section titled “When NOT to Use Inheritance”- “Has-a” relationship: Use composition instead
- Just for code reuse with unrelated classes
- When relationship doesn’t make semantic sense
Common Mistakes
Section titled “Common Mistakes”- Forgetting to call base constructor
- Wrong destruction order expectations
- Accessing private (not protected) base members
- Not using virtual inheritance for diamond
Exercise 00: ClapTrap
Section titled “Exercise 00: ClapTrap”Subject Analysis
Section titled “Subject Analysis”Create a base class ClapTrap - a little robot that can:
- attack: Deals damage, costs 1 energy point
- takeDamage: Reduces hit points
- beRepaired: Restores hit points, costs 1 energy point
Starting attributes:
| Attribute | Value |
|---|---|
| Name | (constructor parameter) |
| Hit points | 10 |
| Energy points | 10 |
| Attack damage | 0 |
Approach Strategy
Section titled “Approach Strategy”Step 1 - Plan for inheritance
Even though this exercise doesn’t require inheritance, the next ones do. Use protected for attributes so derived classes can access them.
Step 2 - Implement resource checking
Actions require energy and HP. If either is zero, the action fails.
Step 3 - Follow OCF
Include all four Orthodox Canonical Form members with constructor/destructor messages.
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Class declaration:
#ifndef CLAPTRAP_HPP#define CLAPTRAP_HPP
#include <string>
class ClapTrap {protected: // Protected, not private - derived classes need access! std::string _name; int _hitPoints; int _energyPoints; int _attackDamage;
public: ClapTrap(std::string name); ClapTrap(const ClapTrap& other); ClapTrap& operator=(const ClapTrap& other); ~ClapTrap();
void attack(const std::string& target); void takeDamage(unsigned int amount); void beRepaired(unsigned int amount);};
#endifStage 2 - Constructor with messages:
#include "ClapTrap.hpp"#include <iostream>
ClapTrap::ClapTrap(std::string name) : _name(name), _hitPoints(10), _energyPoints(10), _attackDamage(0){ std::cout << "ClapTrap " << _name << " constructed" << std::endl;}
ClapTrap::~ClapTrap() { std::cout << "ClapTrap " << _name << " destructed" << std::endl;}Stage 3 - Actions with resource checking:
void ClapTrap::attack(const std::string& target) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ClapTrap " << _name << " can't attack (no energy/HP)!" << std::endl; return; } _energyPoints--; std::cout << "ClapTrap " << _name << " attacks " << target << ", causing " << _attackDamage << " points of damage!" << std::endl;}
void ClapTrap::takeDamage(unsigned int amount) { _hitPoints -= amount; if (_hitPoints < 0) _hitPoints = 0; std::cout << "ClapTrap " << _name << " takes " << amount << " damage! HP: " << _hitPoints << std::endl;}
void ClapTrap::beRepaired(unsigned int amount) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ClapTrap " << _name << " can't repair (no energy/HP)!" << std::endl; return; } _energyPoints--; _hitPoints += amount; std::cout << "ClapTrap " << _name << " repairs " << amount << " HP! HP: " << _hitPoints << std::endl;}Line-by-Line Explanation
Section titled “Line-by-Line Explanation”| Line | Code | Why |
|---|---|---|
protected: | Access specifier | Derived classes can access, others can’t |
if (_energyPoints <= 0) | Resource check | Can’t act without energy |
_energyPoints-- | Consume resource | Actions cost energy |
unsigned int amount | Unsigned parameter | Damage can’t be negative |
Common Pitfalls
Section titled “Common Pitfalls”1. Using private instead of protected
// WRONG - derived classes can't access!private: std::string _name;
// RIGHT - derived classes can accessprotected: std::string _name;2. Forgetting resource checks
// WRONG - can attack with no energy!void ClapTrap::attack(const std::string& target) { _energyPoints--; // Goes negative! // ...}
// RIGHT - check firstvoid ClapTrap::attack(const std::string& target) { if (_energyPoints <= 0 || _hitPoints <= 0) { // Handle failure return; } _energyPoints--; // ...}Testing Tips
Section titled “Testing Tips”int main() { ClapTrap a("Bob");
// Test basic actions a.attack("Enemy"); a.takeDamage(5); a.beRepaired(3);
// Test resource depletion for (int i = 0; i < 12; i++) // Should fail after 10 a.attack("Enemy");
return 0;}Final Code
Section titled “Final Code”#include "ClapTrap.hpp"#include <iostream>
ClapTrap::ClapTrap(std::string name) : _name(name), _hitPoints(10), _energyPoints(10), _attackDamage(0){ std::cout << "ClapTrap " << _name << " constructed" << std::endl;}
ClapTrap::ClapTrap(const ClapTrap& other) : _name(other._name), _hitPoints(other._hitPoints), _energyPoints(other._energyPoints), _attackDamage(other._attackDamage){ std::cout << "ClapTrap " << _name << " copy constructed" << std::endl;}
ClapTrap& ClapTrap::operator=(const ClapTrap& other) { std::cout << "ClapTrap assignment operator called" << std::endl; if (this != &other) { _name = other._name; _hitPoints = other._hitPoints; _energyPoints = other._energyPoints; _attackDamage = other._attackDamage; } return *this;}
ClapTrap::~ClapTrap() { std::cout << "ClapTrap " << _name << " destructed" << std::endl;}
void ClapTrap::attack(const std::string& target) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ClapTrap " << _name << " can't attack!" << std::endl; return; } _energyPoints--; std::cout << "ClapTrap " << _name << " attacks " << target << ", causing " << _attackDamage << " damage!" << std::endl;}
void ClapTrap::takeDamage(unsigned int amount) { _hitPoints -= amount; if (_hitPoints < 0) _hitPoints = 0; std::cout << "ClapTrap " << _name << " takes " << amount << " damage!" << std::endl;}
void ClapTrap::beRepaired(unsigned int amount) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ClapTrap " << _name << " can't repair!" << std::endl; return; } _energyPoints--; _hitPoints += amount; std::cout << "ClapTrap " << _name << " repairs " << amount << " HP!" << std::endl;}Exercise 01: ScavTrap
Section titled “Exercise 01: ScavTrap”Subject Analysis
Section titled “Subject Analysis”Create ScavTrap that inherits from ClapTrap with:
-
Different attribute values:
Attribute Value Hit points 100 Energy points 50 Attack damage 20 -
Custom attack message
-
New ability:
guardGate()
Approach Strategy
Section titled “Approach Strategy”Step 1 - Inherit from ClapTrap
Use : public ClapTrap to establish inheritance.
Step 2 - Constructor chaining
Call the base constructor first, THEN override the values.
Step 3 - Understand construction order
When creating a ScavTrap:
- ClapTrap constructor runs first
- Then ScavTrap constructor runs
Destruction is reverse:
- ScavTrap destructor runs first
- Then ClapTrap destructor runs
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Class declaration:
#ifndef SCAVTRAP_HPP#define SCAVTRAP_HPP
#include "ClapTrap.hpp"
class ScavTrap : public ClapTrap { // Inherits from ClapTrappublic: ScavTrap(std::string name); ScavTrap(const ScavTrap& other); ScavTrap& operator=(const ScavTrap& other); ~ScavTrap();
void attack(const std::string& target); // Override ClapTrap's attack void guardGate(); // New ability};
#endifStage 2 - Constructor with base initialization:
#include "ScavTrap.hpp"#include <iostream>
ScavTrap::ScavTrap(std::string name) : ClapTrap(name) { // ClapTrap constructor already ran with default values // Now we override with ScavTrap values _hitPoints = 100; _energyPoints = 50; _attackDamage = 20; std::cout << "ScavTrap " << _name << " constructed" << std::endl;}
ScavTrap::~ScavTrap() { std::cout << "ScavTrap " << _name << " destructed" << std::endl;}Stage 3 - Override attack and add new ability:
void ScavTrap::attack(const std::string& target) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ScavTrap " << _name << " can't attack!" << std::endl; return; } _energyPoints--; std::cout << "ScavTrap " << _name << " attacks " << target << ", causing " << _attackDamage << " damage!" << std::endl;}
void ScavTrap::guardGate() { std::cout << "ScavTrap " << _name << " is now in Gate keeper mode" << std::endl;}Line-by-Line Explanation
Section titled “Line-by-Line Explanation”| Line | Code | Why |
|---|---|---|
: public ClapTrap | Public inheritance | ScavTrap IS-A ClapTrap |
: ClapTrap(name) | Call base constructor | Initialize ClapTrap part first |
_hitPoints = 100 | Override value | ScavTrap has different stats |
void attack(...) | Override function | ScavTrap has custom attack message |
Common Pitfalls
Section titled “Common Pitfalls”1. Not calling base constructor
// WRONG - ClapTrap not properly initialized!ScavTrap::ScavTrap(std::string name) { _name = name; _hitPoints = 100; // ...}
// RIGHT - call base constructor firstScavTrap::ScavTrap(std::string name) : ClapTrap(name) { _hitPoints = 100; // ...}2. Setting values in initializer list (won’t override)
// WRONG - base constructor already set these!ScavTrap::ScavTrap(std::string name) : ClapTrap(name), _hitPoints(100) { // Error: can't init in derived}
// RIGHT - override in constructor bodyScavTrap::ScavTrap(std::string name) : ClapTrap(name) { _hitPoints = 100; // Override after base construction}Testing Tips
Section titled “Testing Tips”int main() { std::cout << "=== Creating ScavTrap ===" << std::endl; ScavTrap s("Scav");
s.attack("Target"); // Should show ScavTrap message s.guardGate(); // ScavTrap-only ability
std::cout << "=== End of main ===" << std::endl; return 0;}// Expected output:// ClapTrap Scav constructed// ScavTrap Scav constructed// ScavTrap Scav attacks Target, causing 20 damage!// ScavTrap Scav is now in Gate keeper mode// ScavTrap Scav destructed// ClapTrap Scav destructedFinal Code
Section titled “Final Code”#include "ScavTrap.hpp"#include <iostream>
ScavTrap::ScavTrap(std::string name) : ClapTrap(name) { _hitPoints = 100; _energyPoints = 50; _attackDamage = 20; std::cout << "ScavTrap " << _name << " constructed" << std::endl;}
ScavTrap::ScavTrap(const ScavTrap& other) : ClapTrap(other) { std::cout << "ScavTrap " << _name << " copy constructed" << std::endl;}
ScavTrap& ScavTrap::operator=(const ScavTrap& other) { ClapTrap::operator=(other); return *this;}
ScavTrap::~ScavTrap() { std::cout << "ScavTrap " << _name << " destructed" << std::endl;}
void ScavTrap::attack(const std::string& target) { if (_energyPoints <= 0 || _hitPoints <= 0) { std::cout << "ScavTrap " << _name << " can't attack!" << std::endl; return; } _energyPoints--; std::cout << "ScavTrap " << _name << " attacks " << target << ", causing " << _attackDamage << " damage!" << std::endl;}
void ScavTrap::guardGate() { std::cout << "ScavTrap " << _name << " is now in Gate keeper mode" << std::endl;}Exercise 02: FragTrap
Section titled “Exercise 02: FragTrap”Subject Analysis
Section titled “Subject Analysis”Create FragTrap that inherits from ClapTrap with:
| Attribute | Value |
|---|---|
| Hit points | 100 |
| Energy points | 100 |
| Attack damage | 30 |
New ability: highFivesGuys() - requests a high five.
Approach Strategy
Section titled “Approach Strategy”Same pattern as ScavTrap - this exercise reinforces inheritance concepts.
Progressive Code Building
Section titled “Progressive Code Building”#ifndef FRAGTRAP_HPP#define FRAGTRAP_HPP
#include "ClapTrap.hpp"
class FragTrap : public ClapTrap {public: FragTrap(std::string name); FragTrap(const FragTrap& other); FragTrap& operator=(const FragTrap& other); ~FragTrap();
void highFivesGuys();};
#endif#include "FragTrap.hpp"#include <iostream>
FragTrap::FragTrap(std::string name) : ClapTrap(name) { _hitPoints = 100; _energyPoints = 100; _attackDamage = 30; std::cout << "FragTrap " << _name << " constructed" << std::endl;}
FragTrap::FragTrap(const FragTrap& other) : ClapTrap(other) { std::cout << "FragTrap " << _name << " copy constructed" << std::endl;}
FragTrap& FragTrap::operator=(const FragTrap& other) { ClapTrap::operator=(other); return *this;}
FragTrap::~FragTrap() { std::cout << "FragTrap " << _name << " destructed" << std::endl;}
void FragTrap::highFivesGuys() { std::cout << "FragTrap " << _name << " requests a high five!" << std::endl;}Common Pitfalls
Section titled “Common Pitfalls”Same as ScavTrap - ensure you call the base constructor!
Testing Tips
Section titled “Testing Tips”int main() { FragTrap f("Frag"); f.attack("Enemy"); // Uses ClapTrap::attack (not overridden) f.highFivesGuys(); // FragTrap-only ability return 0;}Exercise 03: DiamondTrap
Section titled “Exercise 03: DiamondTrap”Subject Analysis
Section titled “Subject Analysis”Create DiamondTrap that inherits from BOTH ScavTrap AND FragTrap:
ClapTrap / \ ScavTrap FragTrap \ / DiamondTrapThis creates the diamond problem: without special handling, DiamondTrap would have TWO copies of ClapTrap!
Requirements:
- Name: DiamondTrap’s own name, ClapTrap name = name + “_clap_name”
- HP: FragTrap’s (100)
- Energy: ScavTrap’s (50)
- Damage: FragTrap’s (30)
- attack(): ScavTrap’s
whoAmI(): Prints both names
Approach Strategy
Section titled “Approach Strategy”Step 1 - Understand the diamond problem
Without virtual:
DiamondTrap has:- ScavTrap::ClapTrap (one copy)- FragTrap::ClapTrap (another copy!)With virtual:
DiamondTrap has:- ONE shared ClapTrap (the right way)Step 2 - Add virtual to intermediate classes
Both ScavTrap and FragTrap must use virtual public ClapTrap.
Step 3 - Initialize virtual base in most-derived class
With virtual inheritance, the MOST DERIVED class (DiamondTrap) initializes the virtual base (ClapTrap).
Progressive Code Building
Section titled “Progressive Code Building”Stage 1 - Modify ScavTrap and FragTrap (add virtual):
class ScavTrap : virtual public ClapTrap { // Add virtual // ... rest unchanged};class FragTrap : virtual public ClapTrap { // Add virtual // ... rest unchanged};Stage 2 - DiamondTrap declaration:
#ifndef DIAMONDTRAP_HPP#define DIAMONDTRAP_HPP
#include "ScavTrap.hpp"#include "FragTrap.hpp"
class DiamondTrap : public ScavTrap, public FragTrap {private: std::string _name; // DiamondTrap's own name (shadows ClapTrap::_name)
public: DiamondTrap(std::string name); DiamondTrap(const DiamondTrap& other); DiamondTrap& operator=(const DiamondTrap& other); ~DiamondTrap();
using ScavTrap::attack; // Use ScavTrap's attack void whoAmI();};
#endifStage 3 - DiamondTrap implementation:
#include "DiamondTrap.hpp"#include <iostream>
DiamondTrap::DiamondTrap(std::string name) : ClapTrap(name + "_clap_name"), // Initialize virtual base FIRST ScavTrap(name), FragTrap(name), _name(name){ // Take specific attributes from each parent _hitPoints = FragTrap::_hitPoints; // 100 from FragTrap _energyPoints = ScavTrap::_energyPoints; // 50 from ScavTrap _attackDamage = FragTrap::_attackDamage; // 30 from FragTrap std::cout << "DiamondTrap " << _name << " constructed" << std::endl;}
DiamondTrap::~DiamondTrap() { std::cout << "DiamondTrap " << _name << " destructed" << std::endl;}
void DiamondTrap::whoAmI() { std::cout << "I am " << _name << std::endl; std::cout << "My ClapTrap name is " << ClapTrap::_name << std::endl;}Line-by-Line Explanation
Section titled “Line-by-Line Explanation”| Line | Code | Why |
|---|---|---|
virtual public ClapTrap | Virtual inheritance | Prevents duplicate ClapTrap |
: ClapTrap(name + "_clap_name") | Init virtual base | Most-derived class initializes virtual base |
std::string _name | Own member | Shadows ClapTrap::_name |
using ScavTrap::attack | Resolve ambiguity | Explicitly choose ScavTrap’s attack |
ClapTrap::_name | Scope resolution | Access base class member |
Common Pitfalls
Section titled “Common Pitfalls”1. Forgetting virtual on intermediate classes
// WRONG - two ClapTrap copies!class ScavTrap : public ClapTrap { };class FragTrap : public ClapTrap { };
// RIGHT - one shared ClapTrapclass ScavTrap : virtual public ClapTrap { };class FragTrap : virtual public ClapTrap { };2. Not initializing virtual base in DiamondTrap
// WRONG - ClapTrap not properly initialized!DiamondTrap::DiamondTrap(std::string name) : ScavTrap(name), FragTrap(name) { }
// RIGHT - must initialize ClapTrap explicitlyDiamondTrap::DiamondTrap(std::string name) : ClapTrap(name + "_clap_name"), ScavTrap(name), FragTrap(name) { }3. Confusing the two _name members
void DiamondTrap::whoAmI() { // _name is DiamondTrap's own member // ClapTrap::_name is the base class member std::cout << _name << std::endl; // DiamondTrap name std::cout << ClapTrap::_name << std::endl; // ClapTrap name (with suffix)}Testing Tips
Section titled “Testing Tips”int main() { std::cout << "=== Creating DiamondTrap ===" << std::endl; DiamondTrap d("Diamond");
d.whoAmI(); // Shows both names d.attack("Enemy"); // Uses ScavTrap's attack d.guardGate(); // Inherited from ScavTrap d.highFivesGuys(); // Inherited from FragTrap
std::cout << "=== End of main ===" << std::endl; return 0;}// Expected construction order:// ClapTrap Diamond_clap_name constructed// ScavTrap Diamond constructed// FragTrap Diamond constructed// DiamondTrap Diamond constructed//// Expected destruction order (reverse):// DiamondTrap Diamond destructed// FragTrap Diamond destructed// ScavTrap Diamond destructed// ClapTrap Diamond_clap_name destructedFinal Code
Section titled “Final Code”#include "DiamondTrap.hpp"#include <iostream>
DiamondTrap::DiamondTrap(std::string name) : ClapTrap(name + "_clap_name"), ScavTrap(name), FragTrap(name), _name(name){ _hitPoints = FragTrap::_hitPoints; _energyPoints = ScavTrap::_energyPoints; _attackDamage = FragTrap::_attackDamage; std::cout << "DiamondTrap " << _name << " constructed" << std::endl;}
DiamondTrap::DiamondTrap(const DiamondTrap& other) : ClapTrap(other), ScavTrap(other), FragTrap(other), _name(other._name){ std::cout << "DiamondTrap " << _name << " copy constructed" << std::endl;}
DiamondTrap& DiamondTrap::operator=(const DiamondTrap& other) { ClapTrap::operator=(other); _name = other._name; return *this;}
DiamondTrap::~DiamondTrap() { std::cout << "DiamondTrap " << _name << " destructed" << std::endl;}
void DiamondTrap::whoAmI() { std::cout << "I am " << _name << std::endl; std::cout << "My ClapTrap name is " << ClapTrap::_name << std::endl;}Quick Reference
Section titled “Quick Reference”// Basic inheritanceclass Derived : public Base { };
// Constructor chainingDerived(args) : Base(base_args), _member(val) { }
// Override functionvoid Derived::method() { Base::method(); /* extra */ }
// Virtual inheritance (diamond)class Middle : virtual public Base { };Related Concepts
Section titled “Related Concepts”Continue your C++ journey:
- Previous: Module 02: OCF & Operators - Review copy semantics
- Next: Module 04: Polymorphism - Learn how virtual functions enable runtime behavior selection
Key Terms from This Module
Section titled “Key Terms from This Module”Visit the Glossary for definitions of: