Bài giảng Lập trình nâng cao - Bài 8: Nạp chồng toán tử, từ khóa friend và tham chiếu - Hoàng Thị Điệp

pdf 48 trang hapham 1421
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình nâng cao - Bài 8: Nạp chồng toán tử, từ khóa friend và tham chiếu - Hoàng Thị Điệp", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfbai_giang_lap_trinh_nang_cao_bai_8_nap_chong_toan_tu_tu_khoa.pdf

Nội dung text: Bài giảng Lập trình nâng cao - Bài 8: Nạp chồng toán tử, từ khóa friend và tham chiếu - Hoàng Thị Điệp

  1. Bài 8: Nạp chồng toán tử, Từ khóa friend và Tham chiếu Giảng viên: Hoàng Thị Điệp Khoa Công nghệ Thông tin – ĐH Công Nghệ
  2. Chapter 8 Operator Overloading, Friends, and References Copyright © 2010 Pearson Addison-Wesley. All rights reserved
  3. Mục tiêu bài học • Căn bản về nạp chồng toán tử – Toán tử một ngôi – Nạp chồng dưới dạng hàm thành viên • Từ khóa friend và chuyển đổi kiểu tự động – Hàm friend, lớp friend – Hàm kiến tạo và chuyển đổi kiểu tự động • Tham chiếu và bàn thêm về nạp chồng – > – Các toán tử: = , [], ++, DTH INT2202
  4. Giới thiệu về nạp chồng toán tử • Các toán tử +, -, %, ==, v.v. – thực ra là các hàm! • Đơn giản là chúng được “gọi” bằng cú pháp khác: x + 7 – “+” là toán tử 2 ngôi với toán hạng là x và 7 – Con người “thích” kí hiệu này hơn • Hãy nghĩ về nó như là: +(x, 7) – “+” là tên hàm – x, 7 là đối số – Hàm “+” trả về “tổng” của các đối số của nó DTH INT2202
  5. Nạp chồng toán tử • Các toán tử có sẵn – Ví dụ: +, -, = , %, ==, /, * – Đã làm việc với các kiểu có sẵn của C++ – Với kí hiệu “hai ngôi” chuẩn • Ta có thể nạp chồng chúng – Để làm việc với kiểu dữ liệu của ta! – Để cộng “các biến Chair” hoặc “các biến Money” • Phù hợp với nhu cầu của ta • Theo “kí hiệu” mà ta quen dùng • Hãy nạp chồng bằng “công việc” tương tự! DTH INT2202
  6. Căn bản về nạp chồng • Nạp chồng toán tử – Có nhiều điểm tương tự với nạp chồng hàm – Toán tử chính 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 phép + cho 2 toán hạng kiểu Money – Sử dụng tham số tham chiếu hằng cho hiệu quả – Giá trị trả về có kiểu Money • Cho phép cộng các đối tượng "Money” DTH INT2202
  7. Phép “+” đã được nạp chồng • Từ ví dụ vừa rồi: – Lưu ý: phép cộng nạp chồng không phải là hàm thành viên – Định nghĩa trong Display 8.1 làm nhiều việc hơn phép cộng đơn thuần • Phần bắt buộc: xử lý việc cộng 2 đối tượng Money • Xử lý các giá trị âm/dương • Định nghĩa nạp chồng toán tử nhìn chung là đơn giản – Chỉ cần thực hiện “phép cộng” cho kiểu “của bạn” DTH INT2202
  8. Định nghĩa “+” cho Money Display 8.1 Nạp chồng toán tử • Định nghĩa phép “+” cho lớp Money DTH INT2202
  9. Phép “==“ nạp chồng • Phép so sánh bằng, == – Cho phép so sánh các đối tượng Money – Khai báo: bool operator ==( const Money& amount1, const Money& amount2); • Trả về kiểu bool cho đẳng thức đúng/sai – Đây cũng không phải là một hàm thành viên (giống như phép “+” nạp chồng) DTH INT2202
  10. Phép “==“ nạp chồng cho lớp Money: Display 8.1 Nạp chồng toán tử • Định nghĩa phép “==“ cho lớp Money: DTH INT2202
  11. Hàm kiến tạo trả về đối tượng • Hàm kiến tạo là hàm kiểu void? – Ta “nghĩ” như vậy nhưng không phải – Nó là một hàm “đặc biệt” • Với các tính chất đặc biệt • CÓ THỂ trả về một giá trị! • Nhắc lại: câu lệnh return trong phép “+” nạp chồng của kiểu Money: – return Money(finalDollars, finalCents); • Trả về một “lời gọi” tới lớp Money! • Suy ra hàm kiến tạo thực ra “có trả về” một đối tượng! • Gọi là “đối tượng vô danh” DTH INT2202
  12. Trả về giá trị const • Ta l ại xét việc nạp chồng phép “+”: const Money operator +( const Money& amount1, const Money& amount2); – Trả về một “đối tượng hằng”? – Vì sao? • Để hiểu được lý do, hãy xét ảnh hưởng của việc trả về đối tượng không chỉ định là const DTH INT2202
  13. Trả về một giá trị không chỉ định là const • Hãy xem xét khai báo không chỉ định trả về const: Money operator +( const Money& amount1, const Money& amount2); • Xét biểu thức gọi tới nó: m1 + m2 – Trong đó m1 & m2 là các đối tượng Money – Đối tượng trả về có kiểu Money – Ta có thể “thao tác trên” đối tượng trả về! • Ví dụ như gọi tới hàm thành viên DTH INT2202
  14. Thao tác trên đối tượng không chỉ định const • Có thể gọi các hàm thành viên: – Ta có thể gọi tới hàm thành viên trên đối tượng trả về bởi biểu thức m1+m2: • (m1+m2).output(); // Hợp lệ? – Đây không phải là vấn đề vì nó chẳng làm biến đổi giá trị vừa trả về • (m1+m2).input(); // Hợp lệ! – Đây là vấn đề! Hợp lệ nhưng biến đổi đổi giá trị vừa trả về! • Có thể biến đổi đối tượng “vô danh”! • Không cho phép điều đó ở đây! • Vì vậy ta định nghĩa đối tượng trả về là const DTH INT2202
  15. Nạp chồng toán tử một ngôi • C++ có toán tử một ngôi: – Là toán tử nhận một toán hạng – Ví dụ: phép phủ định - • x = -y; // Đặt x bằng âm y – Toán tử một ngôi khác: • ++, • Toán tử một ngôi cũng có thể bị nạp chồng DTH INT2202
  16. Nạp chồng phép phủ định “-“ cho kiểu Money • Khai báo hàm “-“ nạp chồng – Đặt ngoài định nghĩa lớp: const Money operator –(const Money& amount); – Chú ý là chỉ có một đối số • Vì phép toán một ngôi chỉ có 1 toán hạng • Phép “-” bị nạp chồng 2 lần! – Cho 2 toán hạng/đối số (hai ngôi) – Cho 1 toán hạng/đối số (một ngôi) – Phải cung cấp cả 2 định nghĩa DTH INT2202
  17. Định nghĩa nạp chồng “-” • Định nghĩa hàm “-” nạp chồng const Money operator –(const Money& amount) { return Money( -amount.getDollars(), -amount.getCents()); } • Áp dụng toán tử một ngôi “-” cho kiểu có sẵn – Phép toán này là “đã biết” với những kiểu có sẵn • Trả về đối tượng vô danh DTH INT2202
  18. Sử dụng phép “-” nạp chồng • Xét: Money amount1(10), amount2(6), amount3; amount3 = amount1 – amount2; • Sẽ gọi tới phép “-” nạp chồng hai ngôi amount3.output(); // Hiển thị $4.00 amount3 = -amount1; • Sẽ gọi tới phép “-” nạp chồng một ngôi amount3.output(); // Hiển thị -$10.00 DTH INT2202
  19. Nạp chồng dưới dạng hàm thành viên • Ví dụ trước: các hàm nạp chồng toán tử đứng độc lập – Định nghĩa bên ngoài lớp • Có thể nạp chồng dưới dạng “toán tử thành viên” – Được xem như “hàm thành viên” như các hàm khác • Khi toán tử là hàm thành viên: – Toán tử hai ngôi chỉ cần một tham số, không phải 2! – Đối tượng gọi giữ vai trò tham số thứ nhất DTH INT2202
  20. Hoạt động của toán tử thành viên • Money cost(1, 50), tax(0, 15), total; total = cost + tax; – Nếu “+” đượ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 • Đối tượng tax là đối số – Hãy nghĩ là: total = cost.+(tax); • Khai báo “+” trong định nghĩa lớp: – const Money operator +(const Money& amount); – Lưu ý là nó chỉ có một đối số DTH INT2202
  21. Hàm const • Khi nào thì chỉ định hàm là const? – Hàm thành viên const không được phép biến đổi dữ liệu thành viên của lớp – Đối tượng const chỉ có thể gọi tới hàm thành viên const • Phong cách lập trình tốt yêu cầu: – Bất cứ hàm thành viên nào không biến đổi dữ liệu thành viên cần được chỉ định là const • Sử dụng từ khóa const cuối khai báo hàm và dòng đầu hàm DTH INT2202
  22. Nạp chồng toán tử: Cách nào? • Lập trình hướng đối tượng – Các nguyên lý khuyến khích cài đặt toán tử thành viên – Được nhất trí nhiều. Duy trì “tinh thần” của LTHĐT • Toán tử thành viên thì hiệu quả hơn – Không cần gọi tới các hàm truy cập và hàm biến đổi • Ít nhất một điểm yếu quan trọng – (Sẽ bàn sau) DTH INT2202
  23. Nạp chồng ứng dụng hàm () • Toán tử gọi hàm, ( ) – Nếu nạp chồng, phải cài đặt dạng hàm thành viên – Cho phép dùng đối tượng của lớp như một hàm – Có thể nạp chồng cho số lượng đối số bất kì • Ví dụ: Aclass anObject; anObject(42); • Nếu nạp chồng ( ) gọi đến hàm nạp chồng đó DTH INT2202
  24. Các phép nạp chồng khác • &&, ||, và toán tử dấu phẩy – Phiên bản định nghĩa sẵn làm việc với kiểu bool – Nhắc lại: nó sử dụng “tính giá trị biểu thức kiểu đoản mạch” (short-circuit evaluation) – Khi nạp chồng, đoản mạch không còn hiệu lực • Thay vào đó “tính giá trị đầy đủ” được sử dụng • Trái với mong đợi • Nhìn chung, không nên nạp chồng các toán tử này DTH INT2202
  25. Hàm friend • Nạp chồng dưới dạng hàm ngoài lớp (không phải thành viên) – Nhắc lại: khi nạp chồng toán tử dưới dạng hàm ngoài lớp • Chúng truy cập dữ liệu thông qua các hàm thành viên truy cập và hàm thành viên biến đổi • Không hiệu quả (chi phí của các lời gọi) • Từ khóa friend giúp truy cập trực tiếp vào dữ liệu của lớp – Không chi phí, hiệu quả hơn • Do đó: tốt nhất là nạp chồng toán tử dưới dạng hàm ngoài lớp dùng từ khóa friend! DTH INT2202
  26. Hàm friend • Hàm friend của một lớp – Không phải là hàm thành viên – Được truy cập trực tiếp tới các thành viên private • Giống như các hàm thành viên • Dùng từ khóa friend trước khai báo hàm – Chỉ định TRONG định nghĩa lớp – Nhưng chúng không phải là hàm thành viên! DTH INT2202
  27. Sử dụng hàm friend • Nạp chồng toán tử – Cách dùng thường gặp nhất với friend – Cải thiện hiệu quả – Tránh phải gọi tới các hàm thành viên truy cập/biến đổi – Toán tử cần được truy cập • Có thể trao quyền truy cập toàn bộ như hàm friend • friend có thể là bất cứ hàm nào DTH INT2202
  28. Tính thuần túy của hàm friend • friend không thuần túy? – “Tinh thần“ của LTHĐT yêu cầu tất cả toán tử và hàm phải là hàm thành viên – Nhiều người nghĩ rằng friend vi phạm nguyên lý cơ bản của LTHĐT • Có ích? – Với toán tử: rất có ích! – Cho phép chuyển đổi kiểu tự động – Vẫn đóng gói: friend nằm trong định nghĩa lớp – Cải thiện hiệu quả DTH INT2202
  29. Lớp friend • Một lớp có thể là friend của lớp khác – Tương tự như việc hàm là friend của lớp – Ví dụ: lớp F là friend của lớp C • Tất cả các hàm thành viên của lớp F là friend của C • Không thuận nghịch (tức: chiều ngược lại không đúng) • Tình bạn có thể được ban tặng nhưng không thể đòi hỏi • Cú pháp: friend class F; – Nằm bên trong định nghĩa của lớp “cấp quyền” DTH INT2202
  30. Tham chiếu • Tham chiếu là: – Tên của nơi lưu trữ – Tương tự như “con trỏ” • Ví dụ về tham chiếu đứng độc lập: – int robert; int& bob = robert; • bob tham chiếu tới nơi lưu trữ cho robert • Biến đổi trên bob sẽ tác động tới robert • Dễ nhầm lẫn? DTH INT2202
  31. Sử dụng tham chiếu • Có vẻ nguy hiểm • Hữu ích trong một số trường hợp: • Truyền tham chiếu – Thường dùng để cài đặt cơ chế này • Trả về một tham chiếu – Cho phép cài đặt toán tử nạp chồng được tự nhiên hơn – Hãy nghĩ nó trả về “bí danh” của một biến DTH INT2202
  32. Trả về tham chiếu • Cú pháp: double& sampleFunction(double& variable); – double& khác double – Khai báo hàm và dòng đầu định nghĩa hàm phải khớp nhau • Thực thể được trả về phải “có” một tham chiếu – Như là một biến cùng kiểu – Không thể là một biểu thức, ví dụ “x+5” • Vì biểu thức không được cấp một nơi nào đó trong bộ nhớ để ta tham chiếu tới DTH INT2202
  33. Trả về tham chiếu trong định nghĩa • Ví dụ định nghĩa hàm: double& sampleFunction(double& variable) { return variable; } • Ví dụ không quan trọng, vô dụng • Chỉ để biểu diễn khái niệm • Ứng dụng chính: – Nạp chồng một số toán tử nhất định DTH INT2202
  34. Nạp chồng > • Cho phép ghi và đọc đối tượng dùng các luồng chuẩn – Tương tự như nạp chồng các toán tử khác – Có thêm một số điểm tinh vi • Dễ đọc hơn – Như tất cả các toán tử nạp chồng khác – Cho phép: cout > myObject; – Thay cho: myObject.output(); DTH INT2202
  35. Nạp chồng << • Toán tử chèn, << – Dùng với cout – Là toán tử hai ngôi • Ví dụ: cout << "Hello"; – Toán tử là << – Toán hạng thứ nhất là đối tượng định nghĩa sẵn cout • Từ thư viện iostream – Toán hạng thứ 2 là xâu hằng "Hello" DTH INT2202
  36. Nạp chồng << • Các toán hạng của << – Đối tượng cout kiểu ostream – Đối tượng của lớp ta định nghĩa • Nhắc lại: lớp Money – Dùng hàm thành viên output() – Tốt hơn nếu ta có thể dùng toán tử <<: Money amount(100); cout << "I have " << amount << endl; thay cho: cout << "I have "; amount.output() DTH INT2202
  37. Giá trị trả về của << nạp chồng • Money amount(100); cout << amount; – << nên trả về giá trị nào đó – Để cho phép hiện tượng thác lũ: cout << "I have " << amount; (cout << "I have ") << amount; • 2 lệnh trên là tương đương • Trả về gì? – Chính đối tượng cout • Trả về kiểu của đối số đầu tiên, ostream& DTH INT2202
  38. Ví dụ nạp chồng > (1/5) DTH INT2202
  39. Ví dụ nạp chồng > (2/5) DTH INT2202
  40. Ví dụ nạp chồng > (3/5) DTH INT2202
  41. Ví dụ nạp chồng > (4/5) DTH INT2202
  42. Ví dụ nạp chồng > (5/5) DTH INT2202
  43. Toán tử gán, = • Phải được nạp chồng dưới dạng toán tử thành viên • Luôn được nạp chồng tự động – Toán tử gán mặc định: • Sao chép các thành viên • Biến thành viên từ một đối tượng biến thành viên tương ứng của đối tượng khác • Toán tử gán mặc định là chấp nhận được với những lớp đơn giản – Nhưng khi có con trỏ ta phải nạp chồng toán tử gán! DTH INT2202
  44. Tự tăng và tự giảm • Mỗi toán tử có 2 phiên bản – Kí hiệu trước: ++x; – Kí hiệu sau: x++; • Phải phân biệt khi nạp chồng – Cách nạp chồng chuẩn Kí hiệu trước – Thêm một tham số kiểu int Kí hiệu sau • Chỉ là đánh dấu cho trình biên dịch! • Được phép chỉ định kí hiệu sau DTH INT2202
  45. Nạp chồng toán tử mảng, [ ] • Có thể nạp chồng [ ] cho lớp của bạn – Để dùng với đối tượng của lớp đó – Toán tử phải trả về một tham chiếu! – Toán tử [ ] phải là hàm thành viên! DTH INT2202
  46. Tóm tắt 1 • Có thể nạp chồng các toán tử có sẵn của C++ – Để làm việc với các đối tượng của kiểu do người dùng định nghĩa • Toán tử thật ra là hàm • Hàm friend có quyền truy cập trực tiếp vào thành viên private • Toán tử có thể được nạp chồng như hàm thành viên – Toán hạng thứ nhất chính là đối tượng gọi tới hàm thành viên này DTH INT2202
  47. Tóm tắt 2 • Hàm friend chỉ làm tăng tính hiệu quả – Không bắt buộc nếu có đủ hàm thành viên truy cập/biến đổi • Tham chiếu “đặt” cho một biến một bí danh nào đó • Có thể nạp chồng > – Kiểu trả về là tham chiếu tới kiểu stream DTH INT2202
  48. Chuẩn bị bài tới • Đọc chương 9 giáo trình: Xâu DTH INT2202