Kế thừa (Inheritance)
Kế thừa (inheritance) cho phép một class (gọi là lớp con - derived class) tái sử dụng thuộc tính và phương thức của một class khác (gọi là lớp cha - base class), đồng thời có thể bổ sung hoặc thay đổi hành vi riêng. Đây là cách mô hình hóa quan hệ “là một” (is-a) - ví dụ: Dog là một Animal.
Cú pháp cơ bản
Phần tiêu đề “Cú pháp cơ bản”#include <iostream>#include <string>
class Animal { // Lớp cha (base class)public: std::string name;
Animal(std::string n) : name(n) {}
void eat() { std::cout << name << " dang an" << std::endl; }};
class Dog : public Animal { // Lớp con (derived class) kế thừa từ Animalpublic: Dog(std::string n) : Animal(n) {} // Gọi constructor của lớp cha
void bark() { std::cout << name << " sua: Gau gau!" << std::endl; }};
int main() { Dog d("Mực"); d.eat(); // Ke thua tu Animal: Mực dang an d.bark(); // Rieng cua Dog: Mực sua: Gau gau!
return 0;}Dog tự động có sẵn thuộc tính name và phương thức eat() từ Animal, không cần viết lại.
protected: cầu nối giữa private và public
Phần tiêu đề “protected: cầu nối giữa private và public”Thành viên private của lớp cha không truy cập được từ lớp con. Nếu muốn lớp con truy cập trực tiếp một thành viên nhưng vẫn giấu nó khỏi bên ngoài, dùng protected:
class Animal {protected: int age; // Lớp con truy cập được, bên ngoài thì không
public: Animal(int a) : age(a) {}};
class Dog : public Animal {public: Dog(int a) : Animal(a) {}
void printAge() { std::cout << "Tuoi: " << age << std::endl; // OK vì age là protected }};Gọi constructor của lớp cha
Phần tiêu đề “Gọi constructor của lớp cha”Lớp con phải gọi constructor của lớp cha (tường minh hoặc ngầm định) để khởi tạo phần dữ liệu kế thừa. Nếu lớp cha không có constructor không tham số, lớp con bắt buộc phải gọi tường minh trong initializer list, như ví dụ Dog(std::string n) : Animal(n) ở trên.
Ghi đè phương thức (method overriding, sơ bộ)
Phần tiêu đề “Ghi đè phương thức (method overriding, sơ bộ)”Lớp con có thể định nghĩa lại một phương thức đã có ở lớp cha:
class Animal {public: void makeSound() { std::cout << "Dong vat phat ra am thanh" << std::endl; }};
class Cat : public Animal {public: void makeSound() { // Ghi đè (override) phiên bản của Animal std::cout << "Meo: Meo meo!" << std::endl; }};
int main() { Cat c; c.makeSound(); // Meo: Meo meo! - gọi phiên bản của Cat
return 0;}Đây mới là ghi đè “tĩnh” (dựa vào kiểu khai báo). Để ghi đè hoạt động đúng cả khi thông qua con trỏ/tham chiếu tới lớp cha, cần từ khóa virtual - xem bài Đa hình (Polymorphism).
Các loại kế thừa: public, protected, private
Phần tiêu đề “Các loại kế thừa: public, protected, private”Từ khóa đặt trước tên lớp cha (class Dog : public Animal) quyết định mức truy cập tối đa mà lớp con “nhìn thấy” các thành viên kế thừa:
| Kiểu kế thừa | Thành viên public của cha trở thành |
Thành viên protected của cha trở thành |
|---|---|---|
public |
public |
protected |
protected |
protected |
protected |
private |
private |
private |
Trong thực tế, public là kiểu kế thừa phổ biến nhất (đúng nghĩa “is-a”); protected/private ít dùng hơn nhiều.
Tổng kết
Phần tiêu đề “Tổng kết”- Kế thừa cho phép lớp con tái sử dụng thuộc tính/phương thức của lớp cha, mô hình hóa quan hệ “is-a”
- Cú pháp:
class Con : public Cha { ... } protectedcho phép lớp con truy cập trực tiếp, trong khi vẫn giấu thành viên đó khỏi bên ngoài- Constructor lớp con nên gọi tường minh constructor lớp cha trong initializer list
- Ghi đè phương thức ở lớp con hoạt động “tĩnh” theo kiểu khai báo, trừ khi dùng
virtual(xem bài Đa hình)