
31:36
Lecture 23: Inheritance: Part 3 (Constructor & Destructor - Object Lifetime)
NPTEL IIT Kharagpur
Overview
This lecture introduces the `protected` access specifier in C++ to maintain encapsulation within inheritance hierarchies. It explains how `protected` members are accessible to derived classes but not to the outside world, bridging the gap between `private` and `public`. The lecture then delves into the crucial concepts of object lifetime, focusing on the order of constructor and destructor calls in a class hierarchy. Understanding this order is vital for correctly managing resources and preventing errors when objects are created and destroyed.
How was this?
Save this permanently with flashcards, quizzes, and AI chat
Chapters
- Private members of a base class are inaccessible to derived classes, hindering their ability to use or modify inherited data.
- Public members of a base class, if accessed by derived classes, break encapsulation for the entire hierarchy.
- The `protected` access specifier allows derived classes to access base class members while keeping them private from external code.
- Protected members act like public within derived classes but private everywhere else, restoring information hiding in hierarchies.
The `protected` specifier is essential for building robust class hierarchies by allowing controlled access to base class data for derived classes, thus preserving encapsulation.
A base class `B` with a private `data` member. A derived class `D` cannot access `B::data` in its `print` function, causing a compilation error. Changing `B::data` to `protected` allows `D::print` to access it.
- Protected members provide a middle ground for access control in inheritance, balancing encapsulation and usability.
- Friend functions, while having access to private members, are not inherited by derived classes.
- When a friend function operates on a derived class object, it may only access the base class portion if not specifically overloaded for the derived type.
- Making a member `protected` does not automatically grant inherited access to friend functions of the base class.
Understanding the nuances of `protected` access, especially in relation to friend functions, is crucial for correctly implementing and debugging code that relies on inheritance and controlled data access.
A friend function designed to print a base class object `B` might only print the base part of a derived class object `D` if a separate overload for `D` is not provided, even if `B`'s data is `protected`.
- Derived classes do not inherit constructors or destructors; they must define their own.
- When a derived class object is created, the base class constructor is always called first, followed by the derived class constructor.
- When a derived class object is destroyed, the derived class destructor is called first, followed by the base class destructor.
- Derived class constructors must explicitly or implicitly call the appropriate base class constructor, often using an initializer list.
The specific order of constructor and destructor calls in a class hierarchy is fundamental to managing object lifetimes, ensuring resources are properly allocated and deallocated, and preventing memory leaks or undefined behavior.
In a `Derived` class inheriting from `Base`, creating a `Derived` object `d(1, 2)` first calls `Base(1)` (via initializer list) and then the `Derived` constructor. Destruction happens in reverse: `~Derived()` then `~Base()`.
- The lifetime of a derived class object includes the lifetime of its base class subobject.
- Constructors are called in the order of inheritance: base class first, then derived class.
- Destructors are called in the reverse order of construction: derived class first, then base class.
- If a base class has no default constructor and a derived class doesn't explicitly call a base class constructor, compilation will fail.
Visualizing the step-by-step construction and destruction process clarifies how objects are built and dismantled in memory, which is critical for understanding program flow and debugging complex object interactions.
When constructing `d1(1, 2)` (derived from `B`), the output trace shows `Base constructor with int value 0` (default for `B` if not specified) or `Base constructor with int value 1` (if specified), followed by `Derived constructor with 1, 2`. Destruction reverses this order.
Key takeaways
- The `protected` access specifier is crucial for maintaining encapsulation in inheritance by allowing derived classes controlled access to base class members.
- Derived classes do not inherit constructors or destructors but must ensure they are called in the correct order.
- Object construction in an inheritance hierarchy proceeds from the base class to the derived class.
- Object destruction in an inheritance hierarchy proceeds from the derived class to the base class.
- Failure to correctly call base class constructors from derived class constructors can lead to compilation errors.
- Understanding the sequence of constructor and destructor calls is fundamental to managing object lifetime and preventing resource management issues.
Key terms
InheritanceISA relationshipEncapsulationProtected access specifierBase classDerived classConstructorDestructorObject lifetimeClass hierarchy
Test your understanding
- How does the `protected` access specifier differ from `private` and `public` in the context of inheritance?
- Why are constructors and destructors not inherited by derived classes, and what mechanism allows them to be called?
- Describe the exact sequence of constructor calls when creating an object of a derived class that inherits from a base class.
- Explain the order in which destructors are called for objects in an inheritance hierarchy and why this order is important.
- What happens if a derived class constructor does not explicitly call a base class constructor, and how does this relate to the base class's available constructors?