5 hours · 7 marks
Inheritance lets a new class (derived/child class) acquire the properties (data members) and behaviour (member functions) of an existing class (base/parent class), promoting code reuse.
class Animal { // base class
public:
void eat() { cout << "Eating..."; }
};
class Dog : public Animal { // derived class
public:
void bark() { cout << "Barking..."; }
};
int main() {
Dog d;
d.eat(); // inherited from Animal
d.bark(); // Dog's own
}
protected Access Specifierprotected members are accessible within the class itself and within any class derived from it, but NOT from outside the class (unlike public) — this makes it the typical choice for members that a base class wants to hide from the outside world but still share with subclasses.
class Derived : access-specifier Base {
// additional members
};
// example:
class Manager : public Employee { ... };
| Is-a (inheritance) | Has-a (composition/aggregation) |
|---|---|
| Derived class IS a specialized type of the base class | A class HAS another class's object as a member |
Example: Dog is an Animal | Example: Car has an Engine |
Implemented via class D : public B | Implemented by declaring an object as a data member |
The access specifier used while inheriting controls how base class members appear in the derived class:
| Base member | public inheritance | protected inheritance | private inheritance |
|---|---|---|---|
| public | public | protected | private |
| protected | protected | protected | private |
| private | not accessible | not accessible | not accessible |
Private members of the base class are never directly accessible in the derived class regardless of the inheritance mode — they can only be accessed indirectly through public/protected base class methods.
When a derived class defines a member function with the same name and signature as one in the base class, the derived class version "hides"/overrides it for objects of the derived type.
class Base {
public:
void show() { cout << "Base::show"; }
};
class Derived : public Base {
public:
void show() { cout << "Derived::show"; } // overrides/hides Base::show
void callBase() { Base::show(); } // explicit call to base version
};
class C : public A, public B {};When a derived object is created, the base class constructor is executed first, then the derived class constructor body runs. Arguments are passed to the base constructor using a member initialization list.
class Base {
public:
Base(int x) { cout << "Base ctor " << x; }
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x) { // explicitly calling base ctor
cout << " Derived ctor " << y;
}
};
Derived d(1, 2); // prints: Base ctor 1 Derived ctor 2
Destructors execute in the reverse order of constructors — the derived class destructor runs first, then the base class destructor.
~Derived() { cout << "Derived dtor "; }
~Base() { cout << "Base dtor"; }
// output on object destruction: Derived dtor Base dtor
In multiple/hybrid inheritance, if two base classes both inherit from a common ancestor and a further class inherits from both, the common ancestor's members get duplicated (the diamond problem), causing ambiguity.
class Person { public: string name; };
class Student : virtual public Person { }; // virtual inheritance
class Employee : virtual public Person { };
class TeachingAssistant : public Student, public Employee { };
// with 'virtual', TeachingAssistant has only ONE copy of Person's members
Declaring the common base as a virtual base class ensures only a single shared copy of it exists in the most-derived object, eliminating ambiguity and duplication.
virtual, TeachingAssistant would contain two separate copies of Person's members (one via Student, one via Employee), causing ambiguous access like ta.name. Declaring Person as a virtual base ensures a single shared copy.Car is-a Vehicle). Has-a is implemented through composition/aggregation and represents ownership/containment (a Car has-a Engine). See table in section 5.4.class A { public: A() { cout << "A "; } };
class B : public A { public: B() { cout << "B "; } };
class C : public B { public: C() { cout << "C "; } };
int main() { C obj; } // Output: A B C
protected access specifier needed when private and public already exist? 3 marksprivate members are hidden even from derived classes, which prevents legitimate reuse of implementation details. public members are visible to everyone, breaking encapsulation. protected fills the gap: members are hidden from the outside world (like private) but still accessible to derived classes (unlike private), supporting controlled extension of a base class.