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_ptrfor exclusive ownership, and reference-countedstd::shared_ptrwithstd::weak_ptrto 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.cpp33.SmartPointers/33.5SharedPointers/main.cpp33.SmartPointers/33.9WeakPointers/main.cpp34.OperatorOverloading/34.2AdditionOperatorAsMember/main.cpp34.OperatorOverloading/34.7StreamInsertionOperator/main.cpp34.OperatorOverloading/34.17CopyAssignmentOperator/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.2AllLogicalOperators/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.3Rel_OpsNamespace/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.5ThreeWayComparisonOperator/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.7CustomEqualityOperator/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.8DefaultOrderingWithSpaceship/main.cpp35.LogicalOperatorsAndThreeWayComparison/35.10CustomSpaceshipOperatorForOrdering/main.cpp36.Inheritance/36.2FirstTryOnInheritance/main.cpp36.Inheritance/36.3ProtectedMembers/main.cpp36.Inheritance/36.5BaseClassAccessSpecifiersADemo/main.cpp36.Inheritance/36.6ClosingInOnPrivateInheritance/main.cpp36.Inheritance/36.7ResurectingMembersBackInContext/main.cpp36.Inheritance/36.8DefaultArgConstructorsWithInheritance/main.cpp36.Inheritance/36.9ConstructorsWithInheritance/main.cpp36.Inheritance/36.10CopyConstructorsWithInheritance/main.cpp36.Inheritance/36.11InheritingBaseConstructors/main.cpp36.Inheritance/36.12InheritanceAndDestructors/main.cpp36.Inheritance/36.13ReusedSymbolsInInheritance/main.cpp