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óm tắt 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: ...với 1 toán hạng/đối số 15 SỬ DỤNG NẠP CHỒNG TOÁN TỬ “-”  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.output(); amount3 = -amount1;  => Gọi hàm nạp chồng toán tử 1 ngôi “-” 16 NẠP CHỒ...g 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 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ử ...8 GIAO DIỆN LỚP HOURLYEMPLOYEE  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ỉ...

pdf50 trang | Chia sẻ: havih72 | Lượt xem: 376 | Lượt tải: 0download
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, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
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 
Nạp chồng toán tử (Operator Overloading) 
và Hàm bạn (Friend Functions) 
Kế thừa (Inheritance) 
2 
Bài giảng có sử dụng hình vẽ trong cuốn sách “Practical Debugging in C++, 
A. Ford and T. Teorey, Prentice Hall, 2002” 
1. NẠP CHỒNG TOÁN TỬ 
VÀ HÀM BẠN 
Operator Overloading and Friend Functions 
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 
4 
LỚP MONEY 
5 
GIỚI THIỆU NẠP CHỒNG TOÁN TỬ 
 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ố 6 
TẠI SAO DÙNG NẠP CHỒNG TOÁN TỬ? 
 Những toán tử được xây dựng sẵn (built-in 
operators) 
 Ví dụ, +, -, = , %, ==, /, * 
 Đã thao tác được với các kiểu dựng sẵn của C++ (built-in 
types) 
 Nhưng nếu chúng ta muốn thực hiện phép + với 2 
đối tượng của lớp SinhVien ?, giống như: 
sinh_vien1 + sinh_vien2; 
 Chúng ta có thể nạp chồng những toán tử 
này! 
 Để thao tác với kiểu của chúng ta! 
 7 
CƠ BẢN VỀ NẠP CHỒNG 
 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 
8 
NẠP CHỒNG “+” 
 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, ) 
9 
VÍ DỤ ĐỊNH NGHĨA NẠP CHỒNG TOÁN TỬ 
“+” CHO LỚP MONEY 
10 
NẠP CHỒNG “==” 
 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 
11 
HÀM TẠO TRẢ VỀ ĐỐI TƯỢNG 
 Nhớ lại: hàm tạo là một hàm “void” (không có giá 
trị trả lại) 
 Là hàm đặc biệt với những thuộc tính đặc biệt 
 Tại sao trong cài đặt nạp chồng toán tử “+” phía 
trên lại có phần trả về giá trị?: 
 return Money (finalDollars, finalCents)? 
 Trả về một “lời gọi” (invocation) của lớp Money 
 Vì thế hàm tạo có thể “trả về” một đối tượng! 
 Được gọi là “đối tượng vô danh” (anonymous object) 
12 
SỬ DỤNG CONST 
TRONG NẠP CHỒNG TOÁN TỬ 
 Nhìn lại nạp chồng toán tử “+”: 
 const Money operator +(const Money& amount1, 
 const Money&amount2); 
 Tại sao lại trả về một “đối tượng Money constant” ? 
 Ảnh hưởng của việc trả về một đối tượng “non-const”, ví dụ: 
 Money operator +(const Money& amount1, 
 const Money& amount2); 
 Xem xét biểu thức m1 + m2, với m1, m2 là 2 đối tượng của lớp 
Money 
 Kết quả trả về là một đối tượng của lớp Money => có thể thực 
hiện các thao tác như gọi hàm thành viên 
 (m1+m2).output(); //Hợp lệ và Không có vấn đề gì do không 
thay đổi dữ liệu 
 (m1+m2).input(); // Hợp lệ nhưng phát sinh VẤN ĐỀ do thay 
đổi dữ liệu 
 Cho phép thay đổi trên đối tượng vô danh => Không mong 
muốn 
 Vì thế nên định nghĩa đối tượng trả về với const 
13 
NẠP CHỒNG TOÁN TỬ MỘT NGÔI 
 C++ sử dụng một số toán tử một ngôi (unary 
operators) – 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 -- 
14 
NẠP CHỒNG TOÁN TỬ “-” CHO LỚP MONEY 
 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 
(toán tử một ngôi) 
 Đị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 (binary operator), với 2 toán hạng/đối 
số 
 Khi nó là toán tử 1 ngôi (unary operator), với 1 toán hạng/đối 
số 15 
SỬ DỤNG NẠP CHỒNG TOÁN TỬ “-” 
 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.output(); 
amount3 = -amount1; 
 => Gọi hàm nạp chồng toán tử 1 ngôi “-” 
16 
NẠP CHỒNG TOÁN TỬ NHƯ HÀM THÀNH 
VIÊN (1/2) 
 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 
17 
NẠP CHỒNG TOÁN TỬ NHƯ HÀM THÀNH 
VIÊN (2/2) 
 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 
 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ố 
18 
NẠP CHỒNG MỘT SỐ TOÁN TỬ KHÁC 
 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: ++, -- (increment and 
decrement operators) 
 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ử >>, << 
19 
NẠP CHỒNG TOÁN TỬ >> VÀ << 
 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ụ sẽ viết: 
 cout << myObject; 
 cin >> myObject; 
 Thay vì phải viết: 
myObject.output(); 
myObject.input(); 
20 
TOÁN TỬ CHÈN << 
(INSERTION OPERATOR) (1/2) 
 Đượ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() 
21 
TOÁN TỬ CHÈN << 
(INSERTION OPERATOR) (2/2) 
 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; 
22 
VÍ DỤ CHƯƠNG TRÌNH NẠP CHỒNG TOÁN TỬ 
> (1/5) 
23 
VÍ DỤ CHƯƠNG TRÌNH NẠP CHỒNG TOÁN TỬ 
> (2/5) 
24 
VÍ DỤ CHƯƠNG TRÌNH NẠP CHỒNG TOÁN TỬ 
> (3/5) 
25 
VÍ DỤ CHƯƠNG TRÌNH NẠP CHỒNG TOÁN TỬ 
> (4/5) 
26 
VÍ DỤ CHƯƠNG TRÌNH NẠP CHỒNG TOÁN TỬ 
> (5/5) 
27 
HÀM BẠN (FRIEND FUNCTIONS) 
 Không phải hàm thành viên của lớp 
 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 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 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 
28 
LỚP BẠN (FRIEND CLASSES) 
 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 
29 
TÓM TẮT NẠP CHỒNG TOÁN TỬ VÀ HÀM 
BẠN 
 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 
30 
2. KẾ THỪA 
Inheritance 
MỤC TIÊU 
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) 
32 
GIỚI THIỆU VỀ KẾ THỪA 
 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 
33 
CƠ BẢN VỀ KẾ THỪA 
 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) 
34 
LỚP THỪA KẾ (DERIVED CLASSES) 
 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 35 
ĐỊNH NGHĨA LẠI HÀM THÀNH VIÊN 
 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 (check) khác nhau 
36 
VÍ DỤ GIAO DIỆN CHO LỚP THỪA KẾ 
HOURLYEMPLOYEE (1/2) 
37 
VÍ DỤ GIAO DIỆN CHO LỚP THỪA KẾ 
HOURLYEMPLOYEE (2/2) 
38 
GIAO DIỆN LỚP HOURLYEMPLOYEE 
 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() 
39 
ĐỊNH NGHĨA LẠI HÀM THÀNH VIÊN 
TRONG LỚP HOURLYEMPLOYEE 
 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 
40 
TRUY XUẤT HÀM ĐỊNH NGHĨA LẠI 
 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! 
41 
HÀM TẠO TRONG LỚP THỪA KẾ (1/2) 
 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) 
{} 
42 
HÀM TẠO TRONG LỚP THỪA KẾ (2/2) 
 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) 
{ } 
43 
LƯU Ý: DỮ LIỆU PRIVATE CỦA LỚP CƠ SỞ 
 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! 
44 
LƯU Ý: HÀM THÀNH VIÊN PRIVATE CỦA LỚP 
CƠ SỞ 
 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 
45 
KHU VỰC PROTECTED 
 Một phân loại (classification) / 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 
46 
ĐA KẾ THỪA (MULTIPLE INHERITANCE) 
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 {} 
47 
BÀI TẬP 
 Đị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 (SalariedEmployees) 
 Nhân viên bán thời gian, theo giờ (HourlyEmployees) 
 Định nghĩa lại hàm printCheck() riêng của hai lớp con 
48 
TÓM TẮT VỀ KẾ THỪA 
 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 
49 
GIÁO TRÌNH THAM KHẢO 
 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 
50 

File đính kèm:

  • pdfbai_giang_ngon_ngu_lap_trinh_bai_6_nap_chong_toan_tu_va_ke_t.pdf
Ebook liên quan