Tính Đa Kế Thừa (Multiple Inheritance) Trong C++

Tính đa kế thừa

Tính đa kế thừa là chuyên đề tiếp theo mà chúng tôi sẽ giới thiệu trong bài viết này. Ngoài tính đa hình, tính kế thừa, thì tính đa kế thừa cũng được xem như là tính chất quan trọng trong lập trình hướng đối tượng C++.

Hãy theo chúng tôi để tìm hiểu về tính đa kế thừa trong C++ nhé. Từ đó, từng bước hoàn thiện hơn về mặt kiến thức để áp dụng cho các dự án quy mô lớn về sau.

Tính đa kế thừa trong C++ là gì?

Đa kế thừa được biết đến như một tính năng hỗ trợ một lớp được kế thừa từ nhiều lớp cha. Lúc này, nó sẽ có hết tất cả những thuộc tính cũng như phương thức từ những lớp cha vì lớp này được kế thừa từ chính những lớp cha đó.

Để thực hiện đa kế thừa trong C++ thì các bạn có thể dùng từ khóa ‘class’ hay ‘struct’ tương ứng với danh sách kế thừa.

Ví dụ:

class A {

public:

void funcA() {}

};

class B {

public:

void funcB() {}

};

class C : public A, public B {

public:

void funcC() {}

};

Lúc này, lớp C sẽ kế thừa từ chính lớp A và kể cả lớp B. Từ đó, nó được phép dùng cả hai phương thức funcA() và funcB().

Tính đa kế thừa
Hình 1. Tính đa kế thừa

Cách khai báo đa kế thừa

Sau khi hiểu được tính đa kế thừa, hãy cùng nhau tìm hiểu cách khai báo nó nhé. Nói cách khác, chúng ta có thể khai báo đa kế thừa thông qua việc dùng danh sách kế thừa trong ngôn ngữ lập trình C++. Lúc này, danh sách những lớp cha sẽ được tách biệt thông qua dấu phẩy hay được đặt ở trong dấu ngoặc kép sau tên lớp con.

Coder dùng danh sách kế thừa để khai báo đa kế thừa
Hình 2. Coder dùng danh sách kế thừa để khai báo đa kế thừa

Hãy cùng theo dõi cú pháp khai báo đa kế thừa như sau:

class ChildClass : accessSpecifier ParentClass1, accessSpecifier ParentClass2, ..., accessSpecifier ParentClassN

{

// ...

};

Chú thích:

– ‘ChildClass’: tên của lớp con

– ‘ParentClass1’, ‘ParentClass2’,…’ ParentClassN’: những tên của tất cả lớp cha.

– ‘accessSpecifier’: từ khóa ‘public’, ‘private’, hay ‘protected’ và dùng để xác định phương pháp truy cập tới những thành phần được kế thừa từ những lớp cha.

Ví dụ:

class Animal {

public:

void eat() {

cout << "Eating...\n";

}

};

class Bird {

public:

void fly() {

cout << "Flying...\n";

}

};

class Parrot : public Animal, public Bird {

public:

void talk() {

cout << "Talking...\n";

}

};

Từ đoạn code trên có thể thấy lớp ‘Parrot’ được khai báo kế thừa từ lớp cha: ‘Bird’ và ‘Animal’ thông qua việc dùng danh sách kế thừa. Chính vì thế, lớp ‘Parrot’ hoàn toàn dùng được trên cả hai phương thức ‘eat()’ từ lớp ‘Animal’ cũng như ‘fly()’ từ lớp ‘Bird’.

Chú ý:

– Khi đang thực hiện quá trình đa kế thừa, mỗi lớp cơ sở hoàn toàn được tách biệt nhau thông qua việc dùng dấu phẩy “,”.

– Mỗi lớp cơ sở sẽ tồn tại một loại dẫn xuất theo một từ khóa dấn xuất khác nhau.

– Quy tắc truy cập vào những thành phần lớp cơ sở này là giống với kế thừa đơn.

Thế nào là hàm khởi tạo trong đa kế thừa?

Khi một lớp kế thừa từ nhiều lớp cha thì lúc này lớp cha đều có hàm khởi tạo cho riêng mình. Tuy nhiên, khi bạn tạo ra đối tượng từ lớp con thì những hàm khởi tạo từ những lớp cha có thể gọi theo thứ tự mà chúng khai báo kế thừa.

Hàm khởi tạo trong đa kế thừa
Hình 3. Hàm khởi tạo trong đa kế thừa

Bên cạnh đó, để biết được thứ tự gọi hàm khởi tạo trong đa kế thừa của C++ thì chúng ta có thể dùng quy tắc 3-5-7. Dựa trên quy tắc này, khi đối tượng đã được khởi tạo, hàm khởi tạo từ lớp cha ban đầu sẽ được gọi, kế đến là lớp thứ hai, thứ ba và tuần tự cho tới lớp con.

Trong trường hợp một lớp con không thể xác định bất kỳ hàm khởi tạo thì hàm khởi tạo (default) từ lớp con có thể được dùng. Tuy vậy, nếu lớp cha có hàm khởi tạo mà không kèm tham số thì lớp con sẽ cần một hàm khởi tạo không tham số liên quan để khởi tạo đối tượng trên lớp con.

Ví dụ:

#include <iostream>

using namespace std;

class A {

public:

A() {

cout << "A's constructor called" << endl;

}

};

class B {

public:

B() {

cout << "B's constructor called" << endl;

}

};

class C: public A, public B {

public:

C() {

cout << "C's constructor called" << endl;

}

};

int main() {

C obj;

return 0;

}

Sau đó, các bạn chạy đoạn code trên để xuất kết quả.

A's constructor called

B's constructor called

C's constructor called

Lúc này, hàm khởi tạo từ lớp A được gọi trước, và sau đó là hàm khởi tạo trên lớp B. Sau cùng, là hàm khởi tạo của lớp C.

Chú ý:

Giả định khi dùng hàm khởi tạo ngầm định hay không kèm theo tham số thì người dùng không nhất thiết phải gọi với hàm khởi tạo từ các lớp cơ sở hay trình biên dịch có thể được gọi tự động đến chúng theo trình tự dẫn xuất.

Hàm huỷ bỏ trong đa kế thừa

Đối với ngôn ngữ lập trình C++, lớp con sẽ được kế thừa từ nhiều lớp cha và mỗi lớp cha đều có hàm hủy bỏ cho riêng mình. Trong trường hợp đối tượng trên lớp con được tối ưu thì hàm hủy bỏ từ những lớp cha sẽ có thể gọi lần lượt theo thứ tự ngược lại với những thứ tự gọi hàm khởi tạo.

Không quá khác biệt so với hàm khởi tạo, để hỗ trợ người dùng trong việc gọi hàm hủy bỏ trong đa kế thừa, xin hãy áp dụng quy tắc 3-5-7 ở trên nhé.

Ví dụ:

#include <iostream>

using namespace std;

class A {

public:

~A() {

cout << "A's destructor called" << endl;

}

};

class B {

public:

~B() {

cout << "B's destructor called" << endl;

}

};

class C: public A, public B {

public:

~C() {

cout << "C's destructor called" << endl;

}

};

int main() {

C obj;

return 0;

}

Tiếp theo, bạn chạy đoạn code này để in ra kết quả trên màn hình như sau

C's destructor called

B's destructor called

A's destructor called

Lúc này, bạn có thể nhận ra rằng hàm hủy bỏ của lớp C sẽ được gọi đầu tiên. Sau đó, là hàm hủy bỏ trên lớp B và sau cùng là hàm hủy bỏ lớp A.

Chú ý:

– Hàm hủy bỏ trên lớp dẫn xuất có thể giải phóng bộ nhớ cho tất cả thành phần bổ sung hoặc có thể là lớp dẫn xuất.

– Hàm hủy bỏ trên lớp dẫn xuất sẽ được gọi sớm nhất có thể. Tiếp đến là hàm hủy bỏ trên những lớp cơ sở.

– Trình biên dịch sẽ thực thi tự động quá trình mà hàm hủy bỏ trong đa kế thừa.

Cách truy cập những thành phần lớp trong đa kế thừa

Khi lớp con đã được kế thừa từ những lớp cha trước đó thì những thành phần của những lớp cha này có thể được kế thừa sang chính lớp con. Chính vì vậy, người dùng hoàn toàn truy cập được những thành phần này trong lớp con thông qua việc dùng toán tử phạm vi để hướng đến lớp mà thành phần đó thuộc về.

Ví dụ:

#include <iostream>

using namespace std;

class A {

public:

int a;

void displayA() {

cout << "Value of a is: " << a << endl;

}

};

class B {

public:

int b;

void displayB() {

cout << "Value of b is: " << b << endl;

}

};

class C: public A, public B {

public:

void display() {

cout << "Values of a and b are: " << a << " and " << b << endl;

}

};

int main() {

C obj;

obj.a = 10;

obj.b = 20;

obj.displayA();

obj.displayB();

obj.display();

return 0;

}

Cuối cùng, hãy chạy đoạn code trên để xuất kết quả.

Value of a is: 10

Value of b is: 20

Values of a and b are: 10 and 20

Lúc này, người dùng đã truy cập những thành phần ‘a’ từ lớp ‘A’ cũng như thành phần ‘b’ từ lớp ‘B’ trong lớp ‘C’.

Tổng kết

Tính đa kế thừa là tính năng gần như quá quen thuộc trong lập trình hướng đối tượng của C++. Bài viết đã cơ bản giới thiệu và trình bày những mục liên quan đến tính đa kế thừa cũng như cách khai báo, khởi tạo hàm, hàm hủy bỏ và truy cập thành phần lớp trong đa kế thừa. Một lần nữa, xin chân thành cảm ơn quý đọc giả đã quan tâm và theo dõi bài viết.

 

Rate this post

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *