Module 03: Inheritance
Key Concepts:
- Base and derived classes
- Access specifiers with inheritance
- Constructor/destructor chaining
- Member access in inheritance
- Multiple inheritance (Diamond Problem)
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 Base { // Derived inherits from Basepublic: Derived(std::string name); void specialAbility();};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 constructorDestruction 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
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 { };