Chi tiết bài học Đa kế thừa và kế thừa nhiều tầng

Đa kế thừa và kế thừa nhiều tầng

Trong bài này, bạn sẽ được học về các mô hình kế thừa khác nhau trong C++: Đa kế thừa (Multiple inheritance), kế thừa nhiều tầng (Multilevel inheritance) và kế thừa có cấu trúc (Hierarchical inheritance) kèm theo ví dụ.

Kế thừa là một trong những tính năng cốt lõi của một ngôn ngữ lập trình hướng đối tượng. Nó cho phép lập trình viên ứng dụng tạo một lớp kế thừa từ một lớp có sẵn. Lớp kế thừa sẽ kế thừa toàn bộ tính năng của lớp chính (lớp đã tồn tại).

Có nhiều mô hình kế thừa trong lập trình C++.

Kế thừa nhiều tầng trong C++

Trong lập trình C++, bạn không chỉ có thể kế thừa một lớp từ lớp chính mà bạn còn có thể kế thừa một lớp từ lớp kế thừa. Dạng kế thừa này được gọi là kế thừa nhiều tầng.

class A
{ 
... .. ... 
};
class B: public A
{
... .. ...
};
class C: public B
{
... ... ...
};

Ở đây, lớp B được kế thừa từ lớp chính A và lớp C được kế thừa lớp kế thừa B.

Ví dụ 1: Kế thừa nhiều tầng trong C++

#include <iostream>
using namespace std;

class A
{
    public:
      void display()
      {
          cout<<"Base class content.";
      }
};

class B : public A
{

};

class C : public B
{
 
};

int main()
{
    C obj;
    obj.display();
    return 0;
}

Đầu ra

Base class content.

Trong chương trình này, lớp C được kế thừa từ lớp B (đây là lớp kế thừa từ lớp chính A).

Đối tượng obj của lớp C được định nghĩa trong hàm main().

Khi hàm display() được gọi, display() trong lớp A được thực thi. Đó là bởi vì không có lớp display() trong lớp C và lớp B.

Trình biên dịch sẽ tìm hàm display() trong C đầu tiên. Vì hàm này không tồn tại ở đây, nên nó sẽ tìm hàm này trong lớp B (vì C được kế thừa từ B).

Hàm này cũng không tồn tại trong lớp B, vì thế trình biên dịch tìm nó trong lớp A (vì B là lớp được kế thừa từ A).

Nếu hàm display() tồn tại trong C, trình biên dịch sẽ ghi đè hàm display() của lớp A (do tính năng ghi đè hàm thành viên).

Đa kế thừa trong C++

Trong lập trình C++, một lớp có thể được kế thừa từ nhiều hơn một lớp cha. Ví dụ: Lớp Bat (dơi) được kế thừa từ lớp chính Mammal (động vật có vú) và WingedAnimal (động vật có cánh). Điều này hoàn toàn dễ hiểu vì dơi vừa là động vật có vú và vừa là động vật có cánh.

Ví dụ 2: Đa kế thừa trong lập trình C++

Chương trình sau tính toán diện tính và chu vi của một hình chữ nhật sử dụng đa kế thừa.

#include <iostream>
using namespace std;

class Mammal {
  public:
    Mammal()
    {
      cout << "Mammals can give direct birth." << endl;
    }
};

class WingedAnimal {
  public:
    WingedAnimal()
    {
      cout << "Winged animal can flap." << endl;
    }
};

class Bat: public Mammal, public WingedAnimal {

};

int main()
{
    Bat b1;
    return 0;
}

Đầu ra

Mammals can give direct birth.

Winged animal can flap.

Nhập nhằng trong đa kế thừa

Vấn đề dễ nhìn thấy nhất trong đa kế thừa xảy ra trong khi thực hiện ghi đè hàm.

Giả sử hai lớp chính có cùng một hàm nhưng hàm này lại không được ghi đè trong lớp kế thừa.

Nếu bạn thử gọi hàm này sử dụng đối tượng của lớp kế thừa, trình biên dịch sẽ báo lỗi. Đó là bởi vì trình biên dịch sẽ không biết phải gọi hàm nào. Ví dụ,

class base1
{
  public:
     void someFunction( )
     { .... ... .... }  
};
class base2
{
    void someFunction( )
     { .... ... .... } 
};
class derived : public base1, public base2
{
    
};

int main()
{
    derived obj;

    obj.someFunction() // Lỗi!  
}

Vấn đề này có thể được giải quyết bằng cách sử dụng hàm phân giải phạm vi để xác định hàm thuộc lớp base1 hay base2 sẽ được gọi

int main()
{
    obj.base1::someFunction( );  // hàm của lớp base1 sẽ được gọi
    obj.base2::someFunction();   // hàm của lớp base2 sẽ được gọi.
}

Kế thừa có cấu trúc trong C++

Nếu có nhiều hơn một lớp được kế thừa từ lớp chính, nó được gọi là kế thừa có cấu trúc. Trong kế thừa có cấu trúc, toàn bộ tính năng chung trong các lớp con đều có được từ lớp chính.

Ví dụ: Vật lý, hóa học, sinh học đều được kế thừa từ lớp khoa học.

Cú pháp của kế thừa có cấu trúc

class base_class {
     ... .. ...
}

class first_derived_class: public base_class {
     ... .. ...
}

class second_derived_class: public base_class {
     ... .. ...
}

class third_derived_class: public base_class {
     ... .. ...
}