Bài giảng Nguyên lý lập trình hướng đối tượng - Bài 2: Hàm và Nạp chồng hàm - Lý Anh Tuấn

pdf 56 trang Gia Huy 17/05/2022 2520
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Nguyên lý lập trình hướng đối tượng - Bài 2: Hàm và Nạp chồng hàm - Lý Anh Tuấn", để 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_nguyen_ly_lap_trinh_huong_doi_tuong_bai_2_ham_va_n.pdf

Nội dung text: Bài giảng Nguyên lý lập trình hướng đối tượng - Bài 2: Hàm và Nạp chồng hàm - Lý Anh Tuấn

  1. NGUYÊN LÝ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG Bài 2: Hàm và Nạp chồng hàm Giảng viên: TS. Lý Anh Tuấn Email: tuanla@tlu.edu.vn
  2. Nội dung 1. Hàm định nghĩa trước ◦ Hàm trả về giá trị và hàm không trả về giá trị 2. Hàm người dùng định nghĩa ◦ Định nghĩa, khai báo, gọi hàm 3. Phạm vi ◦ Biến cục bộ ◦ Hằng và biến toàn cục 4. Tham số ◦ Truyền giá trị ◦ Truyền tham biến 5. Nạp chồng và đối số mặc định 2
  3. Giới thiệu hàm  Xây dựng các khối cho chương trình  Cách gọi trong các ngôn ngữ khác ◦ Thủ tục, chương trình con, phương thức ◦ Trong C++: hàm  I-P-O ◦ Đầu vào – Xử lý – Đầu ra ◦ Là các thành phần cơ bản của mỗi chương trình ◦ Sử dụng hàm cho mỗi thành phần này 3
  4. Hàm định nghĩa trước  Trong các thư viện có sẵn rất nhiều hàm  Hai kiểu hàm: ◦ Hàm trả về giá trị ◦ Hàm không trả về giá trị (void)  Phải “#include” thư viện phù hợp ◦ Ví dụ:  , (các thư viện của “C”)  (dùng cho cout, cin) 4
  5. Hàm định nghĩa trước  Có rất nhiều hàm toán học ◦ Nằm trong thư viện ◦ Hầu hết trả về một giá trị (câu trả lời)  Ví dụ: theRoot = sqrt(9.0); ◦ Các thành phần: sqrt = tên của hàm thư viện theRoot = biến được sử dụng để nhận câu trả lời 9.0 = đối số hoặc “khởi tạo đầu vào” của hàm 5
  6. Lời gọi hàm  Xét lệnh gán: theRoot = sqrt(9.0); ◦ Biểu thức “sqrt(9.0)” được hiểu như là một lời gọi hàm ◦ Đối số trong lời gọi hàm (9.0) có thể là một literal, một biến, hoặc một biểu thức  Lời gọi có thể là một phần của biểu thức: ◦ VD: bonus = sqrt(sales)/10; ◦ Dựa vào kiểu trả về của hàm để biết nơi được phép sử dụng lời hàm 6
  7. Ví dụ: Hàm định nghĩa trước 7
  8. Ví dụ: Hàm định nghĩa trước Tình huống kết quả: Nhap ngan sach mua tham lot san cho can phong (VND): 2000000 Voi ngan sach 2000000.00 VND Ban co the lot duoc mot dien tich mat san hinh vuong kich thuoc 4.85 m moi canh. 8
  9. Một số hàm định nghĩa trước  #include , thư viện gồm các hàm: ◦ abs() // Trả về giá trị tuyệt đối của một số int ◦ labs() // Trả về giá trị tuyệt đối của một số long int ◦ fabs() // Trả về giá trị tuyệt đối của một số float  Hàm pow(x, y): Trả về x mũ y ◦ VD: Cho biết kết quả in ra của đoạn mã lệnh double result, x = 3.0, y = 2.0; result = pow(x, y); cout << result; 9
  10. Một số hàm toán học Kiểu Kiểu giá Tên Mô tả Ví dụ Giá trị Thư viện đối số trị trả về sqrt Căn bậc hai double double sqrt(4.0) 2.0 cmath pow Lũy thừa double double pow(2.0,3.0) 8.0 cmath abs Giá trị tuyệt đối int int abs(-7) 7 cstdlib kiểu int abs(7) 7 labs Giá trị tuyệt đối long long labs(-70000) 70000 cstdlib kiểu long labs(70000) 70000 fabs Giá trị tuyệt đối double double fabs(-7.5) 7.5 cmath kiểu double fabs(7.5) 7.5 10
  11. Một số hàm toán học ceil Làm tròn lên double double ceil(3.2) 4.0 cmath ceil(3.9) 4.0 floor Làm tròn xuống double double floor(3.2) 3.0 cmath floor(3.9) 3.0 exit Kết thúc chương int void exit(1); Không có cstdlib trình rand Số ngẫu nhiên Không có int rand( ) Thay đổi cstdlib srand Thiết lập hạt unsigned void srand(42); Không có cstdlib giống cho rand int
  12. Hàm void định nghĩa trước  Không trả về giá trị  Thực hiện một hành động, nhưng không gửi câu trả lời  Khi được gọi, bản thân nó là một câu lệnh ◦ VD: exit(1); //Không trả về giá trị, do vậy không được sử dụng để gán  Các khía cạnh khác tương tự như hàm trả về giá trị 12
  13. Bộ phát sinh số ngẫu nhiên  Trả về số “được chọn ngẫu nhiên”  Sử dụng trong mô phỏng, trò chơi ◦ rand(): không có tham số, trả về giá trị giữa 0 & RAND_MAX ◦ Thu hẹp phạm vi: rand() % 6: trả về số ngẫu nhiên giữa 0 & 5 ◦ Tịnh tiến: rand() % 6 +1: dịch chuyển giữa 1 & 6 13
  14. Hạt giống số ngẫu nhiên  Các số giả ngẫu nhiên ◦ Gọi rand() tạo ra một chuỗi các số ngẫu nhiên  Sử dụng “hạt giống” để sửa đổi chuỗi srand(seed_value); ◦ Là hàm void có một đối số ◦ Có thể sử dụng bất cứ giá trị hạt giống nào,  VD: srand(time(0)); ◦ time() trả về thời gian hệ thống ◦ time() nằm trong thư viện 14
  15. Các ví dụ ngẫu nhiên  Số thực ngẫu nhiên giữa 0.0 & 1.0: (RAND_MAX – rand())/static_cast (RAND_MAX) ◦ Ép kiểu cho phép chia số thực  Số nguyên ngẫu nhiên giữa 1 & 6: rand() % 6 + 1 ◦ “%” là toán tử chia lấy phần dư  Số nguyên ngẫu nhiên giữa 10 & 20: rand() % 11 + 10 15
  16. Hàm người dùng định nghĩa  Cho phép tự viết hàm của riêng mình  Xây dựng các khối chương trình ◦ Chia để trị ◦ Khả đọc ◦ Sử dụng lại  Định nghĩa hàm có thể nằm: ◦ Cùng file với hàm main(), hoặc ◦ Trong file riêng rẽ để những người khác cũng có thể sử dụng 16
  17. Các thành phần của hàm  Khai báo hàm/nguyên mẫu hàm ◦ Thông tin cho trình biên dịch ◦ Thông dịch chính xác lời gọi  Định nghĩa hàm ◦ Sự thực thi hay mã lệnh thực hiện công việc của hàm  Lời gọi hàm ◦ Chuyển điều khiển cho hàm 17
  18. Khai báo hàm  Còn được gọi là nguyên mẫu hàm  Bộ biên dịch dựa vào nó để thông dịch lời gọi ◦ Cú pháp: FnName( ); ◦ Ví dụ: double totalCost( int numberParameter, double priceParameter);  Được đặt trước bất kỳ lời gọi nào ◦ Trong không gian khai báo của hàm main() ◦ Hoặc trong không gian toàn cục trước hàm main() 18
  19. Định nghĩa hàm  Sự thực thi của hàm, giống như sự thi hàm main()  Ví dụ: double totalCost( int numberParameter, double priceParameter) { const double TAXRATE = 0.1; double subtotal; subtotal = priceParameter * numberParameter; return (subtotal + subtotal * TAXRATE); }  Lưu ý thụt vào đầu dòng chuẩn 19
  20. Vị trí đặt định nghĩa hàm  Đặt sau hàm main(), không nằm bên trong hàm main()  Các hàm là bình đẳng, không hàm nào là thành phần của hàm khác  Các tham số hình thức trong định nghĩa ◦ Giữ chỗ cho dữ liệu gửi vào ◦ Sử dụng tên biến để tham chiếu tới dữ liệu trong định nghĩa  Lệnh return ◦ Trả dữ liệu về cho lời gọi 20
  21. Lời gọi hàm  Giống lời gọi hàm định nghĩa trước bill = totalCost(number, price);  totalCost trả về giá trị kiểu double, giá trị này được gán cho biến bill  Các đối số: number, price ◦ Đối số có thể là literal, biến, biểu thức, hoặc sự kết hợp của chúng ◦ Trong lời gọi hàm, đối số thường được gọi là “đối số thực sự” 21
  22. Ví dụ hàm Khai báo hàm, còn gọi là nguyên mẫu hàm Lời gọi hàm 22
  23. Ví dụ hàm Đầu đề hàm Thân hàm Định nghĩa hàm Tình huống kết quả: Nhap so luong san pham mua: 4 Nhap don gia cua san pham (VND): 50000 4 san pham voi gia 50000.00 VND moi san pham. Tong tien cua hoa don, bao gom thue la 220000.00 VND. 23
  24. Khai báo hàm thay thế  Khai báo hàm cung cấp thông tin cho bộ biên dịch  Bộ biên dịch chỉ cần biết: ◦ Giá trị trả về ◦ Tên hàm ◦ Danh sách tham số  Không cần tên tham số hình thức: double totalCost(int, double);  Tuy nhiên vẫn nên đưa vào cho dễ đọc 24
  25. Hàm gọi hàm  Đã làm việc này rồi: do main() là một hàm  Khai báo hàm phải xuất hiện trước lời gọi hàm  Định nghĩa hàm thường nằm: ◦ Sau định nghĩa hàm main(), hoặc ◦ Trong file riêng rẽ  Hàm có thể gọi đến chính nó “Đệ quy” 25
  26. Hàm trả về kiểu bool  Kiểu trả về có thể là bất kỳ kiểu dữ liệu nào  Một khai báo hàm/nguyên mẫu hàm: bool appropriate(int rate);  Định nghĩa hàm tương ứng: bool appropriate (int rate) { return (((rate>=10)&&(rate<20))||(rate==0); }  Trả về “true” hoặc “false”  Lời gọi hàm, từ một hàm khác: if (appropriate(entered_rate)) cout << "Rate is valid\n"; 26
  27. Khai báo hàm void  Tương tự như hàm trả về giá trị  Kiểu trả về là “void”  VD: Khai báo hàm/nguyên mẫu hàm: void showResults( double fDegrees, double cDegrees);  Kiểu trả về là “void”, nghĩa là không trả về gì 27
  28. Khai báo hàm void  Định nghĩa hàm: void showResults(double fDegrees, double cDegrees) { cout.setf(ios::fixed); cout.setf(ios::showpoint); cout.precision(1); cout << fDegrees << " degrees fahrenheit equals \n" << cDegrees << " degrees celsius.\n"; }  Lưu ý: Không có câu lệnh return 28
  29. Gọi hàm void  Giống như gọi hàm void định nghĩa trước  Gọi từ một hàm khác, chẳng hạn main(): showResults(degreesF, degreesC); showResults(32.5, 0.3);  Không sử dụng để gán, vì không có giá trị trả về  Các đối số thực sự (degreesF, degreesC) ◦ Được truyền cho hàm ◦ Hàm được gọi để thực hiện công việc với dữ liệu được truyền 29
  30. Câu lệnh return  Chuyển điều khiển về cho lời gọi hàm ◦ Phải có câu lệnh return nếu kiểu trả về khác void ◦ Thường là câu lệnh cuối cùng trong định nghĩa hàm  Với hàm void, câu lệnh return là tùy chọn ◦ Dấu } sẽ chuyển điều khiển từ hàm void một cách ngầm định 30
  31. Hàm main()  main() là một hàm  Chỉ tồn tại duy nhất một hàm main() trong một chương trình  Hàm main() được gọi bởi hệ điều hành  Nó thường có câu lệnh return ◦ Giá trị được trả về cho hệ điều hành  Nên trả về “int” hoặc “void” 31
  32. Phạm vi  Biến cục bộ ◦ Được khai báo bên trong thân của một hàm ◦ Chỉ tồn tại trong hàm đó  Có thể khai báo các biến có cùng tên trong các hàm khác nhau ◦ Phạm vi cục bộ: hàm là phạm vi của nó  Lợi ích của biến cục bộ ◦ Duy trì kiểm soát riêng rẽ với dữ liệu ◦ Hàm nên khai báo bất kỳ dữ liệu cục bộ nào mà nó cần 32
  33. Hằng toàn cục và biến toàn cục  Được khai báo bên ngoài thân hàm ◦ Toàn cục với tất cả các hàm trong file  Khai báo toàn cục cho hằng: ◦ const double TAXRATE = 0.1; ◦ Khai báo là toàn cục do vậy có phạm vi với tất cả các hàm  Biến toàn cục ◦ Được phép nhưng hiếm khi sử dụng ◦ Khó kiểm soát khi sử dụng 33
  34. Khối  Khai báo dữ liệu bên trong lệnh kép ◦ Lệnh kép được gọi là một “khối” và có “phạm vi khối”  Các định nghĩa hàm đều là khối ◦ Tạo ra “phạm vi hàm” cục bộ  Khối lặp: for (int ctr=0;ctr<10;ctr++) { sum+=ctr; } ◦ Biến crt chỉ có phạm vi trong khối thân vòng lặp  Các biến có cùng tên được phép khai báo trong nhiều khối 34
  35. Các tham số  Hai phương pháp truyền đối số làm tham số  Truyền giá trị ◦ Truyền bản sao của giá trị  Truyền tham chiếu ◦ Truyền “địa chỉ” của đối số thực sự 35
  36. Tham số truyền giá trị  Bản sao của đối số thực sự được truyền  Là “biến cục bộ” bên trong hàm  Nếu sửa đổi, chỉ “bản sao cục bộ” thay đổi ◦ Hàm không truy cập tới “đối số thực sự” từ lời gọi  Đây là phương pháp mặc định ◦ Được sử dụng trong tất cả các ví dụ phía trước 36
  37. Ví dụ truyền giá trị 37
  38. Ví dụ truyền giá trị Giá trị của minutes không bị thay đổi bởi lời gọi tới fee. minutesWorked là biến cục bộ được khởi tạo bằng giá trị của minutes. Tình huống kết quả: Chao mung den van phong luat su. Nhap so gio, va so phut ban can tu van: 5 46 Voi 5 gio va 46 phut, hoa don cua ban la 2875000.00 VND 38
  39. Lỗi thường gặp khi truyền giá trị  Khai báo lại tham số bên trong hàm double fee(int hoursWorked, int minutesWorked) { int quarterHours; // local variable int minutesWorked // NO! } ◦ Bộ biên dịch sẽ báo lỗi khai báo lại  Đối số giá trị có vai trò như “biến cục bộ”, nhưng hàm có chúng một cách tự động 39
  40. Tham số truyền tham chiếu  Cung cấp truy cập đến đối số thực sự của lời gọi  Dữ liệu của lời gọi có thể được sửa đổi bởi hàm được gọi  Thường được sử dụng với hàm đầu vào ◦ Để lấy dữ liệu và đưa cho lời gọi  Xác định bằng dấu &, theo sau kiểu của tham số hình thức 40
  41. Ví dụ truyền tham chiếu 41
  42. Ví dụ truyền tham chiếu Tình huống kết quả: Nhap hai so nguyen: 5 6 Thu tu dao nguoc cua cac so: 6 5 42
  43. Lý giải về truyền tham chiếu  Cái gì thực sự được truyền?  Một tham chiếu ngược về đối số thực sự của lời gọi ◦ Trỏ đến vùng nhớ của đối số thực sự ◦ Được gọi là “địa chỉ”, là một số duy nhất tham chiếu đến một vùng riêng biệt trong bộ nhớ 43
  44. Tham số tham chiếu hằng  Sử dụng đối số tham chiếu có nhiều rủi ro ◦ Dữ liệu của lời gọi có thể bị thay đổi ◦ Đôi khi chúng ta không mong muốn điều này  Để bảo vệ dữ liệu, và vẫn truyền tham chiếu ◦ Sử dụng từ khóa const void sendConstRef( const int &par1, const int &par2); ◦ Tạo đối số “chỉ đọc” bởi hàm ◦ Không cho phép thay đổi bên trong thân hàm 44
  45. Danh sách tham số trộn  Kết hợp các kỹ thuật truyền, bao gồm các tham số truyền giá trị và truyền tham biến  Trật tự của các đối số trong danh sách là rất quan trọng: void mixedCall(int & par1, int par2, double & par3);  Lời gọi hàm: mixedCall(arg1, arg2, arg3); ◦ arg1 là kiểu nguyên, được truyền theo tham chiếu ◦ arg2 là kiểu nguyên, được truyền theo giá trị ◦ arg3 là kiểu thực, được truyền theo tham chiếu 45
  46. Nạp chồng  Tên hàm giống nhau, danh sách các tham số khác nhau  Hai định nghĩa hàm riêng biệt  Tín hiệu hàm ◦ Tên hàm & danh sách tham số ◦ Phải là “duy nhất” cho mỗi định nghĩa hàm  Cho phép thực hiện cùng một công việc trên các dữ liệu khác nhau 46
  47. Ví dụ nạp chồng: average  Hàm tính giá trị trung bình của hai số: double average(double n1, double n2) { return ((n1 + n2) / 2.0); }  Hàm tính giá trị trung bình của 3 số: double average(double n1, double n2, double n3) { return ((n1 + n2 + n3) / 3.0); }  Hai hàm có cùng tên 47
  48. Nạp chồng average()  Việc hàm nào được gọi phụ thuộc vào bản thân lời gọi hàm ◦ avg = average(5.2, 6.7); //Gọi average() hai tham số ◦ avg = average(6.5, 8.5, 4.2); //Gọi average() ba tham số  Bộ biên dịch xử lý lời gọi dựa trên tín hiệu của lời gọi hàm ◦ Khớp lời gọi với hàm phù hợp ◦ Mỗi hàm được coi là riêng biệt  Lưu ý: chỉ nạp chồng các hàm thực hiện cùng một công việc 48
  49. Ví dụ nạp chồng  Cho các hàm sau đây: 1. void f(int n, double m); 2. void f(double n, int m); 3. void f(int n, int m);  Các lời gọi: f(98, 99); Calls #3 f(5.3, 4); Calls #2 f(4.3, 5.2); Calls ???  Tránh việc nạp chồng nhập nhằng 49
  50. Chuyển kiểu tự động và nạp chồng  Tham số hình thức dạng số thường ở kiểu "double"  Cho phép bất kỳ kiểu số nào: dữ liệu phụ thuộc tự động được chuyển đổi int double float double char double  Tránh việc nạp chồng cho các kiểu số khác nhau 50
  51. Chuyển kiểu tự động và nạp chồng  double mpg(double miles, double gallons) { return (miles/gallons); } Các lời gọi ví dụ:  mpgComputed = mpg(5, 20); ◦ Chuyển 5 & 20 thành doubles, rồi truyền  mpgComputed = mpg(5.8, 20.2); ◦ Không cần chuyển kiểu  mpgComputed = mpg(5, 2.4); ◦ Chuyển 5 thành 5.0, sau đó truyền các giá trị cho hàm 51
  52. Đối số mặc định  Cho phép lờ đi một vài đối số  Được chỉ ra trong khai báo hàm void showVolume( int length, int width = 1, int height = 1); ◦ Hai đối số cuối là mặc định  Các lời gọi có thể có: ◦ showVolume(2, 4, 6); //tất cả các đối số được cung cấp ◦ showVolume(3, 5); //height mặc định là 1 ◦ showVolume(7); //width & height mặc định là 1 52
  53. Đối số mặc định Các đối số mặc định Một đối số mặc định không nên được chỉ ra lần thứ hai 53
  54. Đối số mặc định Kết quả: The tich cua hop chu nhat voi Chieu dai = 4, Chieu rong = 6 va Chieu cao = 2 la 48 The tich cua hop chu nhat voi Chieu dai = 4, Chieu rong = 6 va Chieu cao = 1 la 24 The tich cua hop chu nhat voi Chieu dai = 4, Chieu rong = 1 va Chieu cao = 1 la 4 54
  55. Tóm tắt  Hai kiểu hàm: hàm trả về giá trị và hàm void  Dữ liệu cục bộ: được khai báo trong định nghĩa hàm  Dữ liệu toàn cục: Được khai báo bên ngoài các định nghĩa hàm, phù hợp cho hằng nhưng không phù hợp cho biến  Tham số/Đối số: ◦ Hình thức: Trong khai báo và định nghĩa hàm ◦ Thực sự: Trong lời gọi hàm  Tham số hình thức là để giữ chỗ, được điền bằng đối số thực sự trong lời gọi hàm 55
  56. Tóm tắt  Tham số truyền giá trị là bản sao cục bộ trong thân hàm nhận ◦ Đối số thực sự không thể sửa đổi  Việc truyền tham chiếu truyền địa chỉ ô nhớ của đối số thực sự ◦ Đối số thực sự bắt buộc phải là biến và có thể sửa đổi  Việc có nhiều định nghĩa cho cùng một tên hàm: được gọi là nạp chồng  Các đối số mặc định cho phép lời gọi hàm lờ đi một số đối số trong danh sách ◦ Nếu không được cung cấp giá trị mặc định được gán 56