Chi tiết tính kế thừa trong C++

Trong bài này, mình sẽ chia sẻ kiến thức cơ bản về kế thừa trong C++ (Inheritance). Cụ thể là đơn kế thừa, bạn sẽ được học kế thừa là gì, các cách khác nhau để lập trình nó kèm theo ví dụ.

Giới thiệu

Kế thừa là một trong các khái niệm cơ sở của phương pháp lập trình hướng đối tượng. Tính kế thừa cho phép định nghĩa các lớp mới từ các lớp đã có. Một lớp có thể là lớp cơ sở cho nhiều lớp dẫn xuất khác nhau. Lớp dẫn xuất sẽ kế thừa một số thành phần (dữ liệu và hàm) của lớp cơ sở, đồng thời có thêm những thành phần mới. Có hai loại kế thừa là: đơn kế thừa và đa kế thừa, có thể minh họa qua các hình vẽ sau đây:

kế thừa trong c++

Định nghĩa

Giả sử đã định nghĩa lớp A. Cú pháp để xây dựng lớp B kế thừa từ lớp A như sau:

Trong đó mode có thể là private hoặc public với ý nghĩa như sau:

  • Kế thừa theo kiểu public thì tất cả các thành phần public của lớp cơ sở cũng là thành phần public của lớp dẫn xuất.
  • Kế thừa theo kiểu private thì tất cả các thành phần public của lớp cơ sở sẽ trở thành các thành phần private của lớp dẫn xuất.

Chú ý: Trong cả hai trường hợp ở trên thì thành phần private của lớp cơ sở làkhông được kế thừa. Như vậy trong lớp dẫn xuất không cho phép truy nhập đến các thành phần private của lớp cơ sở.

Truy nhập các thành phần của lớp kế thừa

Thành phần của lớp dẫn xuất bao gồm: các thành phần khai báo trong lớp dẫn xuất và các thành phần mà lớp dẫn xuất thừa kế từ các lớp cơ sở. Quy tắc sử dụng các thành phần trong lớp dẫn xuất được thực hiện theo theo mẫu như sau: Tên đối tượng.Tên_lớp::Tên_thành_phần. Khi đó chương trình dịch C++ dễ dàng phân biệt thành phần thuộc lớp nào.

Ví dụ Giả sử có các lớp A và B như sau:

Xét khai báo: B ob;

Lúc đó: ob.B::m là thuộc tính n khai báo trong B ob.A::n là thuộc tính n thừa kế từ lớp A ob.D::nhap() là hàm nhap() định nghĩa trong lớp B ob.A::nhap() là hàm nhap() định nghĩa trong lớp A

Chú ý: Để sử dụng các thành phần của lớp dẫn xuất, có thể không dùng tên lớp,chỉ dùng tên thành phần. Khi đó chương trình dịch phải tự phán đoán để biết thành phần đó thuộc lớp nào: trước tiên xem thành phần đang xét có trùng tên với các thành phần nào của lớp dẫn xuất không? Nếu trùng thì đó thành phần của lớp dẫn xuất. Nếu không trùng thì tiếp tục xét các lớp cơ sở theo thứ tự: các lớp có quan hệ gần với lớp dẫn xuất sẽ được xét trước, các lớp quan hệ xa hơn xét sau. Chú ý trường hợp thành phần đang xét có mặt đồng thời trong 2 lớp cơ sở có cùng một đẳng cấp quan hệ với lớp dẫn xuất. Trường hợp này chương trình dịch không thể quyết định được thành phần này thừa kế từ lớp nào và sẽ đưa ra một thông báo lỗi.

Định nghĩa lại các hàm thành phần của lớp cơ sở trong lớp dẫn xuất

Trong lớp dẫn xuất có thể định nghĩa lại hàm thành phần của lớp cơ sở. Như vậy có hai phiên bản khác nhau của hàm thành phần trong lớp dẫn xuất. Trong phạm vi lớp dẫn xuất, hàm định nghĩa lại “che khuất” hàm được định nghĩa. Việc sử dụng hàm nào cần tuân theo quy định ở trên.

Chú ý: Việc định nghĩa lại hàm thành phần khác với định nghĩa hàm quá tải.Hàm định nghĩa lại và hàm bị định nghĩa lại giống nhau về tên, tham số và giá trị trả về. Chúng chỉ khác nhau về vị trí: một hàm đặt trong lớp dẫn xuất và hàm kia thì ở trong lớp cơ sở. Trong khi đó, các hàm quá tải chỉ có cùng tên, nhưng khác nhau về danh sách đối số và tất cả chúng thuộc cùng một lớp. Định nghĩa lại hàm thành phần chính là cơ sở cho việc xây dựng tính đa hình của các hàm.

C++ cho phép đặt trùng tên thuộc tính trong các lớp cơ sở và lớp dẫn xuất. Các thành phần cùng tên này có thể cùng kiểu hay khác kiểu. Lúc này bên trong đối tượng của lớp dẫn xuất có tới hai thành phần khác nhau có cùng tên, nhưng trong phạm vi lớp dẫn xuất, tên chung đó nhằm chỉ định thành phần được khai báo lại trong lớp dẫn xuất. Khi muốn chỉ định thành phần trùng tên trong lớp cơ sở phải dùng tên lớp toán tử ‘::’ đặt trước tên hàm thành phần. Ví dụ 5.2 Xét các lớp A và B được xây dựng như sau:

Ví dụ Chương trình minh họa đơn kế thừa theo kiểu public:

Hàm tạo đối với tính kế thừa

Các hàm tạo của lớp cơ sở là không được kế thừa. Một đối tượng của lớp dẫn xuất về thực chất có thể xem là một đối tượng của lớp cơ sở, vì vậy việc gọi hàm tạo lớp dẫn xuất để tạo đối tượng của lớp dẫn xuất sẽ kéo theo việc gọi đến một hàm tạo của lớp cơ sở. Thứ tự thực hiện của các hàm tạo sẽ là: hàm tạo cho lớp cơ sở, rồi đến hàm tạo cho lớp dẫn xuất.

C++ thực hiện điều này bằng cách: trong định nghĩa của hàm tạo lớp dẫn xuất, ta mô tả một lời gọi tới hàm tạo trong lớp cơ sở. Cú pháp để truyền đối số từ lớp dẫn xuất đến lớp cơ sở như sau:

Tên_ lớp_dẫn_xuất(danh sách đối):Tên_ lớp_cơ_sở (danh sách đối)
{
//thân hàm tạo của lớp dẫn xuất
} ;

Trong phần lớn các trường hợp, hàm tạo của lớp dẫn xuất và hàm tạo của lớp cơ sở sẽ không dùng đối số giống nhau. Trong trường hợp cần truyền một hay nhiều đối số cho mỗi lớp, ta phải truyền cho hàm tạo của lớp dẫn xuất tất cả các đối số mà cả hai lớp dẫn xuất và cơ sở cần đến. Sau đó, lớp dẫn xuất chỉ truyền cho lớp cơ sở những đối số nào mà lớp cơ sở cần.

Ví dụ Chương trình sau minh họa cách truyền đối số cho hàm tạo của lớp cơ sở

Chú ý: Các đối số mà hàm tạo của lớp dẫn xuất truyền cho hàm tạo của lớp cơsở không nhất thiết phải lấy hoàn toàn y như từ các tham số nó nhận được. Ví dụ:

Hinhtron(double x1,double y1,double r1):Diem (x1/2, y1/2)

Hàm hủy đối với tính kế thừa

Hàm hủy của lớp cơ sở cũng không được kế thừa. Khi cả lớp cơ sở và lớp dẫn xuất có các hàm hủy và hàm tạo, các hàm tạo thi hành theo thứ tự dẫn xuất. Các hàm hủy được thi hành theo thứ tự ngược lại. Nghĩa là, hàm tạo của lớp cơ sở thi hành trước hàm tạo của lớp dẫn xuất, hàm hủy của lớp dẫn xuất thi hành trước hàm hủy của lớp cơ sở.

Ví dụ minh họa

Ngoài ra bạn có thể tham khảo thêm Inheritance in C++

Kẻ đứng sau blog này là cậu sinh viên năm 3. Là người khá hài hước và ham học hỏi, vì khá lười nên mình viết blog này để chia sẻ, lưu trữ lại những gì đã học được thời gian qua

Viết một bình luận