Tính Kế Thừa Trong Lập Trình Hướng Đối Tượng, Tính Kế Thừa Trong C++

Kế thừa trong lập trình hướng đối tượng là một tính chất rất quan trọng, do đó, các bạn nên nắm kỹ phần này để có thể học tốt lập trình hướng đối tượng. Trong các kỳ kiểm tra, phỏng vấn xin việc cũng thường được hỏi về tính chất này.

Bạn đang xem: Tính kế thừa trong lập trình hướng đối tượng

Để đọc hiểu bài này tốt nhất, các bạn nên có biến thức Cơ bản về class trong C++, nếu chưa hãy dành một ít thời gian đọc bài viết về class của mình. Nếu bạn đã sẵn sàng thì hãy bắt đầu thôi!

Kế thừa là gì?

Kế thừa là một trong các tính chất đặc trưng của lập trình hướng đối tượng, bên cạnh tính đóng gói (encapsulation), che giấu thông tin (hiding information), tính đa hình (polymorphism) và tính trừu tượng (abstraction). Vậy thì kế thừa là gì?

Kế thừa (inheritance) là một tính chất đặc trưng của lập trình hướng đối tượng. Nó có nghĩa là một class thừa hưởng lại tất cả các thuộc tính, phương thức của class mà nó kế thừa.

Class kế thừa từ một class khác gọi là lớp con (child class hay subclass) hay lớp dẫn xuất (derived class). Class được lớp khác kế thừa được gọi là lớp cha (parent class hay superclass) hay lớp cơ sở (base class).

Ví dụ như bạn có một class con người, có các thuộc tính cơ bản như họ tên, ngày sinh, quê quán, mình khai báo thêm một class sinh viên kế thừa từ class con người. Vậy, class sinh viên sẽ có các thuộc tính họ tên, ngày sinh, quê quán từ class con người mà không cần phải khai báo. Class con người sẽ là lớp cha và class sinh viên là lớp con.

Ngoài các thuộc tính của class cha, class con còn có thể có thêm các thuộc tính, phương thức của riêng mình. Ví dụ như sinh viên thì có thêm các thuộc tính như MSSV, tên trường, chuyên ngành…

***

Phạm vi truy cập trong kế thừa C++

Đối với một class, thuộc tính protected cũng tương tự như private, chỉ có khác ở chỗ class con có thể truy cập thành phần protected, còn private lại không cho phép truy cập.

Phạm vi truy cập giúp đảm bảo tính đóng gói và che giấu thông tin của đối tượng. Ví dụ như khi thuộc tính public ở class cha, được kế thừa private sang class con, thì nó cũng trở thành private ở class con và không thể được truy cập từ bên ngoài class con. Ví dụ:

class A{public: int publicMethod;};class B : private A{};// bên trong hàm mainB b;cout

Cài đặt

Từ đầu bài viết đến giờ, mình nói khá nhiều về lý thuyết mà chưa có đoạn code nào cả. Để cho các bạn dễ hiểu hơn, mình sẽ sử dụng lại các ví dụ trong bài viết và code chúng bằng C++. Hãy bắt đầu với các quan hệ Has-A trước.

Từ đầu bài viết đến giờ, mình nói khá nhiều về lý thuyết mà chưa có đoạn code nào cả. Để cho các bạn dễ hiểu hơn, mình sẽ sử dụng lại các ví dụ trong bài viết và code chúng bằng C++. Hãy bắt đầu với các quan hệ Has-A trước.

Quan hệ 1 – 1:

class GVCN{public: LopHoc lopHoc;};class LopHoc{public: GVCN gvcn;};Bạn nào chưa biết về vector có thể xem lại bài viết Vector trong C++ của mình nha.

Quan hệ 1 – n:

class HocSinh{public: LopHoc lopHoc;};class LopHoc{public: vector hocSinh;};Quan hệ n – n:

class BenhNhan{public: vector bacSi;};class BacSi{public: vector benhNhan;};Quan hệ Is-A:

class ConNguoi{public: string hoTen; string ngaySinh; string queQuan;};class SinhVien : public ConNguoi{public: string MSSV; string tenTruong; string lop; string chuyenNganh;};

Truy xuất thành phần từ lớp cơ sở

Khi class con kế thừa thành phần từ class cơ sở, các thành phần của nó cũng sẽ được thừa hưởng từ class cơ sở. Điều này có nghĩa là, bạn có thể sử dụng con trỏ this để thao tác với thành phần của class hiện tại như bình thường. Ví dụ:

Khi class con kế thừa thành phần từ class cơ sở, các thành phần của nó cũng sẽ được thừa hưởng từ class cơ sở. Điều này có nghĩa là, bạn có thể sử dụng con trỏ this để thao tác với thành phần của class hiện tại như bình thường. Ví dụ:

class A{public: int count; void sayHello() { cout count = 0; // thuộc tính count được kế thừa và có thể truy cập qua con trỏ this this->sayHello(); // phương thức cũng được kế thừa }};

Override phương thức từ lớp cơ sở

Trong khi sử dụng tính kế thừa, bạn sẽ gặp phải trường hợp hai phương thức ở class con và class cha trùng tên nhau. Trong trường hợp này, phương thức được khai báo và định nghĩa ở class con sẽ ghi đè lên phương thức ở class cha và thay thế hoàn toàn nó. Ví dụ:

Trong khi sử dụng tính kế thừa, bạn sẽ gặp phải trường hợp hai phương thức ở class con và class cha trùng tên nhau. Trong trường hợp này, phương thức được khai báo và định nghĩa ở class con sẽ ghi đè lên phương thức ở class cha và thay thế hoàn toàn nó. Ví dụ:

class A{public: void sayHello() { cout Vậy thì bạn có thể thấy được là, nếu như mà phương thức bị trùng tên thì nó bị ghi đè, vậy thì không còn đảm bảo được tính tái sử dụng code của tính kế thừa. Vậy làm sao chúng ta có thể tái sử dụng lại đoạn code của phương thức ở class cha?

Sử dụng toán tử phân giải phạm vi (::), chúng ta có thể gọi được phương thức từ class cha, ngoài ra chúng ta còn có thể bổ sung được những đoạn code của riêng phương thức ở class con. Ví dụ:

class A{public: void sayHello() { cout Thật đơn giản đúng không nào!

Upcasting và downcasting

Nhiều bạn có thể sẽ thắc mắc, ví dụ “sinh viên là một con người”, vậy thì con trỏ kiểu sinh viên có thể trỏ đến con trỏ kiểu con người hoặc ngược lại hay không. Đây chính là vấn đề upcasting và downcasting. Tuy nhiên, để đảm bảo tính toàn vẹn dữ liệu, bạn chỉ nên xem tham khảo chứ không nên dùng, do có thể gây mất mát dữ liệu hoặc nhận dữ liệu không mong muốn.

Upcasting

Upcasting là chuyển đổi đối tượng kiểu class con sang kiểu class cha. Tức là, con trỏ kiểu class cha có thể trỏ đến con trỏ kiểu class con, nhưng không bảo toàn được những thuộc tính, phương thức của class con. Việc chuyển kiểu này được thực hiện ngầm định bởi trình biên dịch C++. Các phương thức bị override sẽ bị override ngược lại bởi class cha. Ví dụ:

class A{public: void sayHello() { cout sayHello(); // Hello, welcome to hutgiammo.com!p->sayGoodbye(); // Lỗi do A không có phương thức nàyRõ ràng, nếu như sinh viên là một con người, thì con trỏ kiểu con người có thể lưu trữ được sinh viên đúng không bởi vì sinh viên là con người mà!

Tuy nhiên, do class cha là class tổng quát hóa, có ít thông tin hơn so với class con, do đó, khi chuyển kiểu ta không thể đảm bảo sự toàn vẹn dữ liệu được.

Downcasting

Downcasting thì ngược lại với upcasting, tức là chuyển đối tượng kiểu class cha sang đối tượng kiểu class con. Cách này lại không được trình biên dịch thực hiện tự động mà bạn phải tự chuyển kiểu nó. Tuy nhiên, các thuộc tính, phương thức ở class con nhiều hơn class cha, do class con là cụ thể hóa của class cha. Do đó, các thuộc tính không có ở class cha sẽ nhận giá trị rác không mong muốn. Ví dụ:

class A{public: void sayHello() { cout propInB = 0; } void sayHello() { A::sayHello(); cout propInB; // Một số ngẫu nhiên

Đa kế thừa

Đa kế thừa nghĩa là thay vì một class dẫn xuất kế thừa từ một class cơ sở, class dẫn xuất này có thể có kế thừa từ nhiều class cơ sở khác nhau. Ví dụ như một loài động vật thì sẽ kế thừa đặc điểm từ cả bố và mẹ của chúng.

Đa kế thừa nghĩa là thay vì một class dẫn xuất kế thừa từ một class cơ sở, class dẫn xuất này có thể có kế thừa từ nhiều class cơ sở khác nhau. Ví dụ như một loài động vật thì sẽ kế thừa đặc điểm từ cả bố và mẹ của chúng.

Trong C++, để thực hiện đa kế thừa, cú pháp tương tự như kế thừa, nhưng class cơ sở có thể có nhiều hơn 1, ngăn cách nhau bởi dấu phẩy (,). Ví dụ:

class A {public: A() {}};class B {public: B() {}};// Class C kế thừa từ class A và Bclass C : public A, public B {public: C() {}};Khi thừa kế từ nhiều class cơ sở, bạn thấy sẽ có một vấn đề phát sinh chính là khi lớp cơ sở có phương thức trùng tên, khi gọi phương thức sẽ xảy ra sự mơ hồ. Sự mơ hồ nghĩa là trình biên dịch không biết bạn đang gọi phương thức từ class dẫn xuất nào. Ví dụ:

class A {public: A() {} void sayHello() { cout Để giải quyết sự nhập nhằn này, bạn có thể sử dụng toán tử phạm vi (::) để chỉ rõ phương thức ở class nào như sau:

class A {public: A() {} void sayHello() { cout

Tổng kết

Trong phần kế thừa này, cơ bản nó không khó, chỉ cần bạn phân tích đặc tả được dữ liệu mà đề bài cho, hoặc trong tình huống thực tế, bạn sẽ làm được nó. Trong thời gian tới mình sẽ cố gắng tổng hợp bài tập về phần này để các bạn nắm được về kế thừa trong C++.

Xem thêm:

Trong phần kế thừa này, cơ bản nó không khó, chỉ cần bạn phân tích đặc tả được dữ liệu mà đề bài cho, hoặc trong tình huống thực tế, bạn sẽ làm được nó. Trong thời gian tới mình sẽ cố gắng tổng hợp bài tập về phần này để các bạn nắm được về kế thừa trong C++.Xem thêm: Từ Điển Anh Việt ” Shower Là Gì, Một Số Vấn Đề Liên Quan Đến “Shower”

Vậy là qua bài này, mình đã giới thiệu cho các bạn biết về kế thừa trong C++ nói riêng và trong lập trình hướng đối tượng nói chung. Hi vọng là các bạn thấy thấy bài viết của mình, đừng quên chia sẻ cho bạn bè cùng biết nha.

Nếu có bất kỳ thắc mắc hoặc góp ý nào, các bạn có thể comment phía bên dưới bài viết. Cảm ơn các bạn đã đọc bài viết!