Skip to content

Ch18: Inheritance

TL;DR: Inheritance & Access Modes: Class inheritance (class Derived : public Base) embeds base objects inside derived types. Access modes (public, protected, private) filter member visibility downwards.

โšก Quick Reference

#include <iostream>
#include <memory>
#include <utility>

class Base {
protected:
    int m_id {0};
public:
    Base() = default;
    Base(int id) : m_id(id) {}
    virtual void print() const { std::cout << "Base: " << m_id << std::endl; }
};

class Derived : public Base {
public:
    using Base::Base;
    int m_id = 999;
    void print() const override {
        std::cout << "Derived: " << m_id << std::endl;
        std::cout << "Base's hidden ID: " << Base::m_id << std::endl;
    }
};

class Person {
public:
    Person() = default;
    void add() {}
};

class Engineer : private Person {
public:
    using Person::add;
};

struct Point {
    int x, y;
    Point operator+(const Point& right) const {
        return Point{x + right.x, y + right.y};
    }
    Point& operator=(const Point& right) {
        if (this != &right) {
            x = right.x;
            y = right.y;
        }
        return *this;
    }
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y;
    }
    auto operator<=>(const Point&) const = default;
};

int main() {
    std::unique_ptr<Base> up = std::make_unique<Derived>(10);
    std::unique_ptr<Base> up2 = std::move(up);
    up2.reset();

    std::shared_ptr<Base> sp = std::make_shared<Base>(2);
    std::weak_ptr<Base> wp = sp;

    return 0;
}

๐Ÿง  Core Concepts

  • Inheritance & Access Modes: Class inheritance (class Derived : public Base) embeds base objects inside derived types. Access modes (public, protected, private) filter member visibility downwards.
  • RAII & Smart Pointers: C++ manages heap lifecycles automatically using move-only std::unique_ptr for exclusive ownership, and reference-counted std::shared_ptr with std::weak_ptr to avoid circular leaks.
  • Operator Overloading: Custom types can implement operators as members or non-members. C++20 introduces the spaceship operator (<=>) to automatically generate all inequality comparisons.

โš ๏ธ Pitfalls (Quick Scan)

Mistake Fix
Accessing private base class members from a derived class Expose base members via public/protected getters, or change access to protected
Using private inheritance downstream Use public/protected inheritance or re-expose members using using declarations
Assigning directly to base class members inside derived constructor bodies Initialize base classes using the constructor initializer list: Derived() : Base(args)
Omitting the base copy constructor in a custom derived copy constructor Explicitly call the base copy constructor in the initializer list: Derived(const Derived& src) : Base(src)
Creating circular references between two objects using shared_ptr Use std::weak_ptr for one direction of the circular connection
Attempting to copy a std::unique_ptr Use std::move to transfer ownership instead of copying
Overwriting member variables in a derived class with identical names to base members (name hiding) Qualify calls using the base class scope: obj.Base::member
Implementing copy assignment without checking for self-assignment Add a safeguard check: if (this != &right_operand)
Omitting returning this from a copy assignment operator Always return *this as a reference (T&)
Defining conflicting manual comparisons alongside std::rel_ops Provide all comparison overloads manually or use the C++20 spaceship operator (<=>)
Using std::strong_ordering for spaceship operators involving floats Use std::partial_ordering when comparisons can result in unordered values like NaN
Expecting constructors to be inherited by derived classes automatically Bring base constructors into scope using using Base::Base;
Assuming using Base::Base; inherits copy and move constructors Implement custom copy and move constructors explicitly in the derived class

๐Ÿ“– Full Details

Cause โ†’ Effect โ†’ Fix with timestamp (click to expand) * **Accessing private base class members from a derived class** -> **Compile-time error because private members are never accessible downwards** -> **Expose base members via public/protected getters, or change access to `protected` (~08:30)** * **Using private inheritance downstream** -> **Base class interface is eclipsed and inaccessible to subsequent derived subclasses** -> **Use public/protected inheritance or re-expose members using `using` declarations (~28:00)** * **Assigning directly to base class members inside derived constructor bodies** -> **Compile-time error because base sub-objects must be initialized beforehand** -> **Initialize base classes using the constructor initializer list: `Derived() : Base(args)` (~03:00)** * **Omitting the base copy constructor in a custom derived copy constructor** -> **Compiler invokes the default base constructor instead, resulting in uncopied base member states (~04:00)** -> **Explicitly call the base copy constructor in the initializer list: `Derived(const Derived& src) : Base(src)` (~04:00)** * **Creating circular references between two objects using `shared_ptr`** -> **Reference counts never drop to zero, causing a permanent memory leak** -> **Use `std::weak_ptr` for one direction of the circular connection (~02:00)** * **Attempting to copy a `std::unique_ptr`** -> **Compile-time error because copy constructor is deleted to enforce exclusivity** -> **Use `std::move` to transfer ownership instead of copying (~07:00)** * **Overwriting member variables in a derived class with identical names to base members (name hiding)** -> **Calls to the member resolve to the derived class version, hiding the base version** -> **Qualify calls using the base class scope: `obj.Base::member` (~02:00)** * **Implementing copy assignment without checking for self-assignment** -> **Deallocating resources on self-assignment (e.g., `p = p`) causes null dereferences or crashes** -> **Add a safeguard check: `if (this != &right_operand)` (~02:00)** * **Omitting returning `*this` from a copy assignment operator** -> **Prevents chaining assignments like `a = b = c`** -> **Always return `*this` as a reference (`T&`) (~03:00)** * **Defining conflicting manual comparisons alongside `std::rel_ops`** -> **Causes ambiguity errors as the compiler struggles to choose between manual and auto-generated comparisons** -> **Provide all comparison overloads manually or use the C++20 spaceship operator (`<=>`) (~04:00)** * **Using `std::strong_ordering` for spaceship operators involving floats** -> **Compile-time error or runtime bugs because floating points support NaN which has no order relation** -> **Use `std::partial_ordering` when comparisons can result in unordered values like NaN (~05:00)** * **Expecting constructors to be inherited by derived classes automatically** -> **Compile-time error because base class constructors are not inherited by default** -> **Bring base constructors into scope using `using Base::Base;` (~04:00)** * **Assuming `using Base::Base;` inherits copy and move constructors** -> **Copy and move constructors are excluded, leaving derived classes with default generated copies** -> **Implement custom copy and move constructors explicitly in the derived class (~04:00)**

๐Ÿ“Ž Repo Files

  • 33.SmartPointers/33.2UniquePointers/main.cpp
  • 33.SmartPointers/33.5SharedPointers/main.cpp
  • 33.SmartPointers/33.9WeakPointers/main.cpp
  • 34.OperatorOverloading/34.2AdditionOperatorAsMember/main.cpp
  • 34.OperatorOverloading/34.7StreamInsertionOperator/main.cpp
  • 34.OperatorOverloading/34.17CopyAssignmentOperator/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.2AllLogicalOperators/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.3Rel_OpsNamespace/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.5ThreeWayComparisonOperator/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.7CustomEqualityOperator/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.8DefaultOrderingWithSpaceship/main.cpp
  • 35.LogicalOperatorsAndThreeWayComparison/35.10CustomSpaceshipOperatorForOrdering/main.cpp
  • 36.Inheritance/36.2FirstTryOnInheritance/main.cpp
  • 36.Inheritance/36.3ProtectedMembers/main.cpp
  • 36.Inheritance/36.5BaseClassAccessSpecifiersADemo/main.cpp
  • 36.Inheritance/36.6ClosingInOnPrivateInheritance/main.cpp
  • 36.Inheritance/36.7ResurectingMembersBackInContext/main.cpp
  • 36.Inheritance/36.8DefaultArgConstructorsWithInheritance/main.cpp
  • 36.Inheritance/36.9ConstructorsWithInheritance/main.cpp
  • 36.Inheritance/36.10CopyConstructorsWithInheritance/main.cpp
  • 36.Inheritance/36.11InheritingBaseConstructors/main.cpp
  • 36.Inheritance/36.12InheritanceAndDestructors/main.cpp
  • 36.Inheritance/36.13ReusedSymbolsInInheritance/main.cpp