Thừa kế
(Inheritance)
GV: Phạm Văn Tùng
Bộ môn: KHMT & CNPM
Nội dung
•
Giới thiệu về thừa kế
•
Lớp dẫn xuất với hàm tạo
•
Bổ từ protected
•
Định nghĩa lại hàm thành viên
•
Các thành viên không được thừa kế
Sử dụng lại mã nguồn
•
Tồn tại nhiều lớp với tập các thuộc tính và phương
thức tương tự nhau hoặc liên quan đến nhau
–
Person, Student, Manager
•
Xuất hiện nhu cầu sử dụng lại các mã nguồn đã viết
–
Thông qua copy
–
Thông qua cơ chế thừa kế
Sử dụng lại mã nguồn (tiếp)
•
Thông qua copy
–
Tốn công copy, dễ nhầm lẫn
–
Khó sửa lỗi do tồn tại nhiều phiên bản
•
Thông qua thừa kế
–
Kĩ thuật lập trình rất mạnh và mềm dẻo
Giới thiệu về thừa kế
•
Thừa kế
–
Chúng ta có thể tạo ra một lớp khác từ một lớp đã có
sẵn mà không cần viết lại (copy lại) mã.
–
Lớp mới được thừa hưởng các thuộc tính (biến thành
viên) và các phương thức (hàm thành viên) của lớp đã
có sẵn
•
Trong lớp mới có thể định nghĩa thêm các thuộc tính
mới
•
Thêm hoặc chỉnh sửa các hàm thành viên sao cho phù
hợp
Thừa kế - các thuật ngữ
•
Lớp cơ sở (Còn gọi là lớp cha)
–
Là lớp dùng để tạo nên lớp khác
•
Lớp dẫn xuất (Còn gọi là lớp con)
–
Là một lớp mới được tạo ra từ lớp cơ sở
–
Tự động có các thành viên của lớp cha
•
Biến thành viên
•
Hàm thành viên
–
Có thể có thêm các biến thành viên hoặc hàm thành
viên của riêng nó
Thừa kế - ví dụ
•
Xét ví dụ: Lớp Employee biểu diễn các nhân viên
Thừa kế - ví dụ (tiếp)
Thừa kế - ví dụ (tiếp)
•
Cần xây dựng lớp HourlyEmployee để biểu diễn các
nhân viên hưởng lương theo giờ
–
Sử dụng cơ chế thừa kế
Thừa kế - ví dụ (tiếp)
Thừa kế - ví dụ (tiếp)
•
Chú ý dòng
class HourlyEmployee : public Employee
–
Chỉ rõ lớp HourlyEmployee thừa kế từ lớp
Employee
•
Trong lớp dẫn xuất chúng ta chỉ liệt kê thêm các
thành viên mới hoặc các hàm thành viên “cần định
nghĩa lại”
Thừa kế - ví dụ (tiếp)
•
Lớp HourlyEmployee có những biến thành viên:
–
Các biến thành viên của lớp Employee: name,
ssn, netPay
–
Các biến được định nghĩa thêm: wageRate, hours
•
Tương tự như vậy lớp HourlyEmployee có các
hàm thành viên bao gồm:
–
Các hàm thành viên được thừa kế từ lớp Employee:
getName, setName, getSsn,…
–
Các hàm thành viên được thêm mới: getRate,
setRate, getHours, setHours,…
Thừa kế - định nghĩa lại hàm thành
viên
•
Trong lớp dẫn xuất chúng ta có thể định nghĩa lại
hàm thành viên được thừa kế từ lớp cơ sở
–
Làm cho hàm đấy phù hợp với lớp dẫn xuất
–
Trong định nghĩa của lớp dẫn xuất phải liệt kê ra hàm
muốn định nghĩa lại
Ví dụ: Hàm printCheck() được định nghĩa lại trong
lớp dẫn xuất
Định nghĩa lại so với nạp chồng
•
Hai khái niệm này hoàn toàn khác nhau
•
Định nghĩa lại hàm trong lớp dẫn xuất
–
Danh sách tham số không thay đổi
–
Chỉ là viết lại định nghĩa hàm đấy cho phù hợp
•
Nạp chồng
–
Danh sách tham số khác nhau
–
Định nghĩa nên một hàm hoàn toàn mới
Truy cập đến hàm thành viên được
định nghĩa lại của lớp cơ sở
•
Khi hàm thành viên của lớp cơ sở được định nghĩa lại
trong lớp dẫn xuất thì thực chất nó không bị mất đi
•
Có thể sử dụng được hàm thành viên đấy của lớp cơ
sở thông qua đối tượng của lớp dẫn xuất
Truy cập đến hàm thành viên được
định nghĩa lại của lớp cơ sở
•
Employee JaneE;
HourlyEmployee SallyH;
JaneE.printCheck(); Gọi hàm printCheck
của lớp Employee
SallyH.printCheck(); Gọi hàm printCheck
của lớp HourlyEmployee
SallyH.Employee::printCheck(); Gọi hàm
printCheck của lớp Employee!
Hàm tạo của lớp dẫn xuất
•
Lớp dẫn xuất KHÔNG kế thừa hàm tạo của lớp cơ sở
–
Phải khởi tạo một cách tường minh cho các biến thành
viên kế thừa từ lớp cơ sở
–
Chúng ta có thể gọi hàm tạo của lớp cơ sở bên trong
hàm tạo của lớp dẫn xuất
•
Hàm tạo của lớp cơ sở nên khởi tạo cho tất cả các
biến thành viên lớp cơ sở
–
Hàm tạo của lớp dẫn xuất chỉ đơn giản gọi hàm tạo
của lớp cơ sở.
Hàm tạo của lớp dẫn xuất
•
Xem xét cú pháp hàm tạo của lớp HourlyEmployee
HourlyEmployee::HourlyEmployee(string
theName, string theNumber, double
theWageRate, double theHours)
: Employee(theName, theNumber),
wageRate(theWageRate), hours(theHours)
{
//Thân hàm tạo rỗng
}
•
Nhớ lại rằng phần sau dấu : được gọi là mục khởi tạo
–
Có chứa lời gọi đến hàm tạo lớp cơ sở
Hàm tạo lớp dẫn xuất (tiếp)
•
Xét một hàm tạo khác của lớp HourlyEmployee
HourlyEmployee::HourlyEmployee():
Employee(), wageRate(0), hours(0)
{
//Deliberately empty
}
•
Hàm tạo mặc định của lớp cơ sở được gọi
•
Trong hàm tạo của lớp dẫn xuất luôn gọi một hàm tạo
của lớp cơ sở.
Hàm tạo của lớp dẫn xuất (tiếp)
•
Trong hàm tạo lớp dẫn xuất luôn gọi một hàm tạo lớp
cơ sở
•
Nếu chúng ta không gọi tường minh
–
Hàm tạo mặc định của lớp cơ sở sẽ được tự động gọi
–
Định nghĩa của hàm tạo mặc định lớp
HourlyEmployee trong slide bên tương đương với:
HourlyEmployee::HourlyEmployee()
: wageRate(0), hours(0)
{ }
Cạm bẫy: Biến thành viên
private của lớp cơ sở
•
Lớp dẫn xuất thừa kế các biến thành viên private của
lớp cơ sở
–
Nhưng ta vẫn KHÔNG THỂ truy cập trực tiếp đến
các biến này trong lớp dẫn xuất
–
Phải truy cập thông qua các hàm truy cập và biến đổi
•
Biến thành viên private CHỈ có thể được truy cập
trực tiếp trong các hàm thành viên của lớp mà chứa
biến private đó
Hàm private
•
Hàm thành viên của lớp cũng có thể là private
–
Thường là các hàm mang tính trợ giúp cho các hàm
thành viên khác trong lớp
–
Chỉ có các hàm thành viên của lớp đó mới có thể gọi
được các hàm private này.
•
Trong lớp dẫn xuất cũng kế thừa các hàm private từ
lớp cơ sở
–
Tuy nhiên cũng như với biến private, ta không thể truy
cập trực tiếp đến các hàm private này bên ngoài lớp
Bổ từ protected
•
Là một bổ từ qui định mức độ truy cập giống như
private và public
–
Cho phép truy cập trực tiếp đến các thành viên khai
báo là protected trong lớp dẫn xuất
–
Tuy nhiên ngoài lớp cơ sở và lớp dẫn xuất thì không
thể truy cập trực tiếp đến các thành viên protected
được
–
Các thành viên protected cho cho phép thừa kế về sau
Bổ từ protected – ví dụ
Quan hệ “là một” và “có một”
•
Thừa kế
–
Được xem là mối quan hệ “là một”
–
Ví dụ: Một HourlyEmployee “là một” Employee
•
Một lớp chứa biến thành viên là đối tượng thuộc lớp
khác
–
Được xem là mối quan hệ “có một”
–
Ví dụ: Một AirpPlane (máy bay) có một
JetEngine (động cơ)