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

Tính kế thừa C++

Ở bài học trước các bạn đã tìm hiểu và có cái nhìn cụ thể về tính đa hình trong C++. Bên cạnh đó, tính kế thừa cũng là đặc tính quan trọng trong ngôn ngữ lập trình hướng đối tượng. Nói cách khác, bài viết sẽ cho bạn biết khái niệm về tính kế thừa, cũng như những phương pháp lập trình và ví dụ minh họa kèm theo.

Sơ lược về tính kế thừa

Kế thừa được xem là một trong số những tính năng quan trọng trong bộ môn lập trình hướng đối tượng của ngôn ngữ C++. Có thể nói nó hỗ trợ người lập trình trong việc khởi tạo một lớp (lớp kế thừa) từ lớp chính (lớp có sẵn). Lúc này, lớp kế thừa sẽ kế thừa hầu hết những tính năng trên lớp chính và mang theo những tính năng bổ trợ của chính nó.

Tính kế thừa trong C++
Hình 1. Tính kế thừa trong C++

Lý do bạn phải sử dụng tính kế thừa

Kế thừa khá cần thiết nhất là đối với ngôn ngữ C++ vì nó hỗ trợ những nhà lập trình có thể thiết lập những lớp mới từ những lớp đã có sẵn. Không những vậy, tính kế thừa có vai trò tránh việc trùng lặp mã lệnh hoặc gia tăng khả năng dùng lại mã lệnh để tối ưu những tính năng khác như tính đóng gói. Cuối cùng, nó mang đến sự tối ưu cho việc bảo trì những dự án lớn sau này.

Sau đây, chúng tôi xin điểm qua những lý do mà bạn nên dùng tính kế thừa trong C++.

1. Tránh lặp lại mã lệnh: trong quá trình dùng đặc tính này, người dùng hoàn toàn có thể tái sử dụng mã lệnh từ những lớp cha mà không phải viết lại chúng. Chính vì thế, sẽ hỗ trợ bạn hạn chế trùng lặp mã lệnh và tiết kiện thời gian.

Tính kế thừa giúp coder tránh lặp lại mã lệnh thường xuyên
Hình 2. Tính kế thừa giúp coder tránh lặp lại mã lệnh thường xuyên

2. Nâng cao khả năng tái sử dụng: tính chất kế thừa thường giúp người dùng tạo ra những lớp con từ các lớp có sẵn để từ đó linh hoạt trong việc sử dụng lại mã lệnh trong chương trình.

3. Hoàn thiện tính đóng gói: tạo ra cấu trúc lớp phù hợp và mang tính đóng gói hiệu quả. Những thành phần cũng như hàm được bao bọc ở lớp này chỉ được truy cập từ phương thức công khai.

4. Tăng tính trừu tượng: tính kế thừa sẽ cải thiện tính trừu tượng trong quá trình thiết kế lớp. Người dùng hoàn toàn tạo ra những lớp cha trừu tượng nhằm định nghĩa phương thức hay thuộc tính trên một nhóm đối tượng và những lớp con sẽ kế thừa từ lớp cha sẽ thực thi những thuộc tính chuyên biệt cho chúng.

5. Tối ưu quá trình bảo trì: các bạn sẽ cảm thấy việc bảo trì được đơn giản hóa hơn vì khi mã lệnh của lớp cha thay đổi, lúc này lớp con cũng sẽ được cập nhật theo. Từ đó, không nhất thiết phải biến đổi mã lệnh trong các lớp con theo cách truyền thống.

Làm sao để lập trình kế thừa trong C++?

Tính kế thừa sẽ được thực thi thông qua từ khóa “Class” cũng như từ khóa “Public” nhằm định nghĩa những quyền truy cập vào lớp con tương ứng với những thành phần trên lớp cha. Để giúp bạn hiểu thêm về nó, chúng tôi xin trình bày ví dụ bên dưới.

#include <iostream>

using namespace std;

// lớp cha

class Shape {

protected:

int width, height;

public:

void setWidth(int w) {

width = w;

}

void setHeight(int h) {

height = h;

}

};

// lớp con kế thừa từ lớp cha Shape

class Rectangle: public Shape {

public:

int getArea() {

return (width * height);

}

};

int main() {

Rectangle rect;

rect.setWidth(5);

rect.setHeight(7);

// in ra diện tích của hình chữ nhật

cout << "Diện tích của hình chữ nhật là: " << rect.getArea() << endl;

return 0;

}

Từ đoạn code trên cho thấy lớp cha “Shape” và lớp con đã kế thừa từ lớp cha “Rectangle”. Khi xét đến lớp cha “Shape”, sẽ xuất hiện hai thuộc tính là “width” và “height”. Chúng sẽ được định nghĩa theo từ khóa “Protected”.

Còn đối với lớp con “Rectangle”, nó được định nghĩa thông qua phương thức “getArea()” nhằm thực hiện việc tính diện tích hình chữ nhật thông qua việc nhân chiều rộng và chiều cao (được trích từ những thuộc tính kế thừa từ lớp cha).

Không những vậy, phương thức “getArea()” từ lớp con sẽ được truy cập với những thuộc tính “width” và “height” trên lớp cha bởi vì nó được khai thác từ quyền truy cập “protected” từ lớp cha.

Mặt khác, đối với hàm main(), người dùng có thể tạo ra đối tượng “rect” từ lớp con Rectangle. Kế đến, hãy gọi phương thức “setWidth()” và “setHeight()” từ lớp cha “Shape” nhằm gán giá trị cho thuộc tính “width” và “height”.

Cuối cùng, để tính diện tích hình chữ nhật và xuất kết quả ra màn hình thì chúng ta sử dụng phương thức “getArea()” từ lớp con Rectangle.

Dùng tính kế thừa sẽ cải thiện sự linh hoạt trong dự án C++
Hình 3. Dùng tính kế thừa sẽ cải thiện sự linh hoạt trong dự án C++

Chỉ định truy xuất kế thừa trong C++

Khi bàn đến việc kế thừa lớp trong C++, chúng tôi xin giới thiệu ba chỉ định truy xuất quan trọng như ‘public’, ‘private’, và ‘protected’. Người dùng sẽ dùng những chỉ định này để theo dõi và quản lý quá trình truy cập cho những thành viên trên lớp cha mà lớp con được kế thừa.

Ý nghĩa những chỉ định truy xuất như sau:

– ‘Public’: những thành viên từ lớp cha sẽ có thể kế thừa từ chỉ định này cũng như có thể truy cập công khai đối với lớp con. Nói cách khác, những đối tượng khác hoàn toàn được phép truy cập vào những thành viên đó từ đối tượng của lớp con.

– ‘Protected’: Những thành viên từ lớp cha sẽ kế thừa từ chỉ định này và được truy cập bảo vệ bên trong lớp con. Lúc này, những đối tượng khác sẽ không thể truy cập trên các thành viên này thông qua đối tượng từ lớp con. Tuy nhiên, nhờ tính kế thừa mà những lớp con khác sẽ truy cập đến chúng dễ dàng.

– ‘Private’: những thành viên trên lớp cha sẽ được kế thừa theo chỉ định cũng như được quyền truy cập riêng tư đối với lớp con. Có thể hiểu là những đối tượng khác không thể truy cập đối với những thành viên này thông qua đối tượng từ lớp con hay những lớp con khác.

Ví dụ:

class Base {

public:

int public_var;

protected:

int protected_var;

private:

int private_var;

};

class Derived : public Base {

public:

void test() {

public_var = 1; // OK

protected_var = 2; // OK

private_var = 3; // ERROR

}

};

int main() {

Base b;

Derived d;

b.public_var = 1; // OK

b.protected_var = 2; // ERROR

b.private_var = 3; // ERROR

d.public_var = 1; // OK

d.protected_var = 2; // OK

d.private_var = 3; // ERROR

return 0;

}

Lớp ‘Base’ sẽ gồm ba thành viên tương ứng với những chỉ định truy xuất khác nhau và lớp ‘Derived’ kế thừa từ lớp ‘Base’. Đối với hàm ‘test’ từ lớp ‘Derived’ thì các bạn sẽ nhận ra rằng không quá khó để truy cập trên những thành viên được kế thừa từ lớp ‘Base’ từ chỉ định truy xuất liên quan.

Làm sao để ghi đè hàm thành viên trong kế thừa C++?

Đối với kế thừa lớp trong C++, người dùng hoàn toàn được phép ghi đè (override) lên những thành viên từ lớp cha trong lớp con. Lúc này, lớp con sẽ ghi đè một hàm thành viên từ lớp cha, nó sẽ thực thi hàm này hơn là hàm tương tự ở lớp cha.

Mặt khác, chỉ cần định nghĩa một hàm mới trong lớp con với cùng tên, kiểu trả về, hay tham số với hàm của lớp cha để ghi đè một hàm thành viên từ lớp cha lên lớp con. Hãy dùng từ khóa ‘override’ để cho mọi người biết hàm mới này chính là hàm ghi đè từ lớp cha.

Ví dụ:

#include <iostream>

class Base {

public:

virtual void print() {

std::cout << "This is the Base class." << std::endl;

}

};

class Derived : public Base {

public:

void print() override {

std::cout << "This is the Derived class." << std::endl;

}

};

int main() {

Base* b = new Derived();

b->print(); // This is the Derived class.

delete b;

return 0;

}

Lớp ‘Base’ sẽ có hàm thành viên ‘print’ với định nghĩa ảo. Nói cách khác, nó được ghi đè từ những lớp con. Lớp ‘Derived’ có thể ghi đè lên hàm ‘print’ từ lớp cha thông qua cách định nghĩa một hàm mới với cùng tên hay kiểu trả về tương ứng với hàm ‘print’ trên lớp cha. Sau đó, chúng ta dùng từ khóa ghi đè lên: ‘override’.

Đối với hàm ‘main’, hãy khởi tạo một con trỏ hướng tới đối tượng từ lớp ‘Derived’ và sau đó gọi hàm ‘print’. Cuối cùng, chạy đoạn code và hiển thị kết quả: “This is the Derived class” do chính hàm ‘print’ từ lớp ‘Derived’ được thực thi đúng hơn là dùng hàm ‘print’ từ lớp ‘Base’.

Kết

Tính kế thừa gần như là tính chất quan trọng đối với ngôn ngữ C++. Chúng tôi tin rằng các bạn sẽ cảm thấy hứng thú và tiếp cận dễ dàng hơn đối với nội dung liên quan đến tính kế thừa trong ngôn ngữ lập trình hướng đối tượng.

Rate this post

Để lại một bình luận

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 *