Skip to content

🧬 Inheritance — Kế thừa

Inheritance cho phép tạo class mới dựa trên class đã có, tái sử dụng code và thiết lập quan hệ "is-a" giữa các đối tượng.

Tại sao cần Inheritance?

Vấn đề: Code Duplication

cpp
class Dog {
    std::string name_;
    int age_;
public:
    void eat() { std::cout << name_ << " is eating\n"; }
    void sleep() { std::cout << name_ << " is sleeping\n"; }
    void bark() { std::cout << "Woof!\n"; }
};

class Cat {
    std::string name_;
    int age_;  // Duplicate!
public:
    void eat() { /* Same code! */ }
    void sleep() { /* Same code! */ }
    void meow() { std::cout << "Meow!\n"; }
};

Giải pháp: Inheritance

cpp
// Base class (Parent)
class Animal {
protected:
    std::string name_;
    int age_;
public:
    Animal(const std::string& name, int age)
        : name_(name), age_(age) {}
    
    void eat() { std::cout << name_ << " is eating\n"; }
    void sleep() { std::cout << name_ << " is sleeping\n"; }
};

// Derived class (Child)
class Dog : public Animal {
public:
    Dog(const std::string& name, int age)
        : Animal(name, age) {}  // Call base constructor
    
    void bark() { std::cout << name_ << " says: Woof!\n"; }
};

class Cat : public Animal {
public:
    Cat(const std::string& name, int age)
        : Animal(name, age) {}
    
    void meow() { std::cout << name_ << " says: Meow!\n"; }
};

Cú pháp Inheritance

cpp
class Derived : access_specifier Base {
    // Derived class members
};

Access Specifiers cho Inheritance

Base Memberpublic inheritanceprotected inheritanceprivate inheritance
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private❌ không access❌ không access❌ không access

Phổ biến nhất: public inheritance (is-a relationship)

cpp
class Dog : public Animal { };     // ✅ Phổ biến nhất
class Cat : protected Animal { };  // 🔸 Hiếm dùng
class Bird : private Animal { };   // 🔸 Implementation inheritance

Constructor trong Inheritance

Gọi Base Constructor

cpp
#include <iostream>
#include <string>

class Vehicle {
protected:
    std::string brand_;
    int year_;
    
public:
    Vehicle(const std::string& brand, int year)
        : brand_(brand), year_(year) {
        std::cout << "Vehicle constructor called\n";
    }
    
    void showInfo() const {
        std::cout << year_ << " " << brand_ << std::endl;
    }
};

class Car : public Vehicle {
private:
    int numDoors_;
    
public:
    // Gọi base constructor trong initializer list
    Car(const std::string& brand, int year, int doors)
        : Vehicle(brand, year),  // ✅ Base constructor FIRST
          numDoors_(doors) {
        std::cout << "Car constructor called\n";
    }
    
    void showDetails() const {
        showInfo();
        std::cout << "Doors: " << numDoors_ << std::endl;
    }
};

int main() {
    Car myCar("Toyota", 2024, 4);
    myCar.showDetails();
    return 0;
}

Output:

Vehicle constructor called
Car constructor called
2024 Toyota
Doors: 4

⚠️ Constructor Order

  1. Base class constructor chạy trước
  2. Derived class constructor chạy sau
  3. Destructor chạy ngược lại: Derived trước, Base sau

Destructor trong Inheritance

cpp
#include <iostream>

class Base {
public:
    Base() { std::cout << "Base constructed\n"; }
    ~Base() { std::cout << "Base destroyed\n"; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived constructed\n"; }
    ~Derived() { std::cout << "Derived destroyed\n"; }
};

int main() {
    Derived d;
    return 0;
}

Output:

Base constructed
Derived constructed
Derived destroyed
Base destroyed

protected vs private Members

cpp
class Base {
private:
    int privateVar_ = 1;     // ❌ Derived cannot access
    
protected:
    int protectedVar_ = 2;   // ✅ Derived can access
    
public:
    int publicVar_ = 3;      // ✅ Everyone can access
};

class Derived : public Base {
public:
    void showVars() {
        // std::cout << privateVar_;    // ❌ Error!
        std::cout << protectedVar_;     // ✅ OK
        std::cout << publicVar_;        // ✅ OK
    }
};

Method Overriding (Hiding)

cpp
#include <iostream>

class Animal {
public:
    void speak() {
        std::cout << "Some generic sound\n";
    }
};

class Dog : public Animal {
public:
    void speak() {  // Hides base class method
        std::cout << "Woof!\n";
    }
};

int main() {
    Dog d;
    d.speak();           // "Woof!"
    
    Animal a;
    a.speak();           // "Some generic sound"
    
    // ⚠️ Nhưng nếu dùng pointer...
    Animal* ptr = &d;
    ptr->speak();        // "Some generic sound" — NOT Woof!
                         // Cần virtual để fix!
}

📌 Đây chỉ là Method Hiding

Để có true polymorphism, ta cần virtual functions → Xem bài tiếp theo!


Multiple Inheritance

cpp
class Flyable {
public:
    void fly() { std::cout << "Flying...\n"; }
};

class Swimmable {
public:
    void swim() { std::cout << "Swimming...\n"; }
};

// Duck kế thừa từ cả hai
class Duck : public Flyable, public Swimmable {
public:
    void quack() { std::cout << "Quack!\n"; }
};

int main() {
    Duck d;
    d.fly();    // ✅ Từ Flyable
    d.swim();   // ✅ Từ Swimmable
    d.quack();  // ✅ Của Duck
}

⚠️ Diamond Problem

Multiple inheritance có thể gây Diamond Problem. Giải pháp: virtual inheritance (advanced topic).


📚 Tổng kết

ConceptKey Takeaway
InheritanceClass mới dựa trên class cũ
public inheritanceis-a relationship (phổ biến nhất)
protected membersDerived classes có thể access
Constructor orderBase → Derived
Destructor orderDerived → Base
Method hidingOverride không có virtual

➡️ Tiếp theo

Tiếp theo: Virtual Functions — True polymorphism với virtual và override.

🧠 Quiz

Câu 1: Thứ tự gọi constructor và destructor khi tạo object của derived class là gì?

  • [ ] A) Derived constructor → Base constructor → Derived destructor → Base destructor
  • [x] B) Base constructor → Derived constructor → Derived destructor → Base destructor
  • [ ] C) Base constructor → Derived constructor → Base destructor → Derived destructor
  • [ ] D) Derived constructor → Base constructor → Base destructor → Derived destructor

💡 Giải thích: Constructor chạy từ Base trước, Derived sau (xây móng trước, xây nhà sau). Destructor chạy ngược lại: Derived trước, Base sau (phá nhà trước, phá móng sau). Đây là quy tắc bất biến trong C++ inheritance.

Câu 2: Đoạn code sau có vấn đề gì?

cpp
class Animal {
private:
    std::string name_;
public:
    Animal(const std::string& n) : name_(n) {}
};
class Dog : public Animal {
public:
    Dog(const std::string& n) : Animal(n) {}
    void print() { std::cout << name_; }
};
  • [ ] A) Dog không gọi được constructor của Animal
  • [x] B) Dog::print() không thể truy cập name_ vì nó là private trong Animal
  • [ ] C) Thiếu virtual destructor trong Animal
  • [ ] D) Code hoàn toàn đúng, không có lỗi

💡 Giải thích: Member name_ được khai báo private trong Animal, nên derived class Dog không thể truy cập trực tiếp. Để derived class truy cập được, cần đổi thành protected, hoặc cung cấp public/protected getter method.

Câu 3: Với public inheritance, member protected của base class trở thành gì trong derived class?

  • [ ] A) public
  • [x] B) protected
  • [ ] C) private
  • [ ] D) Không thể truy cập

💡 Giải thích: Với public inheritance: publicpublic, protectedprotected, private → không truy cập được. Đây là loại inheritance phổ biến nhất, thể hiện quan hệ "is-a" giữa base và derived class.