Bài giảng Ngôn ngữ lập trình - Bài 6: Nạp chồng toán tử và kế thừa - Lê Nguyễn Tuấn Thành

Mục tiêu

 Nạp chồng toán tử cơ bản

 Toán tử hai ngôi (binary operators)

 Toán tử một ngôi (unary operators)

 Nạp chồng bằng hàm thành viên

 Hàm bạn và Lớp bạn

pdf 48 trang yennguyen 4200
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Ngôn ngữ lập trình - Bài 6: Nạp chồng toán tử và kế thừa - Lê Nguyễn Tuấn Thành", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Ngôn ngữ lập trình - Bài 6: Nạp chồng toán tử và kế thừa - Lê Nguyễn Tuấn Thành

Bài giảng Ngôn ngữ lập trình - Bài 6: Nạp chồng toán tử và kế thừa - Lê Nguyễn Tuấn Thành
Ngôn ngữ lập trình 
Bài 6: 
Nạp Chồng Toán Tử 
và Kế Thừa 
Giảng viên: Lê Nguyễn Tuấn Thành 
Email: thanhlnt@tlu.edu.vn 
Bộ Môn Công Nghệ Phần Mềm – Khoa CNTT 
Trường Đại Học Thủy Lợi 
Nội dung 
2 
 Nạp chồng toán tử (Operator Overloading) và Hàm 
bạn (Friend Functions) 
 Kế thừa (Inheritance) 
Bài giảng có sử dụng hình vẽ trong cuốn sách “Absolute C++. W. Savitch, Addison Wesley, 2002” 
1. Nạp chồng toán tử 
và Hàm bạn 
Operator Overloading and Friend Functions 
Mục tiêu 
4 
 Nạp chồng toán tử cơ bản 
 Toán tử hai ngôi (binary operators) 
 Toán tử một ngôi (unary operators) 
 Nạp chồng bằng hàm thành viên 
 Hàm bạn và Lớp bạn 
L
ớ
p
 M
o
n
e
y
5 
Giới thiệu nạp chồng toán tử 
6 
 Những toán tử như +,-, %, == etc. thực ra là những hàm! 
 Các hàm đặc biệt này được gọi với cú pháp khác so với 
cách gọi hàm thông thường 
 Gọi hàm thông thường: 
 Tên_Hàm (Danh_Sách_Đối_Số) 
 Với toán tử: ví dụ, x + 7, “+” là một toán tử 2 ngôi (binary 
operator) với x, 7 là 2 toán hạng (operands) 
 Thử viết theo cách gọi hàm thông thường: +(x,7) 
 “+” là tên hàm 
 x, 7 là tham số của hàm 
 Hàm “+” trả lại giá trị là tổng của 2 đối số 
Tại sao dùng nạp chồng toán tử? 
7 
 Những toán tử được xây dựng sẵn 
 Ví dụ, +, -, = , %, ==, /, * 
 Đã thao tác được với các kiểu dựng sẵn của C++ 
 Nhưng liệu chúng ta có thể thực hiện phép + với 2 
đối tượng của lớp Money?, giống như: 
money1 + money2; 
 Để làm được điều này, chúng ta phải nạp chồng 
những toán tử này cho lớp Money! 
Cơ bản về nạp chồng 
8 
 Nạp chồng toán tử 
 Tương tự như với nạp chồng hàm 
 Toán tử bản thân nó là tên của hàm 
 Ví dụ khai báo 
 const Money operator + (const Money& amount1, 
 const Money& amount2); 
 Nạp chồng toán tử + với toán hạng là đối tượng kiểu 
Money 
 Giá trị trả lại là một kiểu Money 
 Mục đích: cho phép thực hiện phép + trên hai đối tượng 
của lớp Money 
Nạp chồng toán tử “+” 
9 
 const Money operator + (const Money& amount1, 
 const Money& amount2); 
 Chú ý: hàm nạp chồng toán tử “+” này không phải hàm 
thành viên của lớp Money 
 Định nghĩa, cài đặt của hàm này phức tạp hơn so với phép 
cộng thông thường (phải tính đến biến thành viên, kiểm tra giá 
trị âm/dương, ) 
Định nghĩa nạp chồng toán tử “+” cho lớp Money 
10 
Nạp chồng toán tử “==” 
11 
 Toán tử so sánh bằng “==” 
 Cho phép so sánh các đối tượng của lớp Money 
 Khai báo: 
 bool operator ==(const Money& amount1, 
 const Money& amount2); 
 Hàm này cũng không phải là hàm thành viên của lớp Money 
Nạp chồng toán tử một ngôi 
(Unary operators) 
12 
 Toán tử một ngôi: chỉ có một toán hạng 
 Toán tử phủ định (negation) “-” 
X = -Y // đặt X bằng giá trị phủ định của Y 
 Toán tử tăng ++ 
 Toán tử giảm -- 
Nạp chồng toán tử “-” cho lớp Money 
13 
 Khai báo hàm nạp chồng toán tử “-” cho lớp Money 
 const Money operator –(const Money& amount); 
 Không phải hàm thành viên của lớp 
 Chú ý: chỉ có một đối số, do toán tử này chỉ có một toán hạng 
 Định nghĩa hàm nạp chồng toán tử một ngôi “-” 
 const Money operator –(const Money& amount) 
{ 
 return Money(-amount.getDollars(), -amount.getCents()); 
} 
 Trả lại một đối tượng vô danh (anonymous object) 
 Lưu ý: nạp chồng toán tử “-” có hai trường hợp! 
 Khi nó là toán tử 2 ngôi, với 2 toán hạng/đối số 
 Khi nó là toán tử 1 ngôi, với 1 toán hạng/đối số 
Sử dụng nạp chồng toán tử “-” 
14 
 Xét ví dụ sau: 
 Money amount1(10), amount2(6), amount3; 
amount3 = amount1 – amount2; 
 => Gọi nạp chồng toán tử 2 ngôi “-” 
amount3 = -amount1; 
 => Gọi hàm nạp chồng toán tử 1 ngôi “-” 
Nạp chồng toán tử như hàm thành viên (1/2) 
15 
 Những ví dụ ở trước: các hàm đứng độc lập không phải 
thành viên của lớp 
 Có thể nạp chồng như “toán tử thành viên”, được xem 
như hàm thành viên 
 Khi toán tử là hàm thành viên 
 Chỉ có MỘT tham số, không phải có 2 tham số! 
 Được tượng được gọi (phía sau toán tử) được xem là tham 
số duy nhất 
Nạp chồng toán tử như hàm thành viên (2/2) 
16 
 Ví dụ: 
Money cost(1, 50), tax(0, 15), total; 
total = cost + tax; 
 Nếu toán tử “+” được nạp chồng như toán tử thành 
viên thì: 
 Biến/ đối tượng cost là đối tượng gọi hàm nạp chồng 
 Đối tượng tax là tham số duy nhất của hàm nạp chồng 
 Tưởng tượng giống như cách viết sau 
 total = cost.+(tax); 
 Khai báo của toán tử “+” trong định nghĩa lớp 
 const Money operator +(const Money& amount); 
 Chú ý CHỈ CÓ MỘT đối số 
Nạp chồng một số toán tử khác 
17 
 Toán tử gọi hàm: () 
 Toán tử &, ||, dấu phẩy 
 Toán tử gán = (assignment operator), phải được nạp 
chồng như hàm thành viên! 
 Toán tử tăng, giảm: ++, -- 
 Mỗi toán tử có 2 phiên bản: 
 Tiền tố (prefix notation): ++x; 
 Hậu tố (postfix notation): x++; 
 Toán tử mảng [ ], nạp chồng như hàm thành viên! 
 Toán tử >>, << 
Nạp chồng toán tử >> và << 
18 
 Cho phép nhập và xuất dữ liệu cho đối tượng 
 Tăng tính dễ đọc cho chương trình 
 Ví dụ chúng ta sẽ viết: 
 cout << myObject; 
 cin >> myObject; 
 Thay vì phải viết: 
myObject.output(); 
myObject.input(); 
Toán tử chèn << (1/2) 
(Insertion operator) 
19 
 Được sử dụng với cout, ví dụ: cout << "Hello"; 
 Là toán tử hai ngôi: 
 Toán hạng đầu tiên là đối tượng được định nghĩa sẵn cout, từ 
thư viện iostream 
 Toán hạng thứ hai là dữ liệu/đối tượng cần in ra màn hình 
 Giả sử khai báo: Money amount(100); 
 Nếu chúng ta đã nạp chồng toán tử << với lớp Money, 
chúng ta có thể viết: 
 cout << "I have " << amount << endl; 
 thay vì sử dụng hàm thành viên output() và viết: 
 cout << "I have "; 
amount.output() 
Toán tử chèn << (2/2) 
(Insertion operator) 
20 
 Nạp chồng << nên trả về giá trị 
 Giá trị nào được trả về? 
 Đối tượng cout ! 
 Trả về kiểu của đối số đầu tiên, ostream 
 Hai cách viết sau là tương đương: 
 cout << "I have " << amount; 
(cout << "I have ") << amount; 
Chương trình nạp chồng toán tử > (1/5) 
21 
Chương trình nạp chồng toán tử > (2/5) 
22 
Chương trình nạp chồng toán tử > (3/5) 
23 
Chương trình nạp chồng toán tử > (4/5) 
24 
Chương trình nạp chồng toán tử > (5/5) 
25 
Hàm bạn 
(Friend functions) 
26 
 Nhớ lại: 
 Nạp chồng toán tử có thể không phải hàm thành viên 
 Khi đó, truy xuất dữ liệu phải thông qua các hàm accessor và 
mutator 
 Cách làm này không hiệu quả (tăng phụ phí khi gọi các hàm 
này!) 
 Hàm bạn Không phải hàm thành viên của lớp nhưng có 
thể truy xuất trực tiếp đến các dữ liệu trong khu vực 
private của lớp 
 Không có phụ phí khi gọi hàm => hiệu quả hơn 
 Vì vậy: cách tốt nhất là cài đặt nạp chồng toán tử là khai 
báo chúng như các hàm bạn 
 Sử dụng từ khóa friend ở trước khai báo hàm 
Lớp bạn 
(Friend classes) 
27 
 Toàn bộ một lớp có thể là bạn của một lớp khác 
 Tương tự như một hàm là bạn trong một lớp 
 Nếu lớp F là bạn của lớp C => tất cả hàm thành viên 
của lớp F đều là bạn của lớp C 
 Điều ngược lại không đúng 
 Cú pháp: friend class F 
Tóm tắt nạp chồng toán tử và hàm bạn 
28 
 Những toán tử dựng sẵn (built-in) trong C++ có thể 
được nạp chồng để thao tác với đối tượng của lớp mà 
bạn định nghĩa 
 Toán tử thực ra là những hàm! 
 Toán tử có thể được nạp chồng như hàm ngoài (không 
phải thành viên) hoặc hàm thành viên của lớp 
 Toán hạng đầu tiên là đối tượng gọi 
 Hàm bạn truy xuất trực tiếp được các thành viên trong 
khu vực private 
2. Kế thừa 
Inheritance 
Mục tiêu 
30 
 Cơ bản về kế thừa (inheritance) 
 Lớp thừa kế (derived classes), với hàm tạo 
 Khu vực Protected 
 Định nghĩa lại hàm thành viên 
 Hàm không kế thừa 
 Chương trình với kế thừa 
 Toán tử gán và hàm tạo 
 Đa kế thừa (multiple inheritance) 
Giới thiệu về kế thừa 
31 
 Thế nào là kế thừa? Định nghĩa? 
 Một kỹ thuật lập trình mạnh, khái niệm trừu tượng 
 Cấu trúc tổng quát về một khái niệm được định nghĩa 
trong một lớp (lớp cha / lớp cơ sở) 
 Những phiên bản chuyên biệt (lớp con) sau đó kế thừa thuộc 
tính của lớp tổng quát đó 
 Lớp con có thể mở rộng hay thay đổi chức năng cho phù hợp 
Cơ bản về kế thừa 
32 
 Một lớp mới được kế thừa từ một lớp khác 
 Lớp cơ sở (lớp cha) 
 Lớp tổng quát mà từ đó các lớp khác sẽ kế thừa 
 Lớp thừa kế (lớp con) 
 Một lớp mới 
 Tự động có những hàm/biến thành viên của lớp cơ sở 
 Sau đó có thể thêm những hàm/biến thành viên mới 
 Thuật ngữ (terminology) về kế thừa giống như quan hệ 
gia đình 
 Lớp cha (Parent class) ~ Lớp cơ sở (Base class) 
 Lớp con (Child class) ~ Lớp thừa kế (Derived class) 
 Lớp tổ tiên (Ancestor class) 
 Lớp con cháu (Descendant class) 
Lớp thừa kế 
(Derived classes) 
33 
 Xét ví dụ về lớp Nhân_Viên (Employee) 
 Có thể bao gồm nhiều loại nhỏ: 
 Nhân viên được trả lương (Salaried employees) 
 Nhân viên bán thời gian, theo giờ (Hourly employees) 
  
 Khái niệm tổng quát về Nhân_Viên là hữu ích! Được định 
nghĩa trước như một khung chung: 
 Tất cả nhân viên đều có những thông tin chung như: Tên, Tuổi, 
Giới Tính, Quốc Tịch, CMTND 
 Các hàm thành viên liên quan đến những dữ liệu này là giống 
nhau (cơ sở) cho tất cả nhân viên 
 Ví dụ: các hàm accessor, mutator 
Định nghĩa lại hàm thành viên 
34 
 Xét hàm printCheck() của lớp cơ sở Nhân_Viên 
 Được định nghĩa lại trong các lớp thừa kế 
 Do các loại nhân viên khác nhau có thể có các kiểm tra 
khác nhau 
Giao diện cho lớp thừa kế HourlyEmployee (1/2) 
35 
Giao diện cho lớp thừa kế HourlyEmployee (2/2) 
36 
Giao diện lớp HourlyEmployee 
37 
 Lưu ý phần đầu chương trình 
 Cấu trúc #ifndef 
 Khai báo bao gồm (include) các thư viện liên quan 
 Khai báo bao gồm lớp cơ sở employee.h! 
 Khai báo cấu trúc kế thừa 
 class HourlyEmployee : public Employee 
 Giao diện (interface) của lớp thừa kế chỉ liệt kê những thành 
viên mới hoặc sẽ được định nghĩa lại 
 Bởi vì tất cả những thành viên khác kế thừa từ lớp cơ sở đã được 
định nghĩa trước đó! 
 Lớp HourlyEmployee thêm các thành viên sau: 
 Hàm khởi tạo 
 Biến thành viên: wageRate, hours 
 Hàm thành viên: setRate(), getRate(), setHours(), getHours() 
Định nghĩa lại hàm thành viên 
trong lớp HourlyEmployee 
38 
 Lớp HourlyEmployee định nghĩa lại: 
 Hàm thành viên printCheck() của lớp cơ sở 
 Phiên bản mới của hàm printCheck() sẽ “ghi đè” (overrides) phiên 
bản cũ đã được cài đặt trong lớp cơ sở Employee 
 Cài đặt của hàm thành viên này phải được thực hiện trong 
lớp HourlyEmployee 
 Định nghĩa lại hàm khác nạp chồng hàm thế nào? 
 Rất khác nhau 
 Định nghĩa lại hàm trong lớp thừa kế 
 CÙNG danh sách tham số 
 Thực chất là viết lại cùng một hàm 
 Nạp chồng hàm 
 Danh sách tham số khác nhau 
 Định nghĩa một hàm mới với tham số khác 
Truy xuất hàm định nghĩa lại 
39 
 Khi được định nghĩa lại một hàm trong lớp con, định 
nghĩa của hàm này trong lớp cơ sở không bị mất đi! 
 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 trong lớp thừa kế (1/2) 
40 
 Hàm tạo của lớp cơ sở không được kế thừa tự động 
trong lớp con ! 
 Nhưng chúng có thể được gọi bên trong hàm tạo của của lớp 
con! 
 Hàm tạo của lớp cơ sở nên khởi tạo tất cả các biến 
thành viên 
 Xét ví dụ 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) 
{} 
Hàm tạo trong lớp thừa kế (2/2) 
41 
 Nếu lớp con không gọi hàm tạo nào của lớp cơ sở: 
 Hàm tạo mặc định của lớp cơ sở tự động được gọi 
 Ví dụ: 
 HourlyEmployee::HourlyEmployee() 
 : wageRate(0), hours(0) 
{ } 
Lưu ý: dữ liệu private của lớp cơ sở 
42 
 Lớp con kế thừa biến thành viên trong khu vực private 
 Nhưng vẫn không thể truy xuất trực tiếp “theo tên” (by-
name) đến những biến thành viên này 
 Ngay cả truy xuất biến private thông qua các hàm thành viên 
của lớp con! 
 Biến thành viên private có thể CHỈ được truy xuất “theo 
tên” trong các hàm thành viên của lớp cơ sở mà chúng 
được định nghĩa! 
Lưu ý: hàm thành viên private của lớp cơ sở 
43 
 Không thể được truy xuất bên ngoài giao diện và cài đặt 
của lớp cơ sở 
 Ngay cả trong định nghĩa hàm thành viên của lớp con 
Khu vực protected 
44 
 Một khu vực mới cho thành viên của lớp 
 Cho phép truy xuất “theo tên” thành viên trong lớp thừa 
kế 
 Nhưng không cho phép truy xuất trong các lớp không kế 
thừa! 
 Trong lớp mà những thành viên protected này được định 
nghĩa, hoạt động giống như các thành viên private 
Đa kế thừa 
(Multiple inheritance) 
45 
 Lớp con có thể kế thừa nhiều hơn một lớp cơ sở! 
 Cú pháp: các lớp cơ sở được phân tách bằng dấu phẩy 
 Ví dụ: 
class derivedMulti : public base1, base2 {} 
Bài tập 
46 
 Định nghĩa lớp Nhân viên (Employee) 
 Private: Tên, Tuổi, Giới Tính, Quốc Tịch 
 Public: void printCheck() 
 Protected: CMTND 
 Định nghĩa hai lớp con kế thừa từ lớp Nhân_Viên 
 Nhân viên được trả lương (SalariedEmployee) 
 Nhân viên bán thời gian, theo giờ (HourlyEmployee) 
 Định nghĩa lại hàm printCheck() riêng của hai lớp con 
Tóm tắt về kế thừa 
47 
 Kế thừa cho phép sử dụng lại code 
 Cho phép một lớp kế thừa từ lớp khác và thêm các chức năng mới 
 Lớp con kế thừa những thành viên của lớp cơ sở và có thể 
thêm thành viên mới 
 Biến thành viên private trong lớp cơ sở không thể được truy 
xuất “theo tên” trong lớp con 
 Hàm thành viên private không được kế thừa, chỉ được sử 
dụng riêng ở lớp cơ sở 
 Có thể định nghĩa lại hàm thành viên của lớp cơ sở trong lớp 
con 
 Các lớp con khác nhau có thể có những định nghĩa khác nhau 
 Thành viên trong khu vực protected của lớp cơ sở có thể 
được truy xuất “theo tên” trong lớp con 
Giáo trình Tham khảo 
48 
 Giáo trình chính: W. Savitch, Absolute C++, Addison 
Wesley, 2002 
 Tham khảo: 
 A. Ford and T. Teorey, Practical Debugging in C++, Prentice Hall, 
2002 
 Nguyễn Thanh Thủy, Kĩ thuật lập trình C++, NXB Khoa học và 
Kĩ Thuật, 2006 

File đính kèm:

  • pdfbai_giang_ngon_ngu_lap_trinh_bai_6_nap_chong_toan_tu_va_ke_t.pdf