Bài giảng Ngôn ngữ lập trình - Bài 3: Hàm và nạp chồng hàm - Lê Nguyễn Tuấn Thành

Cơ bản về hàm

 Hàm được định nghĩa sẵn

 Hàm trả về một giá trị

 Hàm không trả về giá trị nào (hàm void)

 Hàm do người dùng định nghĩa

 Khai báo, định nghĩa, gọi hàm

 Hàm đệ quy (recursive functions)

 Quy tắc phạm vi (scope rules)

 Biến địa phương (local)

 Hằng số (constant) và biến toàn cục (global)

 Khối, phạm vi lồng nhau (nested scopes)

pdf 59 trang kimcuc 8480
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 3: Hàm và nạp chồng hàm - 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 3: Hàm và nạp chồng hàm - Lê Nguyễn Tuấn Thành

Bài giảng Ngôn ngữ lập trình - Bài 3: Hàm và nạp chồng hàm - Lê Nguyễn Tuấn Thành
Ngôn ngữ lập trình 
Bài 3: 
Hàm và Nạp chồng Hàm 
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 
1. Hàm (Function) 
2. Nạp chồng hàm (Overloading) 
Bài giảng có sử dụng hình vẽ trong cuốn sách “Absolute C++. W. Savitch, Addison Wesley, 2002” 
1. HÀM 
Function 
Cơ bản về hàm 
4 
 Hàm được định nghĩa sẵn 
 Hàm trả về một giá trị 
 Hàm không trả về giá trị nào (hàm void) 
 Hàm do người dùng định nghĩa 
 Khai báo, định nghĩa, gọi hàm 
 Hàm đệ quy (recursive functions) 
 Quy tắc phạm vi (scope rules) 
 Biến địa phương (local) 
 Hằng số (constant) và biến toàn cục (global) 
 Khối, phạm vi lồng nhau (nested scopes) 
Giới thiệu về hàm 
5 
 Hàm (Function): một khối của chương trình (blocks of 
programs) có mục đích rõ ràng 
 Một số thuật ngữ (cách gọi) khác của hàm trong những 
ngôn ngữ khác: 
 Phương thức (procedures), chương trình con (subprograms), 
phương thức (methods) 
 Khái niệm I – P – O 
 Input – Process – Output 
 Thành phần cơ bản của bất kỳ chương trình nào 
 Thao tác với hàm dựa trên các thành phần của khái niệm này 
Hàm định nghĩa trước 
(Predefined functions) 
6 
 C++ cung cấp nhiều thư viện với đầy đủ các hàm được 
định nghĩa sẵn ! 
 Hai kiểu: 
 Hàm trả về một giá trị 
 Hàm không trả về giá trị nào 
 Phải “#include” thư viện thích hợp 
 Ví dụ: , ,  
Sử dụng hàm định nghĩa sẵn 
7 
 C++ có vô số các hàm toán học định nghĩa sẵn! 
 Trong thư viện 
 Hầu hết trả về một giá trị 
 Ví dụ: theRoot = sqrt(9.0); 
 Các thành phần của biểu thức trên: 
 sqrt: tên hàm 
 theRoot: biến được gán giá trị trả về của hàm 
 9.0: đối số (argument) hoặc đầu vào (starting input) cho hàm 
 Viết theo khái niệm I – P – O: 
 I = 9.0 
 P = “tính căn bậc hai” (the square root) 
 O = 3.0, giá trị trả về của hàm, được gán cho biến theRoot 
Lời gọi hàm 
(Function call) 
8 
 Trở lại ví dụ trước theRoot = sqrt(9.0); 
 Biểu thức sqrt(9.0) là một lời gọi hàm (function call hay 
function invocation) 
 Đối số của một lời gọi hàm có thể là một literal (vd: 9.0), 
một biến hay một biểu thức 
 Lời gọi hàm có thể được sử dụng trong một biểu thức 
khác (ví dụ: bonus = sqrt(sales)/10;) 
Chương trình với hàm định nghĩa sẵn (1/2) 
9 
Chương trình với hàm định nghĩa sẵn (2/2) 
10 
Một số hàm định nghĩa sẵn khác (1/3) 
11 
 #include 
 abs(): trả về giá trị tuyệt đối của một số nguyên (int) 
 labs(): trả về giá trị tuyệt đối của một số nguyên lớn (long 
int) 
 fabs(): trả về giá trị tuyệt đối của một số thực (float) 
 Hàm toán học 
 pow (x, y): x^y 
 Lưu ý: một hàm có thể nhiều đối số, mỗi đối số có 
thể có kiểu dữ liệu khác nhau 
Một số hàm định nghĩa sẵn khác (2/3) 
12 
Một số hàm định nghĩa sẵn khác (3/3) 
13 
Hàm void định nghĩa sẵn 
14 
 Không có giá trị trả lại 
 Hàm void vẫn có thể có đối số (arguments) 
 Ví dụ: exit (1) 
Bộ tạo số ngẫu nhiên 
15 
 Trả về một số “được chọn ngẫu nhiên” 
 Được sử dụng cho mô phỏng (simulation), trò chơi (game) 
 rand(): trả về một giá trị ở giữa 0 và RAND_MAX 
 Scaling: ép buộc các số ngẫu nhiên vào một khoảng nhỏ hơn. 
Ví dụ: rand() % 6 // trả lại giá trị ngẫu nhiên trong khoảng từ 
0 đến 5 
 Shifting: rand() % 6 + 1 
 Hạt giống (seed) dùng để tạo số ngẫu nhiên 
 Hàm rand() tạo ra một chuỗi trình tự (sequence) các số ngẫu nhiên 
 Chúng ta có thể sử dụng “hạt giống (seed)” để thay thế trình tự tạo 
số ngẫu nhiên với hàm srand(seed_value): 
 void function 
 Seed có thể là bất kỳ giá trị nào. Ví dụ: srand(time(0)); 
Bài tập 
16 
 Viết chương trình C++ sinh ra N số ngẫu nhiên (N < 
100) trong khoảng 0 đến 1000, sau đó sắp xếp theo thứ 
tự tăng dần hoặc giảm dần 
 Gợi ý: sử dụng hàm sẵn có rand() 
Hàm do người dùng định nghĩa 
17 
 Lập trình viên tự viết hàm cho mục đích của mình! 
 Xây dựng những khối của chương trình 
 Chia để trị (Divide & Conquer) 
 Dễ đọc (Readability) 
 Sử dụng lại (Re-use) 
 Hàm mà bạn định nghĩa có thể: 
 Trong cùng một file với hàm main() 
 Ở file khác, riêng biệt để người khác có thể dùng nó 
Những bước cần có khi xây dựng một hàm 
18 
 3 bước khi xây dựng hàm 
1. Khai báo / nguyên mẫu hàm (function 
declaration/prototype) 
 Thông tin cho trình biên dịch (compiler) 
 Thông dịch (interpret) thích hợp lời gọi hàm 
2. Định nghĩa hàm (function definition) 
 Cài đặt thực tế của hàm 
3. Gọi hàm (function call) 
 Chuyển điều khiển cho hàm 
Khai báo hàm 
(Function declaration) 
19 
 Còn được gọi là nguyên mẫu hàm (function prototype) 
 Khai báo thông tin cho trình biên dịch. Nói cho trình biên 
dịch biết cách để thông dịch lời gọi hàm 
 Cú pháp: 
 TênHàm(danh_sách_đối_số); 
 Ví dụ: 
double totalCost( int numberParameter, double priceParameter); 
 Được đặt trước bất kỳ lời gọi hàm 
 Trong không gian khai báo của hàm main() 
 Hoặc ở trước hàm main() trong không gian toàn cục 
Định nghĩa hàm 
(Function definition) 
20 
 Cài đặt thực tế của hàm. Giống như cài đặt của hàm 
main() 
 Ví dụ: 
 double totalCost ( int numberParameter, 
 double priceParameter) 
{ 
 const double TAXRATE = 0.05; 
 double subTotal; 
 subtotal = priceParameter * numberParameter; 
 return (subtotal + subtotal * TAXRATE); 
} 
 Được đặt sau hàm main(). Không đặt bên trong hàm 
main() 
 Lệnh return: trả quyền điều khiển ngược lại đối tượng 
gọi hàm 
Lời gọi hàm 
(Function call) 
21 
 Giống như gọi những hàm đã được định nghĩa sẵn 
 Ví dụ: bill = totalCost(number, price); // totalCost trả lại 
một giá trị kiểu double và giá trị này được gán cho biến 
bill 
 Hai tham số: number, price 
Ví dụ hàm người dùng định nghĩa (1/2) 
22 
Ví dụ hàm người dùng định nghĩa (2/2) 
23 
Hàm trả lại giá trị kiểu boolean 
24 
1. Khai báo hàm: 
 bool appropriate(int rate); 
2. Định nghĩa hàm: 
 bool appropriate (int rate) 
{ 
 return (((rate>=10)&&(rate<20))||(rate==0); 
} 
3. Gọi hàm: 
 if (appropriate(entered_rate)) 
 cout << "Rate is valid\n"; 
Khai báo hàm void 
25 
 Giống như khai báo hàm trả về giá trị 
 Nhưng kiểu trả về là “void”. 
 Ví dụ khai báo hàm sau: 
 void showResults ( double fDegrees, double cDegrees); 
 Định nghĩa hàm. Không có lệnh return 
 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"; 
} 
 Gọi giống như những hàm void định nghĩa sẵn. Không có phép gán 
 showResults(32.5, 0.3); 
Điều kiện trước và điều kiện sau 
26 
 Tương tự như khái niệm I – P – O 
 Chú thích trong khai báo hàm 
 void showInterest(double balance, double rate); 
// Precondition: balance is nonnegative account balance 
// rate is interest rate as percentage 
// Postcondition: amount of interest on given balance, 
// at given rate  
 Cũng thường được gọi là Input và Output 
Hàm đặc biệt main() 
27 
 main() cũng là một hàm NHƯNG chỉ có duy nhất một 
hàm main() trong chương trình 
 Ai gọi hàm main() ? 
 Hệ điều hành 
 Thông thường có một lệnh return để trả giá trị về cho đối 
tượng gọi (caller), ở đây là hệ điều hành 
 Kiểu trả lại thường là int hoặc void 
Quy tắc phạm vi (scope rules) 
28 
 Biến địa phương (local variables) 
 Được khai báo trong thân hàm 
 Chỉ được sử dụng bên trong hàm đó 
 Có thể khai báo biến cùng tên trong những hàm khác nhau 
 Biến toàn cục (global variables) 
 Khai báo bên ngoài thân hàm 
 Thường cho các hằng số 
Tóm tắt cho phần hàm 
29 
 Hai kiểu hàm 
 Hàm trả về giá trị và hàm void 
 Hàm giống như hộp đen (black boxes) 
 Ẩn đi chi tiết của việc “làm sao để làm được điều đó” (how) 
 Khai báo dữ liệu địa phương của nó 
 Khai báo hàm nên được chú thích đầy đủ 
 Chú thích điều kiện trước và sau (pre- & post-conditions) 
 Chú thích tất cả đối tượng gọi hàm này 
 Biến địa phương 
 Khai báo bên trong định nghĩa hàm 
 Biến toàn cục 
 Khai báo bên trên định nghĩa hàm 
 Thường cho hằng số, không nên cho các biến 
 Tham số / Đối số (Parameters/Arguments) 
Bài tập về hàm 
30 
 Viết một hàm để tính giai thừa của một số nguyên 
dương. 
2. Tham số và 
nạp chồng hàm 
Tham số và nạp chồng hàm 
32 
 Tham số 
 Tham trị (call-by-value) 
 Tham chiếu (call-by-reference) 
 Danh sách tham số trộn lẫn 
 Nạp chồng và đối số mặc định 
 Ví dụ, Quy tắc 
 Test và gỡ rối (debug) hàm 
 assert macro 
 stubs, drivers 
Tham số 
33 
 2 cách thức truyền tham số cho hàm 
1. Truyền tham trị (call-by-value) 
 Bản sao có giá trị giống tham số được truyền vào hàm 
 Được xem như biến địa phương của hàm 
 Nếu thay đổi, chỉ có bản sao địa phương (local copy) thay đổi. 
Hàm không có quyền truy cập vào tham số thực sự của đối 
tượng gọi hàm 
2. Truyền tham chiếu (call-by-reference) 
 Địa chỉ của tham số được truyền vào hàm 
 Cung cấp quyền truy cập tới tham số thực sự của đối tượng 
gọi hàm 
 Dữ liệu có thể được thay đổi bởi hàm 
 Thêm dấu & (ampersand) vào trước tham số 
Chương trình với Tham trị 
(call-by-value) (1/3) 
34 
Chương trình với Tham trị 
(call-by-value) (2/3) 
35 
Chương trình với Tham trị 
(call-by-value) (3/3) 
36 
Chương trình với Tham chiếu 
(call-by-reference) (1/3) 
37 
Chương trình với Tham chiếu 
(call-by-reference) (2/3) 
38 
Chương trình với Tham chiếu 
(call-by-reference) (3/3) 
39 
Phân biệt Tham số và đối số 
40 
 Tham số (Parameters) vs Đối số (Arguments) 
 Gọi là tham số khi khai báo và định nghĩa hàm (bước 1, 2) 
 Gọi là đối số khi gọi hàm (bước 3) 
 Danh sách tham số trộn lẫn. Ví dụ: 
 void mixedCall(int& par1, int par2, double& par3); 
 Khi gọi hàm mixedCall(arg1, arg2, arg3); 
 arg1: là 1 kiểu int, được truyền vào bởi tham chiếu 
 arg2: là 1 kiểu int, được truyền vào bởi tham trị 
 arg3: là 1 kiểu double, được truyền vào bởi tham chiếu 
Nạp chồng hàm (1/2) 
41 
 Khai báo nhiều tên hàm giống nhau, chỉ khác nhau danh sách 
tham số. 
 Phân biệt các hàm này bằng cặp 
 , được gọi là chữ ký (signature) 
 Định nghĩa của các hàm này khác nhau 
 Ví dụ: hai hàm tính giá trị trung bình 
 Của 2 tham số 
 double average(double n1, double n2) 
{ 
 return ((n1 + n2) / 2.0); 
} 
 Của 3 tham số 
 double average(double n1, double n2, double n3) 
{ 
 return ((n1 + n2 + n3) / 3.0); 
} 
Nạp chồng hàm (2/2) 
42 
 Hàm nào sẽ được gọi? 
 Phụ thuộc vào danh sách đối số 
 Phân giải nạp chồng hàm (overloading resolution): 
 Trùng khớp chính xác: tìm kiếm một hàm với chữ ký trùng khớp chính 
xác (exact signature) 
 Trùng khớp tương thích (compatible match): tìm kiếm một hàm với chữ 
ký trùng khớp tương thích (“compatible” signature) khi sự ép kiểu tự 
động có thể thực thi 
 Ép kiểu lên (promotion, vd: int -> double): không mất dữ liệu 
 Ép kiểu xuống (demotion, vd: double -> int): có thể mất dữ liệu 
 Ví dụ phân giải nạp chồng hàm: 
 1. void f (int n, double m); 
2. void f (double n, int m); 
3. void f (int n, int m); 
 f(98, 99); gọi hàm số 3 
f(5.3, 4); gọi hàm số 2 
f(4.3, 5.2); gọi hàm số mấy ??? 
Ép kiểu tự động và nạp chồng 
43 
 Cho phép các kiểu số khác tự động chuyển thành kiểu 
“double” khi cần (int -> double, float -> double, char -> double) 
 Ví dụ: 
 double mpg(double miles, double gallons) 
{ 
 return (miles/gallons); 
} 
 Lời gọi hàm 
 mpgComputed = mpg(5, 20); // chuyển 5 và 20 thành kiểu double, sau đó 
truyền vào hàm 
 mpgComputed = mpg(5.8, 20.2); // không cần thiết phải chuyển kiểu 
 mpgComputed = mpg(5, 2.4); // chuyển 5 thành 5.0, sau đó truyền vào 
hàm 
Đối số mặc định 
(Defaut argument) 
44 
 Cho phép gọi hàm thiếu một số đối số 
 Dùng giá trị mặc định của tham số khi khai báo / định 
nghĩa hàm 
 Ví dụ: 
 void showVolume (int length, int width = 1, int height = 1); 
 Những lời gọi hàm sau hợp lệ: 
 showVolume(2, 4, 6); 
 showVolume(3, 5); // thiếu đối số height, height lấy giá trị mặc định là 1 
 showVolume(7); // thiếu 2 đối số width & height, lấy giá trị mặc định là 1 
Chương trình đối số mặc định (1/2) 
45 
Chương trình đối số mặc định (2/2) 
46 
Bài tập nạp chồng hàm 
47 
 Sử dụng nạp chồng hàm để xếp thứ tự 10 số kiểu 
int, hoặc 10 giá trị long hoặc 10 giá trị double trong 
cùng một chương trình C++. 
Test và debug hàm 
48 
 Có nhiều cách thức khác nhau để kiểm tra tính 
chính xác của một hàm tự định nghĩa 
1. Dùng lệnh cout để in kết quả ra màn hình trong 
lúc định nghĩa và gọi hàm 
2. Sử dụng một bộ gỡ rối của trình biên dịch 
(compiler debugger) 
3. Sử dụng assert macro 
4. Sử dụng stubs và drivers 
Assert macro 
49 
 Assertion: một câu lệnh trả về true hoặc false 
 Sử dụng để kiểm tra độ chính xác của điều kiện 
 Cú pháp: assert(); 
 Không có giá trị trả lại 
 Đánh giá assert_condition, kết thúc nếu false, tiếp tục nếu 
true 
 Được định nghĩa trong thư viện 
Ví dụ về assert macro 
50 
 Khai báo hàm 
 void computeCoin (int coinValue, int& number, int& 
amountLeft); 
//Precondition: 0 < coinValue < 100, 0 <= amountLeft <100 
//Postcondition: number set to max. number of coins 
 Kiểm tra điều kiện trước 
 assert ((0 < coinValue) && (coinValue < 100) 
 && (0 <= amountLeft) && (amountLeft < 100)); 
 Nếu điều kiện trước không thỏa mãn -> điều kiện là false -> chương 
trình kết thúc ngay lập tức 
 Hữu ích trong việc gỡ rối (debugging) 
Bật/tắt assert 
51 
 #define NDEBUG 
#include 
 Thêm dòng #define NDEBUG trước dòng #include để tắt 
tất cả các assertions trong chương trình 
 Xóa dòng này (hoặc tạo thành dòng chú thích) sẽ bật 
chức năng assert trong chương trình 
Stubs và drivers 
52 
 Phân chia các đơn vị biên dịch 
 Mỗi hàm được thiết kế (design), code, test riêng biệt 
 Đảm bảo tính hợp lệ của từng đơn vị 
 Chia để trị: chuyển các nhiệm vụ lớn thành các tác vụ nhỏ 
hơn, dễ quản lý hơn 
 NHƯNG làm sao để test các hàm độc lập nhau 
 Dùng những chương trình driver 
Ví dụ chương trình driver (1/3) 
53 
Ví dụ chương trình driver (2/3) 
54 
Ví dụ chương trình driver (3/3) 
55 
Stubs 
56 
 Phát triển chương trình từng bước 
 Viết các hàm lớn trước (big-picture) 
 Viết các hàm cấp thấp (low-level) sau 
 Stub-out các hàm cho đến khi cài đặt 
 Ví dụ: 
 double unitPrice(int diameter, double price) 
{ 
 return (9.99); // not valid, but noticeably a "temporary" value 
} 
 Lời gọi tới hàm này vẫn hoạt động 
Quy tắc test căn bản 
57 
 Viết chương trình đúng 
 Tối giảm hóa lỗi (errors), bugs 
 Đảm bảo tính hợp lệ của dữ liệu 
 Test và debug từng hàm trong chương trình một cách lần 
lượt 
 Tránh lỗi phân tầng (error-cascading) và kết quả xung đột 
(conflicting results) 
Tóm tắt cho phần nạp chồng hàm 
58 
 Tham trị (call-by-value) là những bản sao cục bộ (local copies) 
trong thân hàm. Đối số thực sự của hàm không thể bị thay đổi 
 Tham chiếu (call-by-reference) truyền địa chỉ bộ nhớ của đối 
số thực sự vào hàm. Đối số thực sự có thể bị thay đổi 
 C++ cho phép nhiều định nghĩa của hàm cùng một tên hàm: 
gọi là nạp chồng hàm 
 Tham số mặc định cho phép lời gọi hàm thiếu một vài đối số 
trong danh sách truyền vào. Khi đó các giá trị mặc định sẽ 
được sử dụng 
 assert macro sẽ kết thúc chương trình nếu điều kiện kiểm 
tra là false 
 Các hàm nên được test một cách độc lập, như các đơn vị biên 
dịch riêng biệt và với driver 
Tham khảo 
59 
 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_3_ham_va_nap_chong_ham_le_n.pdf