Academia.eduAcademia.edu
1 Ngôn ngữ Lập trình C++ Chương I - Giới thiệu ngôn ngữ C++ © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 2 Nội dung chính • • • • • Mã máy, Hợp ngữ, và ngôn ngữ bậc cao Một số ngôn ngữ lập trình bậc cao Lịch sử C và C++ Hệ thống và môi trường lập trình C++ Giới thiệu về C++ – – – – – ví dụ về chương trình C++ đơn giản khái niệm biến vào ra dữ liệu các phép toán số học ra quyết định - các phép toán quan hệ © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 3 1.1 Mã máy, Hợp ngữ, và Ngôn ngữ bậc cao 1. Mã máy (machine language) – – – – – – Là ngôn ngữ duy nhất máy tính trực tiếp hiểu được, là “ngôn ngữ tự nhiên” của máy tính Được định nghĩa bởi thiết kế phần cứng, phụ thuộc phần cứng Gồm các chuỗi số, => chuỗi các số 0 và 1 Dùng để lệnh cho máy tính thực hiện các thao tác cơ bản, mỗi lần một thao tác Nặng nề, khó đọc đối với con người Ví dụ: +1300042774 +1400593419 +1200274027 © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 4 1.1 Mã máy, Hợp ngữ, và Ngôn ngữ bậc cao 2. Hợp ngữ (assembly) – – – Những từ viết tắt kiểu tiếng Anh, đại diện cho các thao tác cơ bản của máy tính Dễ hiểu hơn đối với con người Máy tính không hiểu • – Cần đến các chương trình dịch hợp ngữ (assembler) để chuyển từ hợp ngữ sang mã máy Ví dụ: LOAD BASEPAY ADD OVERPAY STORE GROSSPAY © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 5 1.1 Mã máy, Hợp ngữ, và Ngôn ngữ bậc cao 3. Các ngôn ngữ bậc cao (high-level languages) – – – Tương tự với tiếng Anh, sử dụng các ký hiện toán học thông dụng Một lệnh thực hiện được một công việc mà hợp ngữ cần nhiều lệnh để thực hiện được. Ví dụ: grossPay = basePay + overTimePay – – Các chương trình dịch (compiler) để chuyển sang mã máy Các chương trình thông dịch (interpreter program) trực tiếp chạy các chương trình viết bằng ngôn ngữ bậc cao. • • Chậm hơn Thuận tiện khi đang phát triển chương trình © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 6 1.2 Một số ngôn ngữ lập trình bậc cao • • • • FORTRAN – FORmula TRANslator (1954-1957: IBM) – Tính toán toán học phức tạp, thường dùng trong các ứng dụng khoa học và kỹ thuật COBOL – COmmon Business Oriented Language (1959) – Thao tác chính xác và hiệu quả đối với các khối lượng dữ liệu lớn, • Các ứng dụng thương mại Pascal – Tác giả: Niklaus Wirth – Dùng trong trường học. Java – Tác giả: Sun Microsystems (1991) – Ngôn ngữ điều khiển theo sự kiện (event-driven), hoàn toàn hướng đối tượng, tính khả chuyển (portable) rất cao. – Các trang Web với nội dung tương tác động – Phát triển các ứng dụng quy mô lớn © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 7 1.2 Một số ngôn ngữ lập trình bậc cao • BASIC – Beginner’s All-Purpose Symbolic Instruction Code – Từ giữa những năm1960 • Visual Basic – GUI, xử lý sự kiện (event handling), sử dụng Win32 API, lập trình hướng đối tượng (object-oriented programming), bắt lỗi (error handling) • Visual C++ – C++ của Microsoft và mở rộng • Thư viện của Microsoft (Microsoft Foundation Classes -MFC) • Thư viện chung – GUI, đồ họa, lập trình mạng, đa luồng (multithreading), … – Dùng chung giữa Visual Basic, Visual C++, C# • C# – Bắt nguồn từ C, C++ và Java – Ngôn ngữ điều khiển theo sự kiện (event-driven), hoàn toàn hướng đối tượng, ngôn ngữ lập trình trực quan (visual programming language) © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 8 1.3 Lịch sử ngôn ngữ C và C++ • C – – – – – Dennis Ritchie (Bell Laboratories) Là ngôn ngữ phát triển của hệ điều hành UNIX Độc lập phần cứng => có thể viết các chương trình khả chuyển Chuẩn hóa năm 1990 – ANSI C Kernighan & Ritchie “The C Programming Language”, 2nd, 1988 • C++ – – – – Là mở rộng của C Đầu những năm 1980: Bjarne Stroustrup (phòng thí nghiệm Bell) Cung cấp khả năng lập trình hướng đối tượng. Ngôn ngữ lai • Lập trình cấu trúc kiểu C • Lập trình hướng đối tượng • Cả hai • Có cần biết C trước khi học C++? © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 9 1.4 Hệ thống C++ – Môi trường phát triển chương trình (Program-development environment) – Ngôn ngữ – Thư viện chuẩn (C++ Standard Library) © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 10 1.4 Môi trường cơ bản cho lập trình C++ Các giai đoạn của chương trình C++: 1. Soạn thảo - Edit 2. Tiền xử lý - Preprocess 3. Biên dịch - Compile 4. Liên kết - Link Disk Chương trình được viết bằng chương trình soạn thảo và lưu trên đĩa. Preprocessor Disk Chương trình tiền xử lý thực hiện xử lý mã nguồn Compiler Disk Linker Disk Editor Trình biên dịch tạo object code và lưu trên đĩa. Primary Memory Trình kết nối kết hợp objectcode với các thư viện, tạo file chạy được và lưu lên đĩa Loader Disk 5. Nạp - Load Trình nạp nạp trình vào bộ nhớ chương .. .. .. Primary Memory CPU CPU nhận từng lệnh, thực thi lệnh đó, có thể lưu các giá trị dữ liệu mới khi chương trình chạy. 6. Chạy - Execute .. .. .. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 11 1.4 Môi trường cơ bản cho lập trình C++ • Soạn thảo – File có kiểu mở rộng *.cpp, *cxx, *.cc, *.C – Unix/Linux: vi, emacs – MS.Windows: các môi trường soạn thảo tích hợp: Devcpp, Microssoft Visual C++, Borland C++ Builder, … • Chú ý mức độ hỗ trợ C++ chuẩn – ANSI/ISO C++ © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 12 Ví dụ 1: Hello World! 1 2 3 4 5 6 7 8 9 10 11 12 /* A first program in C++. Print a line of text to standard output */ #include <iostream> hàm main trả về một giá trị kiểu số nguyên. Chú thích // function main begins program execution int main() Định hướng tiền xử lý (preprocessor directive) để khai { báo sử dụng thư viện ra/vào chuẩn <iostream>. std::cout << “Hello World!\n"; return 0; // indicate that program ended successfully Viết một dòng ra output } // end function main chuẩn (màn hình) hàm main xuất hiện đúng một lần trong mỗi chương trình C++. Ngoặc trái { bắt đầu thân hàm. Hello World! Tương ứng, ngoặc phải } kết thúc thân hàm. Các lệnh kết thúc bằng dấu chấm phảy; Từ khóa return là một cách thoát khỏi hàm; giá trị 0 được trả về có nghĩa chương trình kết thúc thành công. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 13 1.5 Các thành phần cơ bản Chú thích và định hướng tiền xử lý • Chú thích - comment // A first program in C++. – – – – Làm tài liệu cho các chương trình Làm chương trình dễ đọc dễ hiểu hơn được trình biên dịch (compiler) bỏ qua 1 dòng chú thích bắt đầu với // • Các định hướng tiền xử lý - directive #include <iostream> – Được xử lý ngay trước khi biên dịch – Bắt đầu bằng # © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 14 Ví dụ 1 - mở rộng 1 1 2 3 // Fig. 1.4: fig01_04.cpp // Printing a line with multiple statements. #include <iostream> 4 5 6 7 8 9 // function main begins program execution int main() { std::cout << "Welcome "; std::cout << "to C++!\n"; 10 11 12 13 return 0; fig01_04.cpp Nhiều dòng lệnh tạo output trên một dòng. // indicate that program ended successfully } // end function main Welcome to C++! ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 15 Ví dụ 1 - mở rộng 2 1 2 3 // Fig. 1.5: fig01_05.cpp // Printing multiple lines with a single statement #include <iostream> 4 5 6 7 8 // function main begins program execution Dùng ký tự dòng mới \n để in trên nhiều dòng. int main() { std::cout << "Welcome\nto\n\nC++!\n"; 9 10 11 12 return 0; // indicate that program ended successfully } // end function main Welcome to C++! ©2004 Trần Minh Châu. FOTECH. VNU. 16 Ví dụ 2: Chương trình tính tổng hai số nguyên 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Enter first integer 45 Enter second integer 72 Sum is 117 // Fig. 1.6: fig01_06.cpp // Addition program. #include <iostream> // function main begins program execution int main() Khai báo các biến nguyên. { int integer1; // first number to be input by user int integer2; // second number to be input by user Nhập một số nguyên từ input int sum; // variable in which sum will be stored chuẩn, ghi vào biến integer1 std::cout << "Enter first integer\n"; std::cin >> integer1; // prompt endl // read an integer cho kết quả là một dòng trống. std::cout << "Enter second integer\n"; // prompt std::cin >> integer2; integer Tính toán có // thểread đượcan thực hiện trong lệnh output: Thay cho các dòng 18 và 20: sum = integer1 + integer2; cout // assign result is " to << sum integer1 + integer2 << endl; << "Sum std::cout << "Sum is " << sum << endl; // print sum return 0; // indicate that program ended successfully } // end function main CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 17 1.5 Các thành phần cơ bản Biến chương trình • Biến - variable: Một nơi trong bộ nhớ, có thể lưu các giá trị thuộc một kiểu nào đó. • Các kiểu dữ liệu cơ bản • • • • int - số nguyên char – ký tự double - số chấm động bool – các giá trị logic true hoặc false • Các biến phải được khai báo tên và kiểu trước khi sử dụng int integer1; int integer2; int sum; • Có thể khai báo nhiều biến thuộc cùng một kiểu dữ liệu trong một dòng khai báo biến. int integer1, integer2, sum; © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 18 1.5 Biến chương trình • Quy tắc đặt tên biến • Chuỗi ký tự (chữ cái a..z, A..Z, chữ số 0..9, dấu gạch dưới _ ) • Không được bắt đầu bằng chữ số • Phân biệt chữ hoa chữ thường. Ví dụ: Tên biến hợp lệ: h678h_m2, _adh2, taxPayment… Không hợp lệ: áadàn, so chia, 2n, … © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 19 1.5 Biến chương trình • Các khái niệm về bộ nhớ (memory) – – – – Mỗi biến tương ứng với một khu trong bộ nhớ máy tính Mỗi biến có tên, kiểu, kích thước, và giá trị Khi biến được gán một giá trị mới, giá trị cũ bị ghi đè Đọc giá trị của các biến trong bộ nhớ không làm thay đổi các biến trong bộ nhớ. © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 20 1.5 Biến chương trình std::cin >> integer1; integer1 45 integer1 45 integer2 72 integer1 45 integer2 72 – giả sử người dùng nhập 45 std::cin >> integer2; – giả sử người dùng nhập 72 sum = integer1 + integer2; sum 117 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 21 1.6 Vào ra dữ liệu Các đối tượng vào/ra cơ bản • cin • dòng dữ liệu vào chuẩn - Standard input stream • thường là từ bàn phím • cout • dòng dữ liệu ra chuẩn - Standard output stream • thường là màn hình máy tính • cerr • dòng báo lỗi chuẩn - Standard error stream • hiện các thông báo lỗi © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 22 1.6 Vào ra dữ liệu In dòng văn bản ra màn hình std::cout << "Enter first integer\n"; • // prompt Đối tượng ra chuẩn - Standard output stream object – std::cout – “nối” với màn hình – << • toán tử chèn vào dòng dữ liệu ra – stream insert operator • giá trị bên phải (right operand) được chèn vào dòng dữ liệu ra • Không gian tên - Namespace – std:: có nghĩa là sử dụng tên thuộc “namespace” std – std:: được bỏ qua nếu dùng các khai báo using • Escape characters \ – đánh dấu các ký tự đặc biệt • ví dụ \\, \’, \n, \t © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 23 1.6 Vào ra dữ liệu Các chuỗi escape Chuỗi Escape Mô tả \n Dòng mới. Đặt con trỏ màn hình tại đầu dòng tiếp theo. Tab. Di chuyển con trỏ đến điểm dừng tab tiếp theo. Về đầu dòng. Chuyển con trỏ màn hình tới đầu dòng hiện tại; không xuống dòng mới. Chuông. Bật chuông hệ thống. Chéo ngược. Dùng để in một đấu chéo ngược. Nháy kép. Dùng để in một dấu nháy kép. \t \r \a \\ \" © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 24 1.6 Vào ra dữ liệu Nhập dữ liệu từ thiết bị vào chuẩn std::cin >> integer1; // read an integer • Đối tượng dòng dữ liệu vào - Input stream object – >> (toán tử đọc từ dòng dữ liệu vào) • được sử dụng với std::cin • đợi người dùng nhập giá trị, rồi gõ phím Enter (Return) • lưu giá trị vào biến ở bên phải toán tử – đổi giá trị được nhập sang kiểu dữ liệu của biến • = (toán tử gán) – gán giá trị cho biến – toán tử hai ngôi - Binary operator – Ví dụ: sum = variable1 + variable2; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 25 1.7 Tính toán số học • Các phép toán số học – * – / Phép nhân Phép chia • Phép chia với số nguyên lấy thương là số nguyên và bỏ phần dư – 7 / 5 cho kết quả 1 • Phép chia với số thực cho kết quả là số thực – 7.0 / 5.0 cho kết quả 1.4 – % Phép lấy số dư – 7 % 5 cho kết quả 2 © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 26 1.7 Tính toán số học • Các quy tắc ưu tiên - Rules of operator precedence – Các phép toán trong ngoặc được tính trước • ngoặc lồng nhau – các phép toán ở bên trong nhất được tính trước nhất – tiếp theo là các phép nhân, chia, và phép lấy số dư • các phép toán được tính từ trái sang phải – cộng và trừ được tính cuối cùng • các phép toán được tính từ trái sang phải © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 27 1 2 3 4 // Fig. 1.14: fig01_14.cpp // Using if statements, relational // operators, and equality operators. #include <iostream> 5 6 7 8 using std::cout; using std::cin; using std::endl; 9 10 11 12 13 14 // function main begins program execution int main() Có thể viết cout và cin mà không cần tiền tố std:: { int num1; // first number to lệnh be read from if kiểm tra user xem các giá trị của int num2; // second number tonum1 be read fromcóuser và num2 bằng nhau không. // program uses cout // program uses cin // program uses endl fig01_14.cpp (1 of 2) khai báo using để sau đó không cần dùng tiền tố std:: Khai báo biến. 15 16 17 18 cout << "Enter two integers, and I will tell you\n" << "the relationships they satisfy: "; cin >> num1 >> num2; // read two integers 19 20 21 if ( num1 == num2 ) cout << num1 << " is equal to " << num2 << endl; Nếu điều kiện là đúng (nghĩa là hai giá trị bằng nhau) thì thực hiện lệnh này. lệnh if kiểm tra xem các giá trị của num1 và num2 có khác nhau không. Nếu điều kiện là đúng (nghĩa là hai giá trị 22 23 24 25 if ( num1 != num2 ) khác nhau) thì thực hiện lệnh này. cout << num1 << " is not equal to " << num2 << endl; 26 27 if ( num1 < num2 ) cout << num1 << " is less than " << num2 << endl; 28 29 30 fig01_14.cpp if ( num1 > num2 ) Một lệnh có(2 thểofđược 2) tách cout << num1 << " is greater than " << num2 << endl; ©2004 Trần Minh Châu. FOTECH. VNU. thành nhiều dòng. fig01_14.cpp output (1 of 2) 31 32 33 34 if ( num1 <= num2 ) cout << num1 << " is less than or equal to " << num2 << endl; 35 36 37 38 if ( num1 >= num2 ) cout << num1 << " is greater than or equal to " << num2 << endl; 39 40 return 0; 41 42 28 // indicate that program ended successfully } // end function main Enter two integers, and I will tell you the relationships they satisfy: 22 12 22 is not equal to 12 22 is greater than 12 22 is greater than or equal to 12 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 29 Enter two integers, and I will tell you the relationships they satisfy: 7 7 7 is equal to 7 7 is less than or equal to 7 7 is greater than or equal to 7 fig01_14.cpp output (2 of 2) ©2004 Trần Minh Châu. FOTECH. VNU. 30 1.8 Ra quyết định: Các phép toán quan hệ Ký hiệu toán học Toán tử của C++ Ví dụ điều kiện C++ Ý nghĩa của điều kiện > > x > y x lớn hơn y < < x < y x nhỏ hơn y ≥ >= x >= y x lớn hơn hoặc bằng y ≤ <= x <= y x nhỏ hơn hoặc bằng y = == x == y x bằng y ≠ != x != y x khác y © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 31 1.8 Ra quyết định: Các phép toán quan hệ • cấu trúc if – Đưa ra quyết định dựa vào kết quả đúng hoặc sai của điều kiện • Nếu điều kiện thỏa mãn thì thực hiện tập lệnh S • nếu không, tập lệnh S không được thực hiện if ( num1 == num2 ) cout << num1 << " is equal to " << num2 << endl; © 2004 Trần Minh Châu. FOTECH. VNU Chương 1. 32 1.9 Khai báo using • Khai báo sử dụng toàn bộ không gian tên – using namespace std; – Để không cần tiền tố std:: cho mọi tên trong std 1 2 3 // Fig. 1.4: fig01_04.cpp // Printing a line with multiple statements. #include <iostream> 4 5 6 7 8 9 10 using namespace std; // function main begins program execution int main() { cout << "Welcome "; std::cout << "to C++!\n"; 11 12 13 14 return 0; } // end function main © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 33 1.9 Khai báo using • Khai báo sử dụng từng tên using std::cout; // program uses cout using std::cin; // program uses cin using std::endl; // program uses endl ... cout << "No need to write std::"; cin >> somevariable; ... © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 1. https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 2 – Các kiểu dữ liệu cơ bản Các cấu trúc điều khiển © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 2 Tài liệu đọc thêm • Tài liệu đọc thêm cho chương này: – Section 2.1. Complete C++ Language Tutorial (CCLT) – Day 7. Teach Yourself C++ in 21 Days (TY21) – Namespace (Sec.5-2.CCLT) (Không bắt buộc) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 3 Chương 2 – Kiểu dữ liệu và phép toán cơ bản Cấu trúc điều khiển và cấu trúc chương trình Đề mục 2.1 Các kiểu dữ liệu cơ bản 2.2 Các phép gán tắt, phép tăng, phép giảm 2.3 Các phép toán logic 2.4 Thuật toán, mã giả, điều khiển của chương trình, sơ đồ khối 2.5 Sơ lược về các cấu trúc điều khiển 2.6 Cấu trúc lựa chọn if, if/else 2.7 Phép toán lựa chọn 3 ngôi 2.8 Cấu trúc lặp while 2.9 Thiết lập thuật toán 2.10 Điều khiển lặp bằng con đếm và giá trị canh © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. Chương 2 – Kiểu dữ liệu và phép toán cơ bản Cấu trúc điều khiển và cấu trúc chương trình 4 Đề mục (tiếp theo) 2.11 Các cấu trúc lồng nhau 2.12 Vòng lặp for 2.13 Cấu trúc đa lựa chọn switch 2.14 Vòng lặp do/while 2.15 break và continue 2.16 Sơ lược về lập trình cấu trúc © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 5 2.1 Các kiểu dữ liệu cơ bản char ký tự hoặc số nguyên 8 bit short số nguyên 16 bit long số nguyên 32 bit int số nguyên độ dài bằng 1 word (16 bit hoặc 32 bit) float số chấm động 4 byte double số chấm động 8 byte long double số chấm động 10 byte bool giá trị Boolean, true hoặc false wchar_t ký tự 2 byte, lưu bảng chữ cái quốc tế © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 6 2.2 Các phép toán cơ bản • phép gán – assignation (=) x = 5; //x: lvalue, 5: rvalue – là biểu thức có giá trị là giá trị được gán • các phép toán số học - Arithmetic operators (+, -, *, /, %) • các phép gán kép - Compound assignation operators (+=, -=, *=, /=, %=, >>=, <<=, &=, ^=, |=) • phép tăng và phép giảm (++, --) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 7 2.2 Các phép toán cơ bản • các phép quan hệ - relational operators ( ==, !=, >, <, >=, <= ) • các phép toán logic - Logic operators ( !, &&, || ) • phép điều kiện - Conditional operator ( ? ). (7 == 5 ? 4 : 3) cho kết quả 3 do 7 khác 5. • các toán tử bit - Bitwise Operators ( &, |, ^, ~, <<, >> ). © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 8 2.2 Các phép gán tắt • Các biểu thức gán tắt - Assignment expression abbreviations – Phép gán cộng c = c + 3; viết tắt thành c += 3; • Các lệnh có dạng variable = variable operator expression; có thể được viết lại thành variable operator= expression; • Các phép gán khác d e f g -= *= /= %= 4 5 3 9 (d (e (f (g = = = = d e f g * / % 4) 5) 3) 9) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 9 2.2 Các phép tăng và giảm • Phép tăng - Increment operator (++) – có thể được dùng thay cho c += 1 • Phép giảm - Decrement operator (--) – có thể được dùng thay cho c -= 1 • Tăng/giảm trước – Preincrement/Predecrement • ++c hoặc --c • Giá trị của biến bị thay đổi, sau đó biểu thức chứa nó được tính giá trị. • Biểu thức có giá trị là giá trị của biến sau khi tăng/giảm • Tăng/giảm sau - Postincrement/Predecrement • c++ hoặc c-• Biểu thức chứa biến được thực hiện, sau đó biến được thay đổi. • Biểu thức có giá trị là giá trị của biến trước khi tăng/giảm © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 10 2.2 Các phép tăng và giảm • Ví dụ: nếu c = 5 – cout << ++c; • c nhận giá trị 6, rồi được in ra – cout << c++; • in giá trị 5 (cout được chạy trước phép tăng). • sau đó, c nhận giá trị 6 • Khi biến không nằm trong biểu thức – Tăng trước và tăng sau có kết quả như nhau ++c; cout << c; và c++; cout << c; là như nhau © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 11 // Fig. 2.14: fig02_14.cpp // Preincrementing and postincrementing. #include <iostream> fig02_14.cpp (1 of 2) using std::cout; using std::endl; // function main begins program execution int main() { int c; // declare variable 5 5 6 12 13 14 15 16 17 // demonstrate postincrement c = 5; // cout << c << endl; // cout << c++ << endl; // cout << c << endl << endl; // assign 5 to c print 5 print 5 then postincrement print 6 18 19 20 21 22 23 // demonstrate preincrement c = 5; // cout << c << endl; // cout << ++c << endl; // cout << c << endl; // assign 5 to c print 5 preincrement then print 6 print 6 24 25 return 0; 26 27 5 6 6 // indicate successful termination ©2004 Trần Minh Châu. FOTECH. VNU. } // end function main 12 2.3 Các phép toán logic • được dùng làm điều kiện trong các vòng lặp và lệnh if • && (logical AND) – true nếu cả hai điều kiện là true if ( gender == 1 && age >= 65 ) ++seniorFemales; • || (logical OR) – true nếu ít nhất một trong hai điều kiện là true if ( semesterAverage >= 90 || finalExam >= 90 ) cout << "Student grade is A" << endl; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 13 2.3 Các phép toán logic • ! (logical NOT, phủ định logic – logical negation) – trả về giá trị true khi điều kiện là false, và ngược lại if ( !( grade == sentinelValue ) ) cout << "The next grade is " << grade << endl; tương đương với: if ( grade != sentinelValue ) cout << "The next grade is " << grade << endl; © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 14 Nhầm lẫn giữa phép so sánh bằng (==) và phép gán (=) • Lỗi thường gặp – Thường không tạo lỗi cú pháp (syntax error) • Các khía cạnh của vấn đề – biểu thức có giá trị có thể được dùng làm điều kiện • bằng không = false, khác không = true – Các lệnh gán cũng tạo giá trị (giá trị được gán) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 15 Nhầm lẫn giữa phép so sánh bằng (==) và phép gán (=) • Ví dụ if ( 4 == payCode ) cout << "You get a bonus!" << endl; – Nếu mã tiền lương (paycode) là 4 thì thưởng • Nếu == bị thay bởi = if ( payCode = 4 ) cout << "You get a bonus!" << endl; – Paycode được gán giá trị 4 (không cần biết giá trị của paycode trước đó) – lệnh gán cho giá trị true (vì 4 khác 0) – trường hợp nào cũng được thưởng © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 16 Nhầm lẫn giữa phép so sánh bằng (==) và phép gán (=) • Lvalue – là biểu thức có thể xuất hiện tại vế trái của phép gán – xác định một vùng nhớ có thể được gán trị (i.e, các biến) • x = 4; • Rvalue – chỉ xuất hiện bên phải phép gán – hằng, các giá trị (literal) • không thể viết 4 = x; • Lvalue có thể được dùng như các rvalue, nhưng chiều ngược lại là không thể © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 17 Viết chương trình • Trước khi viết chương trình – Hiểu kỹ bài toán – Lập kế hoạch giải quyết bài toán • Trong khi viết chương trình – Biết lời giải có sẵn cho các bài toán con – Sử dụng các nguyên lý lập trình tốt © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 18 Thuật toán - Algorithm • Các bài toán tin học – được giải bằng cách thực hiện một chuỗi hành động theo một thứ tự cụ thể • Thuật toán: một quy trình quyết định – Các hành động cần thực hiện – Thứ tự thực hiện – Ví dụ: cách nấu một món ăn • Điều khiển của chương trình – Program Control – Chỉ ra thứ tự thực hiện các lệnh © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 19 Mã giả - Pseudocode • Mã giả: ngôn ngữ không chính thức được dùng để mô tả thuật toán – tương tự với ngôn ngữ hàng ngày • Không chạy được trên máy tính – dùng để mô tả chương trình trước khi viết chương trình • dễ chuyển thành chương trình C++ – chỉ gồm các lệnh chạy • không cần khai báo biến Ví dụ: tìm số nhỏ hơn trong hai số 1. nhập 2 số x,y 2. nếu x>y thì in y ra màn hình 3. nếu không, in x ra màn hình © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. Các cấu trúc điều khiển - Control Structures Khái niệm 20 • Thực thi tuần tự - Sequential execution – Các lệnh được thực hiện theo thứ tự tuần tự • Chuyển điều khiển - Transfer of control – Lệnh tiếp theo được thực thi không phải lệnh tiếp theo trong chuỗi lệnh. • 3 cấu trúc điều khiển – Cấu trúc tuần tự - Sequence structure • theo mặc định, chương trình chạy tuần tự từng lệnh – Các cấu trúc chọn lựa - Selection structures • if, if/else, switch – Các cấu trúc lặp - Repetition structures • while, do/while, for © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 21 Các cấu trúc điều khiển • Các từ khóa của C++ – Không thể dùng làm tên biến hoặc tên hàm C++ Keyw o rd s Keywords common to the C and C++ programming languages auto continue enum if short switch volatile C++ only keywords asm delete inline private static_cast try wchar_t break default extern int signed typedef while case do float long sizeof union char double for register static unsigned const else goto return struct void bool dynamic_cast mutable protected template typeid catch explicit namespace public this typename class false new reinterpret_cast throw using const_cast friend operator true virtual © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 22 Các cấu trúc điều khiển true grade >= 60 print “Passed” false • Sơ đồ khối - Flowchart – mô tả thuật toán bằng hình vẽ – gồm các ký hiệu đặc biệt được nối bằng các mũi tên (flowlines) – Hình chữ nhật (ký hiệu hành động) • kiểu hành động bất kỳ – ký hiệu oval • Bắt đầu hoặc kết thúc một chương trình, hoặc một đoạn mã (hình tròn) • Các cấu trúc điều khiển có đúng 1 đầu vào, 1 đầu ra – Kết nối đầu ra của một cấu trúc điều khiển với đầu vào của cấu trúc tiếp theo – xếp chồng các cấu trúc điều khiển © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 23 Cấu trúc lựa chọn if • Cấu trúc lựa chọn - Selection structure – chọn giữa các tuyến hành động khác nhau – ví dụ bằng mã giả: If student’s grade is greater than or equal to 60 Print “Passed” – Nếu điều kiện thỏa mãn (có giá trị true) • lệnh Print được thực hiện, chương trình chạy tiếp lệnh tiếp theo – Nếu điều kiện không thỏa mãn (có giá trị false) • lệnh Print bị bỏ qua, chương trình chạy tiếp – Cách viết thụt đầu dòng làm chương trình dễ đọc hơn • C++ bỏ qua các ký tự trắng (tab, space, etc.) © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 24 Cấu trúc lựa chọn if • Dịch sang C++ If student’s grade is greater than or equal to 60 Print “Passed” grade >= 60 if ( grade >= 60 ) cout << "Passed"; true print “Passed” false • ký hiệu hình thoi (ký hiệu quyết định) – đánh đấu chọn lựa cần thực hiện – chứa một biểu thức có giá trị true hoặc false • kiểm tra điều kiện, đi theo đường thích hợp • cấu trúc if – Single-entry/single-exit Một biểu thức bất kỳ đều có thể được sử dụng làm điều kiện cho lựa chọn. bằng 0 - false khác 0 - true Ví dụ: 3 - 4 có giá trị true © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 25 Cấu trúc chọn lựa if/else • if – Thực hiện hành động nếu điều kiện thỏa mãn • if/else – thực hiện những hành động khác nhau tùy theo điều kiện được thỏa mãn hay không • mã giả if student’s grade is greater than or equal to 60 print “Passed” else print “Failed” • mã C++ if ( grade >= 60 ) cout << "Passed"; else cout << "Failed"; © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 26 Cấu trúc chọn lựa if/else • phép toán điều kiện 3 ngôi (?:) – ba tham số (điều kiện, giá trị nếu true, giá trị nếu false) • mã có thể được viết: cout << ( grade >= 60 ? “Passed” : “Failed” ); Condition false print “Failed” Value if true grade >= 60 Value if false true print “Passed” © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 27 Cấu trúc chọn lựa if/else • Các cấu trúc if/else lồng nhau – lệnh này nằm trong lệnh kia, kiểm tra nhiều trường hợp – Một khi điều kiện thỏa mãn, các lệnh khác bị bỏ qua if student’s grade is greater than or equal to 90 Print “A” else if student’s grade is greater than or equal to 80 Print “B” else if student’s grade is greater than or equal to 70 Print “C” else if student’s grade is greater than or equal to 60 Print “D” else Print “F” © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 28 Cấu trúc chọn lựa if/else • Ví dụ if ( grade >= 90 ) cout << "A"; else if ( grade >= 80 ) cout << "B"; else if ( grade >= 70 ) cout << "C"; else if ( grade >= 60 ) cout << "D"; else cout << "F"; // 90 and above // 80-89 // 70-79 // 60-69 // less than 60 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 29 Cấu trúc chọn lựa if/else • lệnh phức – compound statement – tập lệnh bên trong một cặp ngoặc if ( grade cout << else { cout << cout << >= 60 ) "Passed.\n"; "Failed.\n"; "You must take this course again.\n"; } – nếu không có ngoặc, cout << "You must take this course again.\n"; sẽ luôn được thực hiện • Khối chương trình - Block – tập lệnh bên trong một cặp ngoặc © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 30 Cấu trúc lặp while • Cấu trúc lặp - Repetition structure – hành động được lặp đi lặp lại trong khi một điều kiện nào đó còn được thỏa mãn – mã giả Trong khi vẫn còn tên hàng trong danh sách đi chợ của tôi Mua mặt hàng tiếp theo và gạch tên nó ra khỏi danh sách – vòng while lặp đi lặp lại cho đến khi điều kiện không thỏa mãn © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 31 Cấu trúc lặp while • Ví dụ int product = 2; while ( product <= 1000 ) product = 2 * product; • Sơ đồ khối của vòng while true product <= 1000 product = 2 * product false © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 32 Thiết lập thuật toán (Điều khiển lặp bằng con đếm) • Vòng lặp được điều khiển bằng con đếm (counter) – Lặp đến khi con đếm đạt đến giá trị nào đó • Lặp hữu hạn - Definite repetition – số lần lặp biết trước • Ví dụ Một lớp gồm 10 sinh viên làm một bài thi. Cho biết các điểm thi (số nguyên trong khoảng từ 0 đến 100). Tính trung bình điểm thi của lớp. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 33 Thiết lập thuật toán (Điều khiển lặp bằng con đếm) • Mã giả cho ví dụ: Đặt tổng bằng 0 Đặt con đếm bằng 1 Trong khi con đếm nhỏ hơn hoặc bằng 10 Nhập điểm tiếp theo Cộng điểm đó vào tổng Thêm 1 vào con đến Đặt trung bình lớp bằng tổng chia cho 10 In trung bình lớp • Tiếp theo: Mã C++ cho ví dụ trên © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 1 2 3 // Fig. 2.7: fig02_07.cpp // Class average program with counter-controlled repetition. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 13 14 15 // function main begins int main() { int total; // int gradeCounter; // int grade; // int average; // 16 17 18 19 34 fig02_07.cpp (1 of 2) program execution sum of grades input by user number of grade to be entered next grade value average of grades // initialization phase total = 0; // initialize total gradeCounter = 1; // initialize loop counter 20 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 35 21 22 23 24 25 26 27 // processing phase while ( gradeCounter <= 10 ) { cout << "Enter grade: "; cin >> grade; total = total + grade; gradeCounter = gradeCounter + 1; } 28 29 30 // termination phase average = total / 10; 31 32 33 // display result cout << "Class average is " << average << endl; 34 35 return 0; 36 37 // // // // // loop 10 times prompt for input read grade from user add grade to total increment counter fig02_07.cpp (2 of 2) fig02_07.cpp output (1 of 1) Con đếm được division tăng thêm 1 mỗi lần vòng lặp chạy. // integer Cuối cùng, con đếm làm vòng lặp kết thúc. // indicate program ended successfully } // end function main Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Class grade: 98 grade: 76 grade: 71 grade: 87 grade: 83 grade: 90 grade: 57 grade: 79 grade: 82 grade: 94 average is 81 ©2004 Trần Minh Châu. FOTECH. VNU. 36 Thiết lập thuật toán (Điều khiển lặp bằng lính canh) • Giả sử bài toán trở thành: Viết một chương trình tính điểm trung bình của lớp, chương trình sẽ xử lý một số lượng điểm tùy ý mỗi khi chạy chương trình. – Số sinh viên chưa biết – Chương trình sẽ làm thế nào để biết khi nào thì kết thúc? • Giá trị canh – Ký hiệu “Kết thúc của dữ liệu vào” – Vòng lặp kết thúc khi nhập canh – Canh được chọn để không bị lẫn với dữ liệu vào thông thường • trong trường hợp này là -1 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 37 Thiết lập thuật toán (Điều khiển lặp bằng lính canh) • Thiết kế từ trên xuống, làm mịn từng bước – Bắt đầu bằng mã giả cho mức cao nhất Tính trung bình điểm thi của lớp – Chia thành các nhiệm vụ nhỏ hơn, liệt kê theo thứ tự Khởi tạo các biến Nhập, tính tổng, và đếm các điểm thi Tính và in trung bình điểm thi © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 38 Thiết lập thuật toán (Điều khiển lặp bằng lính canh) • Nhiều chương trình có 3 pha – Khởi tạo - Initialization • Khởi tạo các biến chương trình – Xử lý - Processing • Nhập dữ liệu, điều chỉnh các biến trong chương trình – Kết thúc - Termination • Tính và in kết quả cuối cùng – Giúp việc chia nhỏ chương trình để làm mịn từ trên xuống © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 39 Thiết lập thuật toán (Điều khiển lặp bằng lính canh) • Làm mịn pha khởi tạo Khởi tạo các biến thành Khởi tạo tổng bằng 0 Khởi tạo biến đếm bằng 0 • Xử lý Nhập, tính tổng, và đếm các điểm thi thành Nhập điểm đầu tiên (có thể là canh) Trong khi người dùng còn chưa nhập canh Cộng điểm vừa nhập vào tổng Cộng thêm 1 vào biến đếm điểm Nhập điểm tiếp theo (có thể là canh) © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 40 Thiết lập thuật toán (Điều khiển lặp bằng lính canh) • Kết thúc Tính và in trung bình điểm thi thành Nếu con đếm khác 0 Đặt trung bình bằng tổng chia cho con đếm In giá trị trung bình Nếu không In “Không nhập điểm nào” • Tiếp theo: chương trình C++ © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using using using using fig02_09.cpp (1 of 3) std::cout; std::cin; std::endl; std::fixed; #include <iomanip> // parameterized stream manipulators using std::setprecision; // sets numeric output precision // function main begins program execution int main() { Dữ liệu kiểu double dùng để int total; // sum of grades biểu diễn sốentered thập phân. int gradeCounter; // number of grades int grade; // grade value 20 21 double average; 22 23 24 25 // initialization phase total = 0; // initialize total gradeCounter = 0; // initialize loop counter 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 41 // Fig. 2.9: fig02_09.cpp // Class average program with sentinel-controlled repetition. #include <iostream> // number with decimal point for average ©2004 Trần Minh Châu. FOTECH. VNU. 42 // processing phase // get first grade from user cout << "Enter grade, -1 to end: "; cin >> grade; // prompt for input // read grade from user fig02_09.cpp (2 of 3) // loop until sentinel value read from user while ( grade != -1 ) { total = total + grade; // add grade to total gradeCounter = gradeCounter + 1; // increment counter cout << "Enter grade, -1 to end: "; cin >> grade; // prompt for input // read next grade } // end while // termination phase // if user entered at least one grade ... if ( gradeCounter != 0 ) { // calculate average of all grades entered average = static_cast< double >( total ) / gradeCounter; static_cast<double>() coi total như một double tạm thời (casting). ©2004 Trần Minh Châu. Cần thiết vì phép chia số nguyên bỏ qua phần dư. gradeCounter là một biến int, nhưng nó được nâng lên kiểu double.FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 49 50 51 43 // display average with two digits of precision cout << "Class average is " << setprecision( 2 ) << fixed << average << endl; 52 53 } // end if part of if/else fig02_09.cpp (3 of 3) 54 55 56 else // if no grades were entered, output appropriate message cout << "No grades were entered" << endl; fig02_09.cpp output (1 of 1) 57 58 return 0; 59 60 // indicate program ended successfully } // end function main Enter Enter Enter Enter Enter Enter Enter Enter Enter Class grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: grade, -1 to end: average is 82.50 75 94 97 88 70 64 83 89 -1 fixed làm số liệu ra được in theo dạng thông thường (không phải dạng ký hiệu khoa học); qui định in cả các chữ số 0 ở sau và in dấu chấm thập phân. setprecision(2)in hai chữ số sau dấu phảy (làm tròn theo độ chính xác quy định). Các chương trình dùng hàm này phải include <iomanip> Include <iostream> ©2004 Trần Minh Châu. FOTECH. VNU. 44 Các cấu trúc điều khiển lồng nhau • Phát biểu bài toán Một trường có danh sách kết quả thi (1 = đỗ, 2 = trượt) của 10 sinh viên. Viết một chương trình phân tích kết quả thi. Nếu có nhiều hơn 8 sinh viên đỗ thì in ra màn hình dòng chữ “Tăng tiền học phí". • Lưu ý – Chương trình xử lý 10 kết quả thi • số lần lặp cố định, sử dụng vòng lặp điều khiển bằng biến đếm – Có thể sử dụng hai con đếm • Một con đếm để đếm số lượng đỗ • Một con đếm khác đếm số lương trượt – Mỗi kết quả thi chỉ là 1 hoặc 2 • Nếu không phải 1 thì coi là 2 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 45 Các cấu trúc điều khiển lồng nhau • Phác thảo mức cao nhất - Top level outline Analyze exam results and decide if tuition should be raised • Làm mịn lần một - First refinement Initialize variables Input the ten quiz grades and count passes and failures Print a summary of the exam results and decide if tuition should be raised • Làm mịn - Refine Initialize variables to Initialize passes to zero Initialize failures to zero Initialize student counter to one © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 46 Các cấu trúc điều khiển lồng nhau • Refine Input the ten quiz grades and count passes and failures to While student counter is less than or equal to ten Input the next exam result If the student passed Add one to passes Else Add one to failures Add one to student counter © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 47 Các cấu trúc điều khiển lồng nhau • tiếp tục làm mịn Print a summary of the exam results and decide if tuition should be raised to Print the number of passes Print the number of failures If more than eight students passed Print “Raise tuition” • Program next © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 2.11: fig02_11.cpp // Analysis of examination results. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 13 14 15 16 // function main begins program execution int main() { // initialize variables in declarations int passes = 0; // number of passes int failures = 0; // number of failures int studentCounter = 1; // student counter int result; // one exam result 17 18 19 20 21 22 23 Chương 2. 48 fig02_11.cpp (1 of 2) // process 10 students using counter-controlled loop while ( studentCounter <= 10 ) { // prompt user for input and obtain value from user cout << "Enter result (1 = pass, 2 = fail): "; cin >> result; 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 29 30 fig02_11.cpp (2 of 2) else // if result not 1, increment failures failures = failures + 1; 31 32 33 34 35 } // end while 36 37 38 39 // termination phase; display number of passes and failures cout << "Passed " << passes << endl; cout << "Failed " << failures << endl; 40 41 42 43 // if more than eight students passed, print "raise tuition" if ( passes > 8 ) cout << "Raise tuition " << endl; 44 45 return 0; 46 47 49 // if result 1, increment passes; if/else nested in while if ( result == 1 ) // if/else nested in while passes = passes + 1; // increment studentCounter so loop eventually terminates studentCounter = studentCounter + 1; // successful termination } // end function main ©2004 Trần Minh Châu. FOTECH. VNU. Enter result Enter result Enter result Enter result Enter result Enter result Enter result Enter result Enter result Enter result Passed 6 Failed 4 (1 (1 (1 (1 (1 (1 (1 (1 (1 (1 = = = = = = = = = = pass, pass, pass, pass, pass, pass, pass, pass, pass, pass, 2 2 2 2 2 2 2 2 2 2 = = = = = = = = = = fail): fail): fail): fail): fail): fail): fail): fail): fail): fail): 1 2 2 1 1 1 2 1 1 2 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Enter result (1 Passed 9 Failed 1 Raise tuition = = = = = = = = = = pass, pass, pass, pass, pass, pass, pass, pass, pass, pass, 2 2 2 2 2 2 2 2 2 2 = = = = = = = = = = fail): fail): fail): fail): fail): fail): fail): fail): fail): fail): 1 1 1 1 2 1 1 1 1 1 CuuDuongThanCong.com 50 fig02_11.cpp output (1 of 1) ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 51 Những điểm quan trọng về vòng lặp điều khiển bằng con đếm • vòng lặp điều khiển bằng con đếm đòi hỏi – Tên của biến điều khiển(control variable) hay biến đếm (loop counter) – Giá trị khởi tạo của biến điều khiển – Điều kiện kiểm tra giá trị cuối cùng – Tăng/giảm biến đếm khi thực hiện vòng lặp int counter = 1; // initialization while ( counter <= 10 ) { cout << counter << endl; ++counter; } // repetition condition // display counter // increment © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 1 2 3 // Fig. 2.16: fig02_16.cpp // Counter-controlled repetition. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 // function main begins program execution int main() { int counter = 1; // initialization 12 13 14 15 while ( counter <= 10 ) { cout << counter << endl; ++counter; 16 17 } // end while 18 19 return 0; 20 21 fig02_16.cpp (1 of 1) // repetition condition // display counter // increment // indicate successful termination } // end function main 52 1 2 3 4 5 6 7 8 9 10 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 53 Cấu trúc vòng lặp for • Dạng tổng quát của vòng for for ( khởi_tạo; điều_kiện_lặp; tăng/giảm ) lệnh • Ví dụ for( int counter = 1; counter <= 10; counter++ ) cout << counter << endl; – In các số nguyên từ 1 đến 10 Không có dấu ; ở cuối © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 1 2 3 // Fig. 2.17: fig02_17.cpp // Counter-controlled repetition with the for structure. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 12 // function main begins program execution int main() { // Initialization, repetition condition and incrementing // are all included in the for structure header. fig02_17.cpp (1 of 1) 13 14 15 for ( int counter = 1; counter <= 10; counter++ ) cout << counter << endl; 16 17 return 0; 18 19 // indicate successful termination } // end function main 54 1 2 3 4 5 6 7 8 9 10 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 55 Cấu trúc vòng lặp for • vòng for thường có thể viết được thành vòng while tương đương khởi_tạo; while ( điều_kiện_lặp){ lệnh tăng/giảm biến đếm; } • Khởi tạo và tăng biến đếm – nếu sử dụng nhiều biến đếm, sử dụng dấu phảy để tách for (int i = 0, j = 0; j + i <= 10; j++, i++) cout << j + i << endl; © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 2.20: fig02_20.cpp // Summation with for. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 Chương 2. 56 fig02_20.cpp (1 of 1) // function main begins program execution int main() { int sum = 0; // initialize sum 12 13 14 15 // sum even integers from 2 through 100 for ( int number = 2; number <= 100; number += 2 ) sum += number; // add number to sum 16 17 18 cout << "Sum is " << sum << endl; return 0; 19 20 fig02_20.cpp output (1 of 1) // output sum // successful termination } // end function main Sum is 2550 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 57 Ví dụ sử dụng vòng for • Chương trình tính lãi kép (compound interest) Một người đầu tư $1000.00 vào một tài khoản tiết kiệm với lãi suất 5%. Giả sử tiền lãi được gộp với vốn trong tài khoảng, tính và in ra số tiền trong tài khoản vào cuối mỗi năm trong vòng 10 năm. Sử dụng công thức sau để tính các khoản tiền đó: • n a = p(1+r) p : khoản đầu tư ban đầu (i.e., the principal), r : lãi suất hàng năm, (interest rate) n : số năm, và a : lượng tiền có trong tài khoản (amount on deposit) vào cuối năm thứ n • © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 58 1 2 3 // Fig. 2.21: fig02_21.cpp // Calculating compound interest. #include <iostream> 4 5 6 7 8 using using using using 9 10 #include <iomanip> 11 12 13 using std::setw; using std::setprecision; 14 15 #include <cmath> 16 17 18 19 20 21 22 // function main begins program execution int main() { double amount; // amount on deposit double principal = 1000.0; // starting principal double rate = .05; // interest rate fig02_21.cpp (1 of 2) std::cout; std::endl; std::ios; std::fixed; <cmath> header cần cho hàm pow (chương trình sẽ không dịch nếu không có khai báo này). // enables program to use function pow 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 // output table column heads cout << "Year" << setw( 21 ) << "Amount on deposit" << endl; 26 27 28 // set floating-point number format cout << fixed << setprecision( 2 ); 29 30 31 // calculate amount on deposit for each of ten years for ( int year = 1; year <= 10; year++ ) { 32 33 34 // calculate new amount for specified year amount = principal * pow( 1.0 + rate, year ); 35 36 37 38 // output one table row cout << setw( 4 ) << year << setw( 21 ) << amount << endl; Đặt độ rộng của output ít nhất 21 ký tự. Nếu output ít hơn 21 kýfig02_21.cpp tự thì căn (2 of 2) phải. pow(x,y) = x mũ y 39 40 } // end for 41 42 return 0; 43 44 59 // indicate successful termination } // end function main ©2004 Trần Minh Châu. FOTECH. VNU. Year 1 2 3 4 5 6 7 8 9 10 60 Amount on deposit 1050.00 1102.50 1157.63 1215.51 1276.28 1340.10 1407.10 1477.46 1551.33 1628.89 fig02_21.cpp output (1 of 1) Các số được căn phải do các lệnh setw (với tham số có giá trị 4 và 21). ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 61 Cấu trúc đa lựa chọn switch • switch – Test biến với nhiều giá trị – chuỗi các nhãn case – trường hợp default không bắt buộc switch ( variable ) { case value1: statements break; case value2: case value3: statements break; default: statements break; // taken if variable == value1 // necessary to exit switch // taken if variable == value2 or == value3 // taken if variable matches no other cases } © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 62 Cấu trúc đa lựa chọn switch true case a case a action(s) break case b action(s) break case z action(s) break false true case b false . . . true case z false default action(s) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 63 Cấu trúc đa lựa chọn switch • Ví dụ sắp tới – Chương trình đọc xếp loại điểm (A-F) – Hiện số lượng mỗi xếp loại được nhập • Chi tiết về các ký tự – Các ký tự đơn thường được lưu bằng kiểu dữ liệu char • char: số nguyên 1-byte, Æcó thể được lưu dưới dạng các giá trị int – Có thể coi ký tự là int hoặc char • 97 là biểu diễn dạng số của chữ ‘a’ thường (ASCII) • dùng cặp nháy đơn để lấy biểu diễn chữ của ký tự cout << "The character (" << 'a' << ") has the value " << static_cast< int > ( 'a' ) << endl; In ra dòng: The character (a) has the value 97 © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 2.22: fig02_22.cpp // Counting letter grades. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 13 14 15 16 17 // function main begins program execution int main() { int grade; // one grade int aCount = 0; // number of As int bCount = 0; // number of Bs int cCount = 0; // number of Cs int dCount = 0; // number of Ds int fCount = 0; // number of Fs 18 19 20 Chương 2. 64 fig02_22.cpp (1 of 4) cout << "Enter the letter grades." << endl << "Enter the EOF character to end input." << endl; 21 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 23 24 25 26 65 // loop until user types end-of-file key sequence while ( ( grade = cin.get() ) != EOF ) {break kết thúc lệnh switch và chương trình chạy tiếp tại lệnh đầu tiên sau cấu trúc switch. fig02_22.cpp // determine which grade was input (2 of 4) switch ( grade ) { // switch structure nested in while 27 28 29 30 31 case 'A': case 'a': ++aCount; break; 32 33 34 35 36 case 'B': case 'b': ++bCount; break; 37 38 39 40 41 Các lệnh gán là biểu thức có // grade was uppercase B giá trị bằng biến bên trái dấu // or lowercase b gán =. Giá trị của lệnh này // increment bCount bằng giá trị trả về bởi hàm // exit switch cin.get(). case 'C': case 'c': ++cCount; break; // grade was uppercase C Đặc điểm này còn được sử // or lowercase dụng để khởi tạo nhiềucbiến // increment cCount một lúc: // exit switch a = b = c = 0; // // // // cin.get() sử dụng dot notation grade was uppercase A (ký hiệu kiểu dấu chấm). Hàm này or lowercase a đọc một ký tự từ bàn phím (sau khi increment aCount nhấn Enter), và gán giá trị đó cho necessary to exit switch biến grade. cin.get() trả về EOF (end-offile), sau khi ký tự EOF được nhập, để đánh dấu kết thúc của dữ liệu vào. EOF có thể là ctrl-d hoặc ctrl-z, tùy theo hệ điều hành. (MS-Windows: ctrl-z, Unix/Linux: ctrl-d) 42 So sánh grade (một biến int) với biểu diễn số của A và a. ©2004 Trần Minh Châu. FOTECH. VNU. 66 43 44 45 46 case 'D': case 'd': ++dCount; break; // // // // 47 48 49 50 51 case 'F': case 'f': ++fCount; break; // exit switch 52 53 54 55 56 case '\n': case '\t': case ' ': break; 57 58 59 60 61 default: // catch all other characters cout << "Incorrect letter grade entered." << " Enter a new grade." << endl; break; // optional; will exit switch anyway 62 63 64 65 grade was uppercase D or lowercase d increment dCount exit switch fig02_22.cpp (3 of 4) Kiểm tra này là cần thiết vì Enter được nhấn sau // grade was uppercase F xếp loại được nhập. Việc nhấn Enter mỗi chữ cái // or lowercase tạo f một ký tự xuống dòng cần được loại bỏ. Cũng // increment fCount như vậy, ta muốn bỏ qua các ký tự trắng. // // // // ignore newlines, tabs, and spaces in input exit switch Lưu ý trường hợp default bao gồm tất cả các trường hợp còn lại (chưa xét đến). } // end switch } // end while 66 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 67 68 69 70 71 72 73 74 // output summary of results cout << "\n\nTotals for each letter grade are:" << "\nA: " << aCount // display number of << "\nB: " << bCount // display number of << "\nC: " << cCount // display number of << "\nD: " << dCount // display number of << "\nF: " << fCount // display number of << endl; 75 76 return 0; 77 78 67 A B C D F grades grades grades grades grades fig02_22.cpp (4 of 4) // indicate successful termination } // end function main ©2004 Trần Minh Châu. FOTECH. VNU. 68 Enter the letter grades. Enter the EOF character to end input. a B c C A d f C E Incorrect letter grade entered. Enter a new grade. D A b ^Z fig02_22.cpp output (1 of 1) Totals for each letter grade are: A: 3 B: 2 C: 3 D: 2 F: 1 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 69 Cấu trúc lặp do/while • Tương tự cấu trúc while – Kiểm tra điều kiện tiếp tục lặp ở cuối, không kiểm tra ở đầu – Thân vòng lặp chạy ít nhất một lần action(s) • Công thức do { statements } while ( condition ); true condition false © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 1 2 3 // Fig. 2.24: fig02_24.cpp // Using the do/while repetition structure. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 70 fig02_24.cpp (1 of 1) { int counter = 1; // initialize counter 12 13 14 15 do { cout << counter << " "; } while ( counter++ <= 10 ); // display counter // end do/while 16 17 cout << endl; 18 19 return 0; 20 21 1 fig02_24.cpp output (1 of 1) Chú ý phép tăng trước (preincrement) // function main begins program execution trong phần kiểm tra điều kiện lặp. int main() // indicate successful termination } // end function main 2 3 4 5 6 7 8 9 10 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 71 Các lệnh break và continue • break – Thoát ngay ra khỏi các cấu trúc while, for, do/while, switch – Chương trình tiếp tục chạy tại lệnh đầu tiên ngay sau cấu trúc • thường được sử dụng để – Thoát khỏi vòng lặp sớm hơn bình thường – bỏ qua phần còn lại của switch © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Chương 2. 72 // Fig. 2.26: fig02_26.cpp // Using the break statement in a for structure. #include <iostream> fig02_26.cpp using std::cout; using std::endl; 1 2 3 4 (1 of 2) Broke out of loop when x became 5 // function main begins program execution int main() { int x; // x declared here so it can be used after the loop // loop 10 times for ( x = 1; x <= 10; x++ ) { Thoát khỏi vòng for khi break được thực thi. // if x is 5, terminate loop if ( x == 5 ) break; // break loop only if x is 5 cout << x << " "; // display value of x } // end for cout << "\nBroke out of loop when x became " << x << endl; return 0; // indicate successful termination } // end function main CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 73 Các lệnh break và continue • continue – được dùng trong while, for, do/while – bỏ qua phần còn lại của thân vòng lặp – chạy tiếp lần lặp tiếp theo • với các vòng while và do/while – thực hiện kiểm tra điều kiện lặp ngay sau lệnh continue • với vòng for – biểu thức tăng/giảm biến đếm được thực hiện – sau đó, điều kiện lặp được kiểm tra © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Chương 2. 74 // Fig. 2.27: fig02_27.cpp // Using the continue statement in a for structure. #include <iostream> fig02_27.cpp using std::cout; using std::endl; 1 2 3 4 6 7 8 9 10 (1 of 2) value 5 Used continue to skip printing the // function main begins program execution int main() Bỏ qua phần còn lại của thân vòng { for, nhảy đến lần lặp tiếp theo. // loop 10 times for ( int x = 1; x <= 10; x++ ) { // if x is 5, continue with next iteration of loop if ( x == 5 ) continue; // skip remaining code in loop body cout << x << " "; // display value of x } // end for structure cout << "\nUsed continue to skip printing the value 5" << endl; return 0; } // end function main CuuDuongThanCong.com // indicate successful termination ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 75 Lập trình cấu trúc Structured-Programming • Lập trình cấu trúc – Structured programming – Chương trình dễ hiểu, test, tìm lỗi (debug) và dễ sửa đổi hơn • Các quy tắc lập trình cấu trúc – Chỉ sử dụng các cấu trúc điều khiển một đầu vào một đầu ra – Quy tắc 1) Bắt đầu bằng một sơ đồ khối đơn giản nhất 2) Mỗi hình chữ nhật (hành động) có thể được thay bằng một chuỗi gồm 2 hình chữ nhật khác 3) Mỗi hình chữ nhật (hành động) có thể được thay bằng một cấu trúc điều khiển tùy ý (tuần tự, if, if/else, switch, while, do/while hoặc for) 4) Các quy tắc 2 và 3 có thể được áp dụng nhiều lần và theo thứ tự tùy ý © 2004 Trần Minh Châu. FOTECH. VNU Chương 2. 76 Lập trình cấu trúc Structured-Programming Mô tả quy tắc 3 (thay một hình chữ nhật tùy ý bằng một cấu trúc điều khiển) Qui tắc 3 Qui tắc 3 Qui tắc 3 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 2. https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 3 – Hàm © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 2 Chương 3 - Hàm Đề mục 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15 Giới thiệu Các thành phần của chương trình C++ Các hàm trong thư viện toán học Hàm Định nghĩa hàm (Function Definition) Nguyên mẫu hàm (Function Prototype) Header File Sinh số ngẫu nhiên Ví dụ: Trò chơi may rủi và Giới thiệu về kiểu enum Các kiểu lưu trữ (Storage Class) Các quy tắc phạm vi (Scope Rule) Đệ quy (Recursion) Ví dụ sử dụng đệ quy: chuỗi Fibonacci So sánh Đệ quy và Vòng lặp Hàm với danh sách đối số rỗng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 3 Chương 3 - Hàm Đề mục 3.16 3.17 3.18 3.19 3.20 3.21 Hàm Inline Tham chiếu và tham số là tham chiếu Đối số mặc định Toán tử phạm vi đơn (Unary Scope Resolution Operator) Chồng hàm (Function Overloading) Khuôn mẫu hàm (Function Templates) © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 4 3.1 Giới thiệu • Chia để trị - Divide and conquer – Xây dựng một chương trình từ các thành phần (component) nhỏ hơn – Quản lý từng thành phần dễ quản lý hơn quản lý chương trình ban đầu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 5 3.2 Các thành phần của chương trình C++ • Các module: các hàm(function) và lớp(class) • Các chương trình sử dụng các module mới và đóng gói sẵn (“prepackaged”) – Mới: các hàm và lớp do lập trình viên tự định nghĩa – Đóng gói sẵn: các hàm và lớp từ thư viện chuẩn • lời gọi hàm - function call – tên hàm và các thông tin (các đối số - arguments) mà nó cần • định nghĩa hàm - function definition – chỉ viết một lần – được che khỏi các hàm khác • tương tự – Một ông chủ (hàm gọi - the calling function or caller) đề nghị một công nhân (hàm được gọi - the called function) thực hiện một nhiệm vụ và trả lại (báo cáo lại) kết quả khi nhiệm vụ hoàn thành. © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 6 3.3 Các hàm trong thư viện toán học • Thực hiện các tính toán toán học thông thường – Include header file <cmath> (hoặc <math.h>) • Cách gọi hàm – tên_hàm (đối_số); hoặc – tên_hàm(đối_số_1, đối_số_2, …); • Ví dụ cout << sqrt( 900.0 ); – Mọi hàm trong thư viện toán đều trả về giá trị kiểu double • các đối số (argument) cho hàm có thể là – hằng - Constants • sqrt( 4 ); – biến - Variables • sqrt( x ); – biểu thức - Expressions • sqrt( sqrt( x ) ) ; • sqrt( 3 - 6x ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 7 Me tho d ceil( x ) De sc rip tio n làm tròn x tới số nguyên nhỏ nhất không nhỏ hơn x cos( x ) cos của x (lượng giác) (x tính theo đơn vị radian) hàm mũ: e mũ x exp( x ) Exa mp le ceil( 9.2 ) is 10.0 ceil( -9.8 ) is -9.0 cos( 0.0 ) is 1.0 exp( 1.0 ) is 2.71828 exp( 2.0 ) is 7.38906 fabs( x ) giá trị tuyệt đối của x fabs( 5.1 ) is 5.1 fabs( 0.0 ) is 0.0 fabs( -8.76 ) is 8.76 floor( x ) làm tròn x xuống số nguyên lớn floor( 9.2 ) is 9.0 nhất không lớn hơn x floor( -9.8 ) is -10.0 fmod( x, y ) phần dư của phép chia x/y , tính fmod( 13.657, 2.333 ) is 1.992 bằng kiểu số thực log( x ) loga tự nhiên của x (cơ số e) log( 2.718282 ) is 1.0 log( 7.389056 ) is 2.0 log10( x ) loga cơ số 10 của x log10( 10.0 ) is 1.0 log10( 100.0 ) is 2.0 pow( x, y ) x mũ y pow( 2, 7 ) is 128 pow( 9, .5 ) is 3 sin( x ) sin x (lượng giác) sin( 0.0 ) is 0 (x tính theo radian) sqrt( x ) căn bậc hai của x sqrt( 900.0 ) is 30.0 sqrt( 9.0 ) is 3.0 tan( x ) tang x (lượng giác) tan( 0.0 ) is 0 (x tính theo radian) Fig . 3.2 Ma th lib ra ry func tio ns. © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 8 3.4 Hàm - function • Chương trình con – Module hóa một chương trình – khả năng tái sử dụng phần mềm – Software reusability • gọi hàm nhiều lần • Các biến địa phương – Local variables – khai báo trong hàm nào thì chỉ được biết đến bên trong hàm đó – biến được khai báo bên trong định hàm là biến địa phưnơg • Các tham số – Parameters – là các biến địa phương với giá trị được truyền vào hàm khi hàm được gọi – cung cấp thông tin về bên ngoài hàm © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 9 // Creating and using a programmer-defined function. #include <iostream> Function prototype: chỉ rõ kiểu dữ liệu của đối số và giá trị trả về. square cần một số int, và trả về int. using std::cout; using std::endl; int square( int ); fig03_03.cpp (1 of 2) // function prototype int main() { // loop 10 times and calculate and output Cặp ngoặc () dùng khi gọi hàm. Khi chạy xong, hàm trả kết quả. // square of x each time for ( int x = 1; x <= 10; x++ ) cout << square( x ) << " "; // function call cout << endl; return 0; // indicates successful termination 1 } // end main 4 9 16 25 36 49 64 81 100 // square function definition returns square of an integer int square( int y ) // y is a copy of argument to function { return y * y; // returns square of Định y as nghĩa an int hàm square. } // end function square y là một bản sao của đối số được truyền vào. Hàm trả về y * ©2004 Trần Minh Châu. y, hoặc y bình phương. FOTECH. VNU. 10 1 2 3 // Fig. 3.4: fig03_04.cpp // Finding the maximum of three floating-point numbers. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 double maximum( double, double, double ); // function prototype 10 11 12 13 14 15 int main() { double number1; double number2; double number3; fig03_04.cpp (1 of 2) Hàm maximum lấy 3 tham số (cả 3 là double) và trả về một double. 16 17 18 cout << "Enter three floating-point numbers: "; cin >> number1 >> number2 >> number3; 19 20 21 22 23 // number1, number2 and number3 are arguments to // the maximum function call cout << "Maximum is: " << maximum( number1, number2, number3 ) << endl; 24 25 return 0; // indicates successful termination CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 11 } // end main dấu phảy phân tách các tham số. // function maximum definition; // x, y and z are parameters double maximum( double x, double y, double z ) { double max = x; // assume x is largest if ( y > max ) max = y; // if y is larger, // assign y to max if ( z > max ) max = z; // if z is larger, // assign z to max return max; // max is largest value fig03_04.cpp (2 of 2) fig03_04.cpp output (1 of 1) } // end function maximum Enter three floating-point numbers: 99.32 37.3 27.1928 Maximum is: 99.32 Enter three floating-point numbers: 1.1 3.333 2.22 Maximum is: 3.333 ©2004 Trần Minh Châu. Enter three floating-point numbers: 27.9 14.31 88.99 FOTECH. VNU. M i i 88 99 12 3.4 Hàm • Nguyên mẫu hàm - Function prototype – Cho trình biên dịch biết kiểu dữ liệu của đối số và kiểu giá trị trả về của hàm int square( int ); • Hàm lấy một giá trị int và trả về một giá trị int – Sẽ giới thiệu kỹ hơn sau • Gọi hàm square(x); – Cặp ngoặc đơn là toán tử dùng để gọi hàm • Truyền đối số x • Hàm nhận được bản sao các đối số cho riêng mình – Sau khi kết thúc, hàm trả kết quả về cho nơi gọi hàm © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 13 3.5 Định nghĩa hàm – function definition • định nghĩa hàm return-value-type function-name( parameter-list ) { declarations and statements } – danh sách tham số – Parameter list • dấu phảy tách các tham số – mỗi tham số cần cho biết kiểu dữ liệu của tham số đó • Nếu không có đối số, sử dụng void hoặc để trống – giá trị trả về – Return-value-type • kiểu của giá trị trả về (sử dụng void nếu không trả về giá trị gì) © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 14 3.5 Định nghĩa hàm • Từ khóa return Ví dụ về hàm int square( int y ) { return y * y; – trả dữ liệu về, và trả điều khiển lại cho nơi gọi (caller) • nếu không trả về, sử dụng return; – hàm kết thúc khi chạy đến ngoặc phải ( } ) } • điều khiển cũng được trả về cho nơi gọi int main() { ... • Không thể định nghĩa một hàm bên trong một hàm khác cout << square(x); ... } © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 15 3.6 Nguyên mẫu hàm - Function Prototype • Function prototype bao gồm – Tên hàm – Các tham số (số lượng và kiểu dữ liệu) – Kiểu trả về (void nếu không trả về giá trị gì) • Function prototype chỉ cần đến nếu định nghĩa hàm đặt sau lời gọi hàm (function call) • Prototype phải khớp với định nghĩa hàm – Function prototype double maximum( double, double, double ); – Function definition double maximum( double x, double y, double z ) { … } © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 16 3.6 Function Prototype • Chữ ký của hàm - Function signature – Phần prototype chứa tên và các tham số của hàm • double maximum( double, double, double ); Function signature • Ép kiểu đối số – Argument Coercion – Ép các đối số thành các kiểu dữ liệu thích hợp • đổi int (4) thành double (4.0) cout << sqrt(4) – các quy tắc biến đổi • các đối số thường được tự động đổi kiểu • đổi từ double sang int có thể làm tròn dữ liệu – 3.4 thành 3 – các kiểu hỗn hợp được nâng lên kiểu cao nhất • int * double © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 17 3.6 Function Prototype Da ta typ es long double double float unsigned long int (synonymous with unsigned long) (synonymous with long) long int unsigned int (synonymous with unsigned) int (synonymous with unsigned short) unsigned short int short int (synonymous with short) unsigned char char bool (false becomes 0, true becomes 1) Fig . 3.5 Pro m o tio n hiera rc hy fo r b uilt-in d a ta typ es. © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 18 3.7 Header File • Các file header chứa – các function prototype – định nghĩa của các kiểu dữ liệu và các hằng • Các file header kết thúc bằng .h – các file header do lập trình viên định nghĩa #include “myheader.h” • Các file header của thư viện #include <cmath> – chú ý: • <cmath> tương đương với <math.h> (kiểu cũ, trước ANSI C++) • <iostream> tương đương với <iostream.h> (kiểu cũ, trước ANSI C++) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 19 3.8 Sinh số ngẫu nhiên Random Number Generation • Hàm rand (thuộc <cstdlib>) – i = rand(); – Sinh một số nguyên không âm trong đoạn từ 0 đến RAND_MAX (thường là 32767) • Lấy tỷ lệ và dịch (scaling and shifting) – phép đồng dư (lấy số dư) – Modulus (remainder) operator: % • 10 % 3 bằng 1 • x % y nằm giữa 0 và y – 1 – Ví dụ i = rand() % 6 + 1; • “Rand() % 6” sinh một số trong khoảng từ 0 đến 5 (lấy tỷ lệ ) • “+ 1” tạo khoảng từ 1 đến 6 (dịch) – Tiếp theo: chương trình thả súc sắc © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Chương 3. 20 // Fig. 3.7: fig03_07.cpp // Shifted, scaled integers produced by 1 + rand() % 6. #include <iostream> using std::cout; using std::endl; 6 5 6 6 #include <iomanip> 6 1 6 2 fig03_07.cpp (1 of 2) 5 1 2 3 5 5 4 4 6 3 2 1 using std::setw; #include <cstdlib> // contains function prototype for rand int main() { // loop 20 times Kết for ( int counter = 1; counter <= 20; counter++ ) { // pick random number from 1 to 6 and output cout << setw( 10 ) << ( 1 + rand() % 6 ); quả của rand() được lấy tỷ lệ và dịch thành một số it trong khoảng từ 1 đến 6. // if counter divisible by 5, begin new line of output if ( counter % 5 == 0 ) cout << endl; } // end for structure return 0; // indicates successful termination } // end main CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 21 3.8 Sinh số ngẫu nhiên • Tiếp theo – Chương trình biểu diễn phân bố (distribution) của hàm rand() – Giả lập 6000 lần thả súc sắc – In số lượng các giá trị 1, 2, 3, v.v.... thả được – số lượng đếm được của mỗi giá trị phải xấp xỉ 1000 © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 3.8: fig03_08.cpp // Roll a six-sided die 6000 times. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 #include <cstdlib> 13 14 15 16 17 18 19 20 21 22 int main() { int frequency1 = 0; int frequency2 = 0; int frequency3 = 0; int frequency4 = 0; int frequency5 = 0; int frequency6 = 0; int face; // represents one roll of the die Chương 3. 22 fig03_08.cpp (1 of 3) // contains function prototype for rand 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 // determine face value and increment appropriate counter switch ( face ) { fig03_08.cpp (2 of 3) case 1: // rolled 1 ++frequency1; break; case 2: // rolled 2 ++frequency2; break; case 3: // rolled 3 ++frequency3; break; case 4: // rolled 4 ++frequency4; break; case 5: // rolled 5 ++frequency5; break; ©2004 Trần Minh Châu. FOTECH. VNU. 24 case 6: // rolled 6 ++frequency6; break; fig03_08.cpp (3 of 3) default: // invalid value cout << "Program should never get here!"; } // end switch Trường hợp mặc định được xét đến, ngay cả khi nó không bao giờ xảy ra. Đây là một nét của phong cách lập trình tốt 59 60 } // end for 61 62 63 64 65 66 67 68 69 // display results in tabular format cout << "Face" << setw( 13 ) << "Frequency" << "\n 1" << setw( 13 ) << frequency1 << "\n 2" << setw( 13 ) << frequency2 << "\n 3" << setw( 13 ) << frequency3 << "\n 4" << setw( 13 ) << frequency4 << "\n 5" << setw( 13 ) << frequency5 << "\n 6" << setw( 13 ) << frequency6 << endl; 70 71 return 0; 72 73 23 // loop 6000 times and summarize results for ( int roll = 1; roll <= 6000; roll++ ) { face = 1 + rand() % 6; // random number from 1 to 6 Face 1 2 3 4 5 6 Frequency 1003 1017 983 994 1004 999 // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 3.8 Sinh số ngẫu nhiên • Gọi rand() lặp đi lặp lại – cho kết quả là cùng một chuỗi số • Các số giả ngẫu nhiên (pseudorandom numbers) – chuỗi các số "ngẫu nhiên" được định sẵn – chương trình chạy lần nào cũng sinh cùng một chuỗi • Để được các chuỗi ngẫu nhiên khác nhau – Cung cấp một giá trị hạt giống • điểm xuất phát cho việc sinh chuỗi ngẫu nhiên • hạt giống giống nhau sẽ cho cùng một chuỗi ngẫu nhiên – srand(seed); • <cstdlib> • sử dụng trước rand() để đặt hạt giống © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 1 2 3 // Fig. 3.9: fig03_09.cpp // Randomizing die-rolling program. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 #include <iomanip> 10 11 using std::setw; 12 13 14 // contains prototypes for functions srand and rand #include <cstdlib> 15 16 17 18 19 // main function begins program execution int main() { unsigned seed; 20 21 22 23 26 fig03_09.cpp (1 of 2) Đặt hạt giống bằng cout << "Enter seed: "; srand(). cin >> seed; srand( seed ); // seed random number generator 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 29 30 31 32 33 // pick random number from 1 to 6 and output it cout << setw( 10 ) << ( 1 + rand() % 6 ); fig03_09.cpp (2 of 2) // if counter divisible by 5, begin new line of output if ( counter % 5 == 0 ) cout << endl; fig03_09.cpp output (1 of 1) 34 35 } // end for 36 37 return 0; 38 39 27 // loop 10 times for ( int counter = 1; counter <= 10; counter++ ) { // indicates successful termination rand() sinh cùng một chuỗi ngẫu nhiên nếu dùng cùng một hạt giống } // end main Enter seed: 67 6 1 1 6 4 1 6 6 2 4 Enter seed: 432 4 3 6 1 3 5 1 4 6 2 Enter seed: 67 6 1 1 6 4 1 6 6 2 4 ©2004 Trần Minh Châu. FOTECH. VNU. 28 3.8 Sinh số ngẫu nhiên • Có thể sử dụng thời gian hiện tại để làm hạt giống – không cần phải đặt hạt giống mỗi lần sinh 1 số ngẫu nhiên – srand( time( 0 ) ); – time( 0 ); • <ctime> • trả về thời gian hiện tại, tính bằng giây • Tổng quát về dịnh và lấy tỷ lệ – Number = shiftingValue + rand() % scalingFactor – shiftingValue = số đầu tiên của khoảng mong muốn – scalingFactor = độ rộng của khoảng mong muốn © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 29 3.9 Ví dụ: Trò chơi may rủi và Giới thiệu về kiểu enum • Kiểu liệt kê - Enumeration – tập hợp các số tự nhiên được đặt tên enum typeName {constant1, constant2…}; – Các hằng số là các số nguyên bắt đầu từ 0 (mặc định), tăng dần, mỗi lần thêm 1 đơn vị. – Các hằng phải có tên riêng – Không thể gán giá trị kiểu nguyên cho biến kiểu liệt kê • Phải dùng một giá trị thuộc cùng kiểu liệt kê đã được định nghĩa • Ví dụ enum Status {CONTINUE, WON, LOST}; enum Foo {Zero, One, Two}; Status enumVar; enumVar = WON; // cannot do enumVar = 1 or enumVar=One © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 30 3.9 Ví dụ: Trò chơi may rủi và Giới thiệu về kiểu enum • Các hằng kiểu liệt kê có thể có giá trị đặt trước enum Months { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC}; – bắt đầu tại 1, tăng dần mỗi lần thêm 1 • Tiếp theo: giả lập trò chơi gieo súc sắc – – – – Gieo 2 con súc sắc, được kết quả là tổng hai giá trị gieo được 7 hoặc 11 tại lần gieo đầu tiên: người chơi thắng 2, 3, hoặc 12 tại lần gieo đầu tiên: người chơi thua 4, 5, 6, 8, 9, 10 • giá trị gieo được trở thành "điểm" (point) của người chơi • người chơi phải gieo được số điểm của mình trước khi gieo được 7 để thắng cuộc © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 31 1 2 3 // Fig. 3.10: fig03_10.cpp // Craps. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 // contains function prototypes for functions srand and rand Hàm gieo 2 con súc sắc và trả về kết #include <cstdlib> 10 11 #include <ctime> 12 13 int rollDice( void ); 14 15 16 17 18 Kiểu liệt kê để ghi trạng thái int main() của ván chơi hiện tại. { // enumeration constants represent game status enum Status { CONTINUE, WON, LOST }; fig03_10.cpp (1 of 5) quả là 1 giá trị kiểu int. // contains prototype for function time 19 20 21 int sum; int myPoint; 22 23 Status gameStatus; // function prototype // can contain CONTINUE, WON or LOST 24 ©2004 Trần Minh Châu. FOTECH. VNU. 25 26 // randomize random number generator using current time srand( time( 0 ) ); 27 28 sum = rollDice(); 29 30 31 // determine game status and point based on sum of dice switch ( sum ) { 32 fig03_10.cpp (2 of 5) // first roll of the dice 32 33 34 35 36 37 // win on first roll case 7: case 11: gameStatus = WON; break; 38 39 40 41 42 43 44 // lose on first roll case 2: case 3: case 12: gameStatus = LOST; break; lệnh switch quyết định kết cục ván chơi, dựa vào kết quả gieo súc sắc. 45 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 46 47 48 49 50 51 33 // remember point default: gameStatus = CONTINUE; myPoint = sum; cout << "Point is " << myPoint << endl; break; // optional fig03_10.cpp (3 of 5) 52 53 } // end switch 54 55 56 57 // while game not complete ... while ( gameStatus == CONTINUE ) { sum = rollDice(); // roll dice again 58 59 60 61 62 63 64 65 66 // determine game status if ( sum == myPoint ) gameStatus = WON; else if ( sum == 7 ) gameStatus = LOST; // win by making point // lose by rolling 7 } // end while 67 ©2004 Trần Minh Châu. FOTECH. VNU. 68 69 70 71 72 // display won or lost message if ( gameStatus == WON ) cout << "Player wins" << endl; else cout << "Player loses" << endl; 73 74 return 0; fig03_10.cpp (4 of 5) // indicates successful termination Hàm rollDice không lấy đối số, nên nó có từ khóa void tại danh sách tham số. 75 76 } // end main 77 78 79 80 81 82 83 // roll dice, calculate sum and display results int rollDice( void ) { int die1; int die2; int workSum; 84 85 86 87 34 die1 = 1 + rand() % 6; die2 = 1 + rand() % 6; workSum = die1 + die2; // pick random die1 value // pick random die2 value // sum die1 and die2 88 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 89 90 91 // display results of this roll cout << "Player rolled " << die1 << " + " << die2 << " = " << workSum << endl; 92 93 return workSum; 94 95 35 fig03_10.cpp (5 of 5) // return sum of dice } // end function rollDice fig03_10.cpp output (1 of 2) Player rolled 2 + 5 = 7 Player wins Player rolled 6 + 6 = 12 Player loses Player rolled Point is 6 Player rolled Player rolled Player rolled Player rolled Player wins 3 + 3 = 6 5 4 2 1 + + + + 3 5 1 5 = = = = 8 9 3 6 Player rolled Point is 4 Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player rolled Player loses 1 + 3 = 4 4 2 6 2 2 1 4 4 + + + + + + + + 6 4 4 3 4 1 4 3 = = = = = = = = 10 6 10 5 6 2 8 7 ©2004 Trần Minh Châu. FOTECH. VNU. 36 3.10 Các kiểu lưu trữ – Storage Classes • biến có các thuộc tính – đã biết: tên, kiểu, kích thước, giá trị – kiểu lưu trữ – Storage class • biến tồn tại bao lâu trong bộ nhớ – Phạm vi – Scope • biến có thể được sử dụng tại những nơi nào trong chương trình – Liên kết – Linkage • Đối với những chương trình gồm nhiều file (multiple-file program) – (xem chương 6), những file nào có thể sử dụng biến đó © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 37 3.10 Các kiểu lưu trữ – Storage Classes • loại biến tự động – Automatic storage class – biến được tạo khi chương trình chạy vào một khối chương trình (block) – và bị hủy bỏ khi chương trình ra khỏi block – Chỉ có các biến địa phương của các hàm mới có thể là biến tự động • mặc định là tự động • từ khóa auto dùng để khai báo biến tự động – từ khóa register • gợi ý đặt biến vào thanh ghi tốc độ cao • có lợi cho các biến thường xuyên được sử dụng (con đếm vòng lặp) • Thường là không cần thiết, trình biên dịch tự tối ưu hóa – Chỉ dùng một trong hai từ register hoặc auto. • register int counter = 1; © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 38 3.10 Các kiểu lưu trữ • loại biến tĩnh – Static storage class – Biến tồn tại trong suốt chương trình – Có thể không phải nơi nào cũng dùng được, do áp dụng quy tắc phạm vi (scope rules) • từ khóa static – dành cho biến địa phương bên trong hàm – giữ giá trị giữa các lần gọi hàm – chỉ được biết đến trong hàm của biến đó • từ khóa extern – mặc định với các biến/hàm toàn cục (global variables/functions) • toàn cục: được định nghĩa bên ngoài các hàm – được biết đến tại mọi hàm nằm sau biến đó © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 39 3.11 Các quy tắc phạm vi – Scope Rules • Phạm vi – Scope – Phạm vi của một định danh (tên) là phần chương trình nơi có thể sử dụng định danh đó • Phạm vi file – File scope – được định nghĩa bên ngoài một hàm và được biết đến tại mọi hàm trong file – các biến toàn cục (global variable), định nghĩa và prototype của các hàm. • Phạm vi hàm – Function scope – chỉ có thể được dùng đến bên trong hàm chứa định nghĩa – Chỉ áp dụng cho các nhãn (label), ví dụ: các định danh đi kèm một dấu hai chấm (case:) © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 40 3.11 Các quy tắc phạm vi • Phạm vi khối – Block scope – Bắt đầu tại nơi khai báo, kết thúc tại ngoặc phải } • chỉ có thể được dùng trong khoảng này – Các biến địa phương, các tham số hàm – các biến static cũng có phạm vi khối • loại lưu trữ độc lập với phạm vi • Function-prototype scope – danh sách tham số của function prototype – không bắt buộc phải chỉ rõ các tên trong prototype • Trình biên dịch bỏ qua – Trong một prototype, mỗi tên chỉ được dùng một lần © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 41 1 2 3 // Fig. 3.12: fig03_12.cpp // A scoping example. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 được khai báo bên ngoài hàm; void useLocal( void ); // function prototype là biến toàn cục với phạm vi file. void useStaticLocal( void ); // function prototype void useGlobal( void ); // function prototype 11 12 int x = 1; 13 14 15 16 int main() { int x = 5; 17 18 19 20 21 22 23 24 25 26 fig03_12.cpp (1 of 5) // global variable Biến địa phương với phạm vi hàm. // local variable to main cout << "local x in main's outer scope is " << x << endl; { // start new scope Tạo một khối, cho x phạm vi khối. Khi khối kết thúc, x sẽ bị hủy bỏ. int x = 7; cout << "local x in main's inner scope is " << x << endl; ©2004 Trần Minh Châu. FOTECH. VNU. } // end new scope 42 27 28 cout << "local x in main's outer scope is " << x << endl; 29 30 31 32 33 34 35 useLocal(); useStaticLocal(); useGlobal(); useLocal(); useStaticLocal(); useGlobal(); 36 37 cout << "\nlocal x in main is " << x << endl; 38 39 return 0; 40 41 // // // // // // fig03_12.cpp (2 of 5) useLocal has local x useStaticLocal has static local x useGlobal uses global x useLocal reinitializes its local x static local x retains its prior value global x also retains its value // indicates successful termination } // end main 42 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 43 44 45 46 47 48 49 50 51 52 53 54 43 // useLocal reinitializes local variable x during each call void useLocal( void ) { int x = 25; // initialized each time useLocal is called fig03_12.cpp (3 of 5) Biến tự động (biến địa phương của cout << << ++x; cout << << endl << "local x is " << x hàm). Biến này sẽ bị hủy khi hàm kết " on entering useLocal" endl; thúc, << và được khởi tạo lại khi hàm bắt đầu. "local x is " << x " on exiting useLocal" << endl; } // end function useLocal 55 ©2004 Trần Minh Châu. FOTECH. VNU. 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 44 // useStaticLocal initializes static local variable x only the // first time the function is called; value of x is saved // between calls to this function void useStaticLocal( void ) { // initialized only first time useStaticLocal is called static int x = 50; cout << << ++x; cout << << fig03_12.cpp (4 of 5) endl << "local static x is " << x " on entering useStaticLocal" << endl; "local static x is " << x " on exiting useStaticLocal" << endl;Biến } // end function useStaticLocal tĩnh địa phương của hàm; nó được khởi tạo đúng một lần và giữ nguyên giá trị giữa các lần gọi hàm. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 72 73 74 75 76 77 78 79 80 81 45 // useGlobal modifies global variable x during each call void useGlobal( void ) Hàm này không khai báo biến nào. Nó sử dụng biến toàn { cục x đã được khai báo tại đầu chương trình. fig03_12.cpp cout << endl << "global x is " << x (5 of 5) << " on entering useGlobal" << endl; x *= 10; fig03_12.cpp cout << "global x is " << x output (1 of 2) << " on exiting useGlobal" << endl; } // end function useGlobal local x in main's outer scope is 5 local x in main's inner scope is 7 local x in main's outer scope is 5 local x is 25 on entering useLocal local x is 26 on exiting useLocal local static x is 50 on entering useStaticLocal local static x is 51 on exiting useStaticLocal global x is 1 on entering useGlobal global x is 10 on exiting useGlobal ©2004 Trần Minh Châu. FOTECH. VNU. 46 local x is 25 on entering useLocal local x is 26 on exiting useLocal fig03_12.cpp output (2 of 2) local static x is 51 on entering useStaticLocal local static x is 52 on exiting useStaticLocal global x is 10 on entering useGlobal global x is 100 on exiting useGlobal local x in main is 5 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 47 3.12 Đệ quy – Recursion • Các hàm đệ quy – Recursive functions – các hàm tự gọi chính mình – chỉ giải quyết một trường hợp cơ bản (base case) • Nếu không phải trường hợp cơ bản – Chia bài toán thành các bài toán nhỏ hơn – Gọi bản sao mới của hàm để giải quyết vấn đề nhỏ hơn (gọi đệ quy (recursive call) hoặc bước đệ quy(recursive step)) • hội tụ dần dần về trường hợp cơ bản • hàm gọi chính nó tại lệnh return – Cuối cùng, trường hợp cơ bản được giải quyết • câu trả lời đi ngược lên, giải quyết toàn bộ bài toán © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 48 3.12 Đệ quy • Ví dụ: tính giai thừa (factorial) n! = n * ( n – 1 ) * ( n – 2 ) * … * 1 – Quan hệ đệ quy ( n! = n * ( n – 1 )! ) 5! = 5 * 4! 4! = 4 * 3!… – Trường hợp cơ bản (1! = 0! = 1) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 49 1 2 3 // Fig. 3.14: fig03_14.cpp // Recursive factorial function. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 unsigned long factorial( unsigned long ); // function prototype 13 14 15 16 17 18 19 20 int main() { // Loop 10 times. During each iteration, calculate // factorial( i ) and display result. for ( int i = 0; i <= 10; i++ ) cout << setw( 2 ) << i << "! = " << factorial( i ) << endl; 21 22 23 24 return 0; fig03_14.cpp (1 of 2) Kiểu dữ liệu unsigned long có thể lưu số nguyên trong khoảng từ 0 đến 4 tỷ. // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. 25 26 27 28 29 30 31 32 33 34 35 36 37 0! 1! 2! 3! 4! 5! 6! 7! 8! 9! 10! 50 // recursive definition of function factorial unsigned long factorial( unsigned long number ) { Trường hợp cơ bản xảy ra khi // base case ta có 0! hoặc 1!. if ( number <= 1 ) Mọi trường hợp khác phải return 1; fig03_14.cpp (2 of 2) fig03_14.cpp output (1 of 1) được chia nhỏ (bước đệ qui). // recursive step else return number * factorial( number - 1 ); } // end function factorial = = = = = = = = = = = 1 1 2 6 24 120 720 5040 40320 362880 3628800 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 51 3.13 Ví dụ sử dụng đệ quy: chuỗi Fibonacci • Chuỗi Fibonacci: 0, 1, 1, 2, 3, 5, 8... – Mỗi số là tổng của hai số đứng liền trước – Ví dụ một công thức đệ quy: • fib(n) = fib(n-1) + fib(n-2) • Mã C++ cho hàm Fibonacci long fibonacci( long n ) { if ( n == 0 || n == 1 ) // base case return n; else return fibonacci( n - 1 ) + fibonacci( n – 2 ); } © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 52 3.13 Ví dụ sử dụng đệ quy: chuỗi Fibonacci f( 3 ) return f( 2 ) return f( 1 ) return 1 + + f( 0 ) f( 1 ) return 1 return 0 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 53 3.13 Ví dụ sử dụng đệ quy: chuỗi Fibonacci • Thứ tự thực hiện – return fibonacci( n - 1 ) + fibonacci( n - 2 ); • Không xác định hàm nào được thực hiện trước – C++ không qui định – Chỉ có các phép &&, || và ?: đảm bảo thứ tự thực hiện từ trái sang phải • Các lời gọi hàm đệ quy – Mỗi tầng đệ quy nhân đôi số lần gọi hàm • số thứ 30 cần 2^30 ~ 4 tỷ lời gọi hàm – Độ phức tạp lũy thừa (Exponential complexity) © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 3.15: fig03_15.cpp // Recursive fibonacci function. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; Chương 3. 54 8 9 fig03_15.cpp (1 of 2) Các số Fibonacci tăng rất nhanh và đều là số không âm. Do đó, ta dùng kiểu unsigned long. unsigned long fibonacci( unsigned long ); // function prototype 10 11 12 13 int main() { unsigned long result, number; 14 15 16 17 // obtain integer from user cout << "Enter an integer: "; cin >> number; 18 19 20 // calculate fibonacci value for number input by user result = fibonacci( number ); 21 22 23 // display result cout << "Fibonacci(" << number << ") = " << result << endl; 24 25 return 0; // indicates successful termination CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 55 } // end main fig03_15.cpp (2 of 2) // recursive definition of function fibonacci unsigned long fibonacci( unsigned long n ) { // base case if ( n == 0 || n == 1 ) return n; fig03_15.cpp output (1 of 2) // recursive step else return fibonacci( n - 1 ) + fibonacci( n - 2 ); } // end function fibonacci Enter an integer: 0 Fibonacci(0) = 0 Enter an integer: 1 Fibonacci(1) = 1 Enter an integer: 2 Fibonacci(2) = 1 Enter an integer: 3 Fibonacci(3) = 2 ©2004 Trần Minh Châu. FOTECH. VNU. 56 Enter an integer: 4 Fibonacci(4) = 3 Enter an integer: 5 Fibonacci(5) = 5 fig03_15.cpp output (2 of 2) Enter an integer: 6 Fibonacci(6) = 8 Enter an integer: 10 Fibonacci(10) = 55 Enter an integer: 20 Fibonacci(20) = 6765 Enter an integer: 30 Fibonacci(30) = 832040 Enter an integer: 35 Fibonacci(35) = 9227465 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 57 3.14 So sánh Đệ quy và Vòng lặp • Lặp – Vòng lặp (Iteration): lặp tường minh – Đệ quy: các lời gọi hàm được lặp đi lặp lại • Kết thúc – Vòng lặp: điều kiện lặp thất bại – Đệ quy: gặp trường hợp cơ bản • Cả hai đều có thể lặp vô tận • Cân đối giữa hiệu quả chương trình (vòng lặp) và công nghệ phần mềm tốt (đệ quy) – vòng lặp chạy nhanh hơn – đệ quy trong sáng hơn © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 58 3.15 Hàm với danh sách tham số rỗng • Danh sách tham số rỗng – dùng từ khóa void hoặc để danh sách rỗng – dành cho hàm không lấy đối số – thí dụ: hàm print không lấy đối số và không trả về giá trị nào • void print(); • void print( void ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 59 1 2 3 // Fig. 3.18: fig03_18.cpp // Functions that take no arguments. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 void function1(); void function2( void ); 10 11 12 13 14 int main() { function1(); function2(); // call function1 with no arguments // call function2 with no arguments 15 16 return 0; // indicates successful termination 17 18 fig03_18.cpp (1 of 2) // function prototype // function prototype } // end main 19 ©2004 Trần Minh Châu. FOTECH. VNU. 20 21 22 23 24 // function1 uses an empty parameter list to specify that // the function receives no arguments void function1() { cout << "function1 takes no arguments" << endl; 25 26 } // end function1 27 28 29 30 31 32 // function2 uses a void parameter list to specify that // the function receives no arguments void function2( void ) { cout << "function2 also takes no arguments" << endl; 33 34 } // end function2 60 fig03_18.cpp (2 of 2) fig03_18.cpp output (1 of 1) function1 takes no arguments function2 also takes no arguments ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 61 3.16 Hàm Inline • Hàm inline – Từ khóa inline đặt trước hàm – Yêu cầu trình biên dịch sao chép mã vào chương trình thay cho việc tạo lời gọi hàm • Giảm chi phí gọi hàm (function-call overhead) • Trình biên dịch có thể bỏ qua inline – Tốt đối với các hàm nhỏ, hay dùng • Ví dụ inline double cube( const double s ) { return s * s * s; } – const cho trình biên dịch biết rằng hàm không sửa đổi s • được nói đến trong các chương 6-7 © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 4 // Fig. 3.19: fig03_19.cpp // Using an inline function to calculate. // the volume of a cube. #include <iostream> 5 6 7 8 using std::cout; using std::cin; using std::endl; 9 10 11 12 13 14 15 16 17 18 Chương 3. 62 fig03_19.cpp (1 of 2) // Definition of inline function cube. Definition of function // appears before function is called, so a function prototype // is not required. First line of function definition acts as // the prototype. inline double cube( const double side ) { return side * side * side; // calculate cube } // end function cube 19 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 20 21 22 63 int main() { cout << "Enter the side length of your cube: "; fig03_19.cpp (2 of 2) 23 24 double sideValue; 25 26 cin >> sideValue; 27 28 29 30 // calculate cube of sideValue and display result cout << "Volume of cube with side " << sideValue << " is " << cube( sideValue ) << endl; 31 32 return 0; 33 34 fig03_19.cpp output (1 of 1) // indicates successful termination } // end main Enter the side length of your cube: 3.5 Volume of cube with side 3.5 is 42.875 ©2004 Trần Minh Châu. FOTECH. VNU. 64 3.17 Tham chiếu và Tham số là tham chiếu • Các tham chiếu là các biệt danh (alias) của các biến khác – chỉ tới cùng một biến – có thể được dùng bên trong một hàm int count = 1; // khai báo biến nguyên count int &cRef = count; // tạo cRef là một biệt danh của count ++cRef; // tăng count (sử dụng biệt danh của count) • Các tham chiếu phải được khởi tạo khi khai báo – Nếu không, trình biên dịch báo lỗi – Tham chiếu lạc (Dangling reference) • tham chiếu tới biến không xác định © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 1 2 3 // Fig. 3.21: fig03_21.cpp // References must be initialized. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 65 fig03_21.cpp (1 of 1) fig03_21.cpp output (1 of 1) int main() { int x = 3; y được khai báo là một tham chiếu tới x. 11 12 13 // y refers to (is an alias for) x int &y = x; 14 15 16 17 cout << "x = " << x << endl << "y = " << y << endl; y = 7; cout << "x = " << x << endl << "y = " << y << endl; 18 19 return 0; 20 21 // indicates successful termination } // end main x y x y = = = = 3 3 7 7 ©2004 Trần Minh Châu. FOTECH. VNU. 1 2 3 // Fig. 3.22: fig03_22.cpp // References must be initialized. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 int main() { int x = 3; int &y; 66 fig03_22.cpp (1 of 1) fig03_22.cpp output Lỗi biên dịch – tham chiếu không được khởi tạo. (1 of 1) // Error: y must be initialized 12 13 14 15 cout << "x = " << x << endl << "y = " << y << endl; y = 7; cout << "x = " << x << endl << "y = " << y << endl; 16 17 return 0; 18 19 // indicates successful termination } // end main Borland C++ command-line compiler error message: Error E2304 Fig03_22.cpp 11: Reference variable 'y' must be initialized- in function main() Microsoft Visual C++ compiler error message: D:\cpphtp4_examples\ch03\Fig03_22.cpp(11) : error C2530: 'y' : references must be initialized CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 67 3.17 Tham chiếu và Tham số là tham chiếu • Gọi bằng giá trị - Call by value – Bản sao của dữ liệu được truyền cho hàm – Thay đổi đối với bản sao không ảnh hưởng tới dữ liệu gốc – Ngăn chặn các hiệu ứng phụ không mong muốn • Gọi bằng tham chiếu - Call by reference – Hàm có thể truy nhập trực tiếp tới dữ liệu gốc – Các thay đổi thể hiện tại dữ liệu gốc © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 68 3.17 Tham chiếu và Tham số là tham chiếu • Tham số tham chiếu - Reference parameter – Ý nghĩa: Là biệt danh (alias) của biến được truyền vào lời gọi hàm • 'truyền tham số bằng tham chiếu' hay 'truyền tham chiếu' – Cú pháp: Đặt ký hiệu & sau kiểu dữ liệu tại prototype của hàm • void myFunction( int &data ) • có nghĩa “data là một tham chiếu tới một biến kiểu int” – dạng của lời gọi hàm không thay đổi • tuy nhiên dữ liệu gốc khi được truyền bằng tham chiếu có thể bị sửa đổi • Con trỏ (chương 5) – Một cách truyền tham chiếu khác © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 3.20: fig03_20.cpp // Comparing pass-by-value and pass-by-reference // with references. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int squareByValue( int ); void squareByReference( int & ); 69 fig03_20.cpp (1 of 2) Lưu ý ký hiệu & có nghĩa truyền tham chiếu (pass-byreference). // function prototype // function prototype int main() { int x = 2; int z = 4; // demonstrate squareByValue cout << "x = " << x << " before squareByValue\n"; cout << "Value returned by squareByValue: " << squareByValue( x ) << endl; cout << "x = " << x << " after squareByValue\n" << endl; 22 ©2004 Trần Minh Châu. FOTECH. VNU. 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 70 // demonstrate squareByReference cout << "z = " << z << " before squareByReference" << endl; squareByReference( z ); cout << "z = " << z << " after squareByReference" << endl; fig03_20.cpp (2 of 2) return 0; // indicates successful termination thay đổi number, nhưng đối số gốc } // end main (x) không bị thay đổi. // squareByValue multiplies number by itself, stores the // result in number and returns the new value of number int squareByValue( int number ) { return number *= number; // caller's argument not modified } // end function squareByValue thay đổi numberRef, một biệt danh của đối số gốc. Do đó, z bị thay đổi. // squareByReference multiplies numberRef by itself and // stores the result in the variable to which numberRef // refers in function main void squareByReference( int &numberRef ) { numberRef *= numberRef; // caller's argument modified } // end function squareByReference ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 71 x = 2 before squareByValue Value returned by squareByValue: 4 x = 2 after squareByValue fig03_20.cpp output (1 of 1) z = 4 before squareByReference z = 16 after squareByReference ©2004 Trần Minh Châu. FOTECH. VNU. 72 3.18 Các đối số mặc định • Lời gọi hàm với các tham số được bỏ qua – Nếu không đủ số tham số, các vị trí ở bên phải nhất sẽ được nhận giá trị mặc định của chúng – Các giá trị mặc định • Có thể là hằng, biến toàn cục, hoặc các lời gọi hàm • Đặt các giá trị mặc định tại function prototype int myFunction( int x = 1, int y = 2, int z = 3 ); – myFunction(3) • x = 3, y và z nhận giá trị mặc định (bên phải nhất) – myFunction(3, 5) • x = 3, y = 5 còn z nhận giá trị mặc định © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 73 1 2 3 // Fig. 3.23: fig03_23.cpp // Using default arguments. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 // function prototype that specifies default arguments int boxVolume( int length = 1, int width = 1, int height = 1 ); 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fig03_23.cpp (1 of 2) Các giá trị mặc định được đặt trong function prototype. Các lời gọi hàm thiếu một int main() số đối số – Các đối số bên { phải nhất nhận giá trị mặc // no arguments--use default values for all dimensions định. cout << "The default box volume is: " << boxVolume(); // specify length; default width and height cout << "\n\nThe volume of a box with length 10,\n" << "width 1 and height 1 is: " << boxVolume( 10 ); // specify length and width; default height cout << "\n\nThe volume of a box with length 10,\n" << "width 5 and height 1 is: " << boxVolume( 10, 5 ); ©2004 Trần Minh Châu. FOTECH. VNU. 24 25 26 27 // specify all arguments cout << "\n\nThe volume of a box with length 10,\n" << "width 5 and height 2 is: " << boxVolume( 10, 5, 2 ) << endl; 28 29 return 0; 30 31 32 33 34 35 36 37 38 74 fig03_23.cpp (2 of 2) // indicates successful termination fig03_23.cpp output (1 of 1) } // end main // function boxVolume calculates the volume of a box int boxVolume( int length, int width, int height ) { return length * width * height; } // end function boxVolume The default box volume is: 1 The volume of a box with length 10, width 1 and height 1 is: 10 The volume of a box with length 10, width 5 and height 1 is: 50 The volume of a box with length 10, width 5 and height 2 is: 100 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 75 3.19 Toán tử phạm vi đơn • Toán tử phạm vi đơn (::) Unitary Scope Resolution Operator – Dùng để truy nhập biến toàn cục nếu biến địa phương có cùng tên – Không cần thiết nếu các tên biến khác nhau – Cách dùng ::tên_biến y = ::x + 3; – Nên tránh dùng các tên giống nhau cho các biến địa phương và toàn cục © 2004 Trần Minh Châu. FOTECH. VNU 1 2 3 // Fig. 3.24: fig03_24.cpp // Using the unary scope resolution operator. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setprecision; 11 12 13 // define global constant PI const double PI = 3.14159265358979; 14 15 16 17 18 int main() { // define local constant PI const float PI = static_cast< float >( ::PI ); Chương 3. 76 fig03_24.cpp (1 of 2) Truy nhập PI toàn cục với ::PI. Chuyển đổi PI toàn cục thành một giá trị float cho PI địa phương. Ví dụ này cho thấy sự khác nhau giữa float và double. 19 20 21 22 23 // display values of local and global PI constants cout << setprecision( 20 ) << " Local float value of PI = " << PI << "\nGlobal double value of PI = " << ::PI << endl; 24 25 return 0; // indicates successful termination CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 26 27 77 } // end main fig03_24.cpp (2 of 2) Borland C++ command-line compiler output: Local float value of PI = 3.141592741012573242 Global double value of PI = 3.141592653589790007 fig03_24.cpp output (1 of 1) Microsoft Visual C++ compiler output: Local float value of PI = 3.1415927410125732 Global double value of PI = 3.14159265358979 ©2004 Trần Minh Châu. FOTECH. VNU. 78 3.20 Chồng hàm • Chồng hàm - Function overloading – Các hàm có cùng tên nhưng khác nhau về tham số – Nên thực hiện các nhiệm vụ tương tự • ví dụ, hàm tính bình phương cho int và hàm tính bình phương cho float int square( int x) {return x * x;} float square(float x) { return x * x; } • Các hàm chồng phân biệt nhau bởi chữ ký – Dựa vào tên và kiểu tham số (xét cả thứ tự) – Trình biên dịch đảm bảo gọi đúng hàm chồng được yêu cầu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 1 2 3 // Fig. 3.25: fig03_25.cpp // Using overloaded functions. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 79 fig03_25.cpp (1 of 2) Các hàm chồng có cùng tên nhưng có tham số khác nhau // function square for int values int square( int x ) { cout << "Called square with int argument: " << x << endl; return x * x; } // end int version of function square // function square for double values double square( double y ) { cout << "Called square with double argument: " << y << endl; return y * y; } // end double version of function square 23 ©2004 Trần Minh Châu. FOTECH. VNU. 24 25 26 27 int main() { int intResult = square( 7 ); // calls int version double doubleResult = square( 7.5 ); // calls double version 28 29 30 31 cout << "\nThe square of integer 7 is " << intResult << "\nThe square of double 7.5 is " << doubleResult << endl; 32 33 return 0; 34 35 80 fig03_25.cpp (2 of 2) fig03_25.cpp output (1 of 1) // indicates successful termination } // end main Tùy theo đối số được truyền vào (int hoặc double) để gọi hàm thích hợp. Called square with int argument: 7 Called square with double argument: 7.5 The square of integer 7 is 49 The square of double 7.5 is 56.25 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 81 3.21 Khuôn mẫu hàm - Function Template • Cách ngắn gọn để tạo các hàm chồng – Sinh các hàm riêng biệt cho các kiểu dữ liệu khác nhau • Cú pháp – Bắt đầu bằng từ khóa template – các tham số kiểu hình thức trong cặp ngoặc <> • typename hoặc class (đồng nghĩa) đặt trước mỗi tham số kiểu • là đại diện cho các kiểu cài sẵn (ví dụ int) hoặc các kiểu dữ liệu người dùng • chỉ ra các kiểu dữ liệu cho đối số hàm, giá trị trả về, biến địa phương – Hàm được định nghĩa như bình thường, ngoại trừ việc sử dụng các kiểu hình thức © 2004 Trần Minh Châu. FOTECH. VNU Chương 3. 82 3.21 Khuôn mẫu hàm • Ví dụ template < class T > // or template< typename T > T square( T value1 ) { return value1 * value1; } – T là một kiểu hình thức, được dùng làm tham số kiểu • hàm trên trả về giá trị thuộc cùng kiểu với tham số – Tại lời gọi hàm, T được thay bằng kiểu dữ liệu thực • Nếu là int, mọi T trở thành int int x; int y = square(x); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 3. https://fb.com/tailieudientucntt 1 2 3 // Fig. 3.27: fig03_27.cpp // Using a function template. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 83 fig03_27.cpp (1 of 3) Tham số kiểu hình thức T là đại diện của kiểu dữ liệu được kiểm tra trong hàm maximum. // definition of function template maximum template < class T > // or template < typename T > T maximum( T value1, T value2, T value3 ) { T max = value1; if ( value2 > max ) max = value2; maximum mong đợi mọi tham số đều thuộc cùng một kiểu dữ liệu. if ( value3 > max ) max = value3; return max; } // end function template maximum 24 ©2004 Trần Minh Châu. FOTECH. VNU. 25 26 27 28 84 int main() { // demonstrate maximum with int values int int1, int2, int3; 29 30 31 cout << "Input three integer values: "; cin >> int1 >> int2 >> int3; 32 33 34 35 // invoke int version of maximum cout << "The maximum integer value is: " << maximum( int1, int2, int3 ); 36 37 38 // demonstrate maximum with double values double double1, double2, double3; 39 40 41 cout << "\n\nInput three double values: "; cin >> double1 >> double2 >> double3; 42 43 44 45 // invoke double version of maximum cout << "The maximum double value is: " << maximum( double1, double2, double3 ); fig03_27.cpp (2 of 3) maximum được gọi với nhiều kiểu dữ liệu. 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 85 47 48 // demonstrate maximum with char values char char1, char2, char3; 49 50 51 cout << "\n\nInput three characters: "; cin >> char1 >> char2 >> char3; fig03_27.cpp (3 of 3) 52 53 54 55 56 // invoke char version of maximum cout << "The maximum character value is: " << maximum( char1, char2, char3 ) << endl; fig03_27.cpp output (1 of 1) 57 58 return 0; 59 60 // indicates successful termination } // end main Input three integer values: 1 2 3 The maximum integer value is: 3 Input three double values: 3.3 2.2 1.1 The maximum double value is: 3.3 Input three characters: A C B The maximum character value is: C ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 4 – Mảng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 2 Chương 4 – Mảng Đề mục 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 Giới thiệu Mảng Khai báo mảng Ví dụ về sử dụng mảng Truyền tham số cho hàm Sắp xếp mảng Ví dụ: Dùng mảng tính Mean, Median và Mode Tìm kiếm trên mảng: Tìm kiếm Tuyến tính và tìm kiếm Nhị phân Mảng nhiều chiều © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 3 4.1 Giới thiệu • Mảng (array) – Cấu trúc của những phần tử dữ liệu có liên quan – Thực thể tĩnh (giữ nguyên kích thước trong suốt chương trình) • Một vài loại mảng – mảng dựa vào con trỏ (Pointer-based arrays) (C-like) – mảng là đối tượng (Arrays as objects) (C++) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 4 4.2 Mảng • Mảng – Tập hợp các vùng nhớ liên tiếp – Cùng tên, cùng kiểu (int, char, ...) • Truy nhập đến 1 phần tử – Chỉ ra tên mảng và vị trí - position (chỉ số - index) – Cú pháp: tên_mảng[ chỉ_số ] – Phần tử đầu tiên ở vị trí 0 • Mảng c có n phần tử c[ 0 ], c[ 1 ] … c[ n - 1 ] – Phần tử thứ N ở vị trí thứ N-1 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 5 4.2 Mảng • Phần tử của mảng cũng như các biến khác – Gán giá trị và in mảng số nguyên c c[ 0 ] = 3; cout << c[ 0 ]; • Có thể sử dụng các phép toán trong cặp ngoặc vuông c[ 5 – 2 ] cũng giống c[3] © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 6 Tên mảng (Lưu ý rằng mọi phần tử của mảng này đều có cùng tên, c) c[0] -45 c[1] 6 c[2] 0 c[3] 72 c[4] 1543 c[5] -89 c[6] 0 c[7] 62 c[8] -3 c[9] 1 c[10] 6453 c[11] 78 Chỉ số của phần tử trong mảng c © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 7 4.3 Khai báo mảng • Khi khai báo mảng, chỉ rõ – Tên – Kiểu của mảng • Bất cứ kiểu dữ liệu nào – Số phần tử – type arrayName[ arraySize ]; int c[ 10 ]; // mảng của 10 số nguyên float d[ 3284 ]; // mảng của 3284 số thực • Khai báo nhiều mảng cùng kiểu – Sử dụng dấu phẩy như với các biến bình thường int b[ 100 ], x[ 27 ]; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 8 4.4 Ví dụ về sử dụng mảng • Khởi tạo mảng – Dùng vòng lặp khởi tạo từng phần tử – Khởi tạo cả danh sách • Chỉ rõ từng phần tử khi khai báo mảng int n[ 5 ] = { 1, 2, 3, 4, 5 }; • Nếu trong danh sách không có đủ số giá trị khởi tạo, các phần tử ở bên phải nhất sẽ nhận giá trị 0 • Nếu danh sách thừa sẽ gây lỗi cú pháp – Khởi tạo giá trị bằng 0 cho tất cả các phần tử int n[ 5 ] = { 0 }; – Nếu không khai báo kích thước mảng, kích thước của danh sách các giá trị khởi tạo sẽ quyết định kích thước mảng int n[] = { 1, 2, 3, 4, 5 }; • Có 5 giá trị khởi tạo, do đó mảng có 5 phần tử • Nếu không khai báo kích thước mảng thì phải khởi tạo khi khai báo © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 fig04_03.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 14 9 // Fig. 4.3: fig04_03.cpp // Initializing an array. #include <iostream> Khai báo mảng 10 phần tử số nguyên. int main() { int n[ 10 ]; // n is an array of 10 integers Khởi tạo mảng bằng vòng lặp for. Chú ý rằng mảng gồm các phẩn tử từ n[0] đến n[9]. 15 16 17 18 // initialize elements of array n to 0 for ( int i = 0; i < 10; i++ ) n[ i ] = 0; // set element at location i to 0 19 20 cout << "Element" << setw( 13 ) << "Value" << endl; 21 22 23 24 // output contents of array n in tabular format for ( int j = 0; j < 10; j++ ) cout << setw( 7 ) << j << setw( 13 ) << n[ j ] << endl; 25 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 return 0; 10 // indicates successful termination } // end main fig04_03.cpp (2 of 2) Element 0 1 2 3 4 5 6 7 8 9 Value 0 0 0 0 0 0 0 0 0 0 fig04_03.cpp output (1 of 1) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 fig04_04.cpp (1 of 1) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 14 15 16 17 11 // Fig. 4.4: fig04_04.cpp // Initializing an array with a declaration. #include <iostream> Lưu ý cách dùng danh sách khởi tạo cho mảng. int main() { // use initializer list to initialize array n int n[ 10 ] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 }; cout << "Element" << setw( 13 ) << "Value" << endl; 18 19 20 21 // output contents of array n in tabular format for ( int i = 0; i < 10; i++ ) cout << setw( 7 ) << i << setw( 13 ) << n[ i ] << endl; 22 23 return 0; 24 25 // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Element 0 1 2 3 4 5 6 7 8 9 12 Value 32 27 64 18 95 14 90 70 60 37 fig04_04.cpp output (1 of 1) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 13 4.4 Ví dụ về sử dụng mảng • Kích thước của mảng – Có thể được xác định bằng hằng số (const) • const int size = 20; – Hằng số không thể thay đổi – Hằng phải được khởi tạo khi khai báo – Còn được gọi là “named constant” (giá trị được đặt tên) hoặc “read-only variable” (biến chỉ đọc) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 fig04_05.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 14 15 16 17 18 19 20 21 22 23 14 // Fig. 4.5: fig04_05.cpp // Initialize array s to the even integers from 2 to 20. #include <iostream> int main() { // constant variable can be used const int arraySize = 10; int s[ arraySize ]; Chú ý từ khoá const. Chỉ có các biến const được dùng để khai báo kích thướcsize mảng. to specify array // array s has 10 elements Chương trình dễ thay đổi hơn khi ta dùng hằng (const) cho kích thước của for ( int i = 0; i < arraySize; i++ ) // set the values mảng. s[ i ] = 2 + 2 * i; Ta có thể thay đổi arraySize, và tất cả các vòng lặp vẫn hoạt động bình cout << "Element" << setw( 13 ) << "Value" << endl; thường (nếu không, ta phải sửa mọi vòng lặp trong chương trình). ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 29 30 15 // output contents of array s in tabular format for ( int j = 0; j < arraySize; j++ ) cout << setw( 7 ) << j << setw( 13 ) << s[ j ] << endl; return 0; // indicates successful termination fig04_05.cpp output (1 of 1) } // end main Element 0 1 2 3 4 5 6 7 8 9 fig04_05.cpp (2 of 2) Value 2 4 6 8 10 12 14 16 18 20 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 fig04_06.cpp (1 of 1) using std::cout; using std::endl; Khởi tạo hằng int main() { const int x = 7; fig04_06.cpp output (1 of 1) // initialized constant variable 11 12 13 cout << "The value of constant variable x is: " << x << endl; 14 15 return 0; 16 17 16 // Fig. 4.6: fig04_06.cpp // Using a properly initialized constant variable. #include <iostream> // indicates successful termination } // end main The value of constant variable x is: 7 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 // Fig. 4.7: fig04_07.cpp // A const object must be initialized. 3 4 5 6 int main() { const int x; Lỗi cú pháp do không khởi tạo hằng. Sửa giá trị của hằng cũng là một lỗi. // Error: x must be initialized 7 8 x = 7; // Error: cannot modify a const variable 9 10 return 0; // indicates successful termination 11 12 17 fig04_07.cpp (1 of 1) fig04_07.cpp output (1 of 1) } // end main d:\cpphtp4_examples\ch04\Fig04_07.cpp(6) : error C2734: 'x' : const object must be initialized if not extern d:\cpphtp4_examples\ch04\Fig04_07.cpp(8) : error C2166: l-value specifies const object ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 18 // Fig. 4.8: fig04_08.cpp // Compute the sum of the elements of the array. #include <iostream> fig04_08.cpp (1 of 1) using std::cout; using std::endl; fig04_08.cpp output (1 of 1) int main() { const int arraySize = 10; 11 12 int a[ arraySize ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 13 14 int total = 0; 15 16 17 18 19 20 21 22 23 24 // sum contents of array a for ( int i = 0; i < arraySize; i++ ) total += a[ i ]; cout << "Total of array element values is " << total << endl; return 0; } // end main // indicates successful termination Total of array element values is 55 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // Fig. 4.9: fig04_09.cpp // Histogram printing program. Element #include <iostream> 0 1 using std::cout; 2 using std::endl; 3 4 #include <iomanip> 5 6 using std::setw; 7 8 int main() 9 { fig04_09.cpp (1 of 2) Value 19 3 15 7 11 9 13 5 17 1 Histogram ******************* *** *************** ******* *********** ********* ************* ***** ***************** * const int arraySize = 10; int n[ arraySize ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 }; cout << "Element" << setw( 13 ) << "Value" << setw( 17 ) << "Histogram" << endl; 19 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 20 21 22 23 // for each element of array n, output a bar in histogram for ( int i = 0; i < arraySize; i++ ) { cout << setw( 7 ) << i << setw( 13 ) In số dấu saofig04_09.cpp (*) tương ứng << n[ i ] << setw( 9 ); với giá trị của (2 phần of 2) tử n[i]. 24 25 26 for ( int j = 0; j < n[ i ]; j++ ) cout << '*'; 27 28 cout << endl; } // end outer for structure 31 32 return 0; } // end main // print one bar fig04_09.cpp output (1 of 1) // start next line of output 29 30 33 34 20 // indicates successful termination Element 0 1 2 3 4 5 6 7 8 9 Value 19 3 15 7 11 9 13 5 17 1 Histogram ******************* *** *************** ******* *********** ********* ************* ***** ***************** * ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 21 // Fig. 4.10: fig04_10.cpp // Roll a six-sided die 6000 times. #include <iostream> fig04_10.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 #include <cstdlib> #include <ctime> 14 15 16 17 18 int main() { const int arraySize = 7; int frequency[ arraySize ] = { 0 }; Viết lại một chương trình cũ. Một mảng được sử dụng thay cho 6 biến thường, và các phần tử dễ dàng cập nhật hơn (không cần sử dụng switch). Dòng lệnh này tạo ra một số trong khoảng 1 đến 6 và tăng phần tử // seed random-number generator frequency[] có chỉ số đó. 19 20 srand( time( 0 ) ); 21 22 23 24 25 // roll die 6000 times for ( int roll = 1; roll <= 6000; roll++ ) ++frequency[ 1 + rand() % 6 ]; // replaces 20-line switch // of Fig. 3.8 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 26 27 cout << "Face" << setw( 13 ) << "Frequency" << endl; 28 29 30 31 32 // output frequency elements 1-6 in tabular format for ( int face = 1; face < arraySize; face++ ) cout << setw( 4 ) << face << setw( 13 ) << frequency[ face ] << endl; 33 34 return 0; 35 36 fig04_10.cpp (2 of 2) fig04_10.cpp output (1 of 1) // indicates successful termination } // end main Face 1 2 3 4 5 6 Frequency 1003 1004 999 980 1013 1001 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 23 // Fig. 4.11: fig04_11.cpp ***modified*** // Student mark statistic program. #include <iostream> fig04_11.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 14 15 16 int main() { // define array sizes const int markSize = 40; // size of array of marks const int frequencySize = 11; // size of array frequency 17 18 19 20 21 // place student marks in array of marks int marks[ markSize ] = { 1, 2, 6, 4, 8, 5, 9, 7, 8, 10, 1, 6, 3, 8, 6, 10, 3, 8, 2, 7, 6, 5, 7, 6, 8, 6, 7, 5, 6, 6, 5, 6, 7, 5, 6, 4, 8, 6, 8, 10 }; 22 23 24 // initialize frequency counters to 0 int frequency[ frequencySize ] = { 0 }; 25 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 // for each student's mark, select value of an element of array // responses and use that value as subscript in array // frequency to determine element to increment fig04_11.cpp for ( int student = 0; student < markSize; student++ ) (2 of 2) ++frequency[ marks[student] ]; 31 32 33 // display results cout << "Rating" << setw( 17 ) << "Frequency" << endl; 34 35 36 37 38 // output frequencies in tabular format for ( int rating = 1; rating < frequencySize; rating++ ) cout << setw( 6 ) << rating << setw( 17 ) << frequency[ rating ] << endl; 39 40 return 0; 41 42 } // end main // indicates successful termination marks[student] là điểm (từ 1 đến 10). Giá trị này quyết định chỉ số của phần tử frequency[] cần tăng. CuuDuongThanCong.com Rating 1 2 3 4 5 6 7 8 9 10 Frequency 2 2 2 2 5 11 5 7 1 ©2004 Trần Minh Châu. 3 FOTECH. VNU. https://fb.com/tailieudientucntt 24 25 4.4 Ví dụ về sử dụng mảng • Xâu - string (xem thêm ở chương 5) – Mảng của các ký tự – Mọi xâu đều kết thúc với ký tự null ('\0') – Ví dụ • char string1[] = "hello"; – Ký tự null tự động được thêm vào, xâu có 6 phần tử • char string1[] = { 'h', 'e', 'l', 'l', 'o', '\0’ }; – Chỉ số cũng giống như đối với mảng String1[ 0 ] bằng 'h' string1[ 2 ] bằng 'l' © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 26 4.4 Ví dụ về sử dụng mảng • Nhập từ bàn phím bằng cin char string2[ 10 ]; cin >> string2; – Ghi dữ liệu vào của người dùng vào xâu • Dừng lại ở ký tự trắng đầu tiên (tab, newline, blank…) • Thêm vào ký tự null – Nếu nhập quá nhiều, dữ liệu sẽ tràn mảng • Ta cần phải tránh điều này (mục 5.12 sẽ giải thích phương pháp) • In xâu – cout << string2 << endl; • Không sử dụng được với các mảng có kiểu dữ liệu khác – In các ký tự cho đến khi gặp null © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 27 // Fig. 4_12: fig04_12.cpp // Treating character arrays as strings. #include <iostream> fig04_12.cpp (1 of 2) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 int main() { char string1[ 20 ], // reserves 20 characters char string2[] = "string literal"; // reserves 15 characters 13 14 15 16 // read string from user into array string2 cout << "Enter the string \"hello there\": "; cin >> string1; // reads "hello" [space terminates input] 17 18 19 20 // output strings cout << "string1 is: " << string1 << "\nstring2 is: " << string2; 21 22 cout << "\nstring1 with spaces between characters is:\n"; Hai cách khác nhau để khai báo xâu. string2 được khởi tạo và kích thước được xác định tự động. Ví dụ về đọc xâu từ bàn phím và in ra. 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 28 24 25 26 // output characters until null character is reached for ( int i = 0; string1[ i ] != '\0'; i++ ) cout << string1[ i ] << ' '; 27 28 29 Có thể truy nhập xâu giống fig04_12.cpp như đối với mảng. Vòng lặp (2 of 2) cin >> string1; // reads "there" kết thúc khi gặp ký tự null. cout << "\nstring1 is: " << string1 << endl; fig04_12.cpp output (1 of 1) return 0; // indicates successful termination 30 31 32 33 } // end main Enter the string "hello there": hello there string1 is: hello string2 is: string literal string1 with spaces between characters is: h e l l o string1 is: there ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 29 4.4 Ví dụ về sử dụng mảng • Kiểu lưu trữ tĩnh – static storage (chương 3) – Nếu là static, các biến địa phương lưu lại giá trị giữa các lần gọi hàm – chỉ được nhìn thấy trong thân hàm – Có thể khai báo mảng địa phương là static • được khởi tạo về 0 static int array[3]; • Nếu không phải static – Được tạo (và huỷ) tại mỗi lần gọi hàm © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 fig04_13.cpp (1 of 3) 4 5 6 using std::cout; using std::endl; 7 8 9 void staticArrayInit( void ); void automaticArrayInit( void ); 10 11 12 13 14 15 int main() { cout << "First call to each function:\n"; staticArrayInit(); automaticArrayInit(); // function prototype // function prototype 16 17 18 19 20 cout << "\n\nSecond call to each function:\n"; staticArrayInit(); automaticArrayInit(); cout << endl; 21 22 return 0; 23 24 30 // Fig. 4.13: fig04_13.cpp // Static arrays are initialized to zero. #include <iostream> // indicates successful termination } // end main 25 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 // function to demonstrate a static local array void staticArrayInit( void ) Mảng static, khởi tạo về 0 tại { lần gọi hàm đầu tiên. // initializes elements to 0 first time function is called static int array1[ 3 ]; 31 32 cout << "\nValues on entering staticArrayInit:\n"; 33 34 35 36 // output contents of array1 for ( int i = 0; i < 3; i++ ) cout << "array1[" << i << "] = " << array1[ i ] << " 37 38 cout << "\nValues on exiting staticArrayInit:\n"; 39 40 41 42 43 // modify and output for ( int j = 0; j < cout << "array1[" << ( array1[ 44 45 contents of array1 3; j++ ) << j << "] = " j ] += 5 ) << " "; 31 fig04_13.cpp (2 of 3) "; Dữ liệu trong mảng bị thay đổi, các thay đổi được bảo toàn. } // end function staticArrayInit 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 47 48 49 50 51 // function to demonstrate an automatic local array void automaticArrayInit( void ) Mảng automatic, được tạo lại { tại mỗi lần gọi hàm. fig04_13.cpp // initializes elements each time function is called (3 of 3) int array2[ 3 ] = { 1, 2, 3 }; 52 53 cout << "\n\nValues on entering automaticArrayInit:\n"; 54 55 56 57 // output contents of array2 for ( int i = 0; i < 3; i++ ) cout << "array2[" << i << "] = " << array2[ i ] << " 58 59 cout << "\nValues on exiting automaticArrayInit:\n"; 60 61 62 63 64 // modify and output for ( int j = 0; j < cout << "array2[" << ( array2[ 65 66 contents of array2 3; j++ ) << j << "] = " j ] += 5 ) << " "; "; Tuy mảng bị thay đổi, nó sẽ bị huỷ khi hàm kết thúc và thayt đổi trong dữ liệu sẽ bị mất. } // end function automaticArrayInit ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 32 33 First call to each function: Values on array1[0] Values on array1[0] entering staticArrayInit: = 0 array1[1] = 0 array1[2] = 0 exiting staticArrayInit: = 5 array1[1] = 5 array1[2] = 5 Values on array2[0] Values on array2[0] entering automaticArrayInit: = 1 array2[1] = 2 array2[2] = 3 exiting automaticArrayInit: = 6 array2[1] = 7 array2[2] = 8 fig04_13.cpp output (1 of 1) Second call to each function: Values on array1[0] Values on array1[0] entering staticArrayInit: = 5 array1[1] = 5 array1[2] = 5 exiting staticArrayInit: = 10 array1[1] = 10 array1[2] = 10 Values on array2[0] Values on array2[0] entering automaticArrayInit: = 1 array2[1] = 2 array2[2] = 3 exiting automaticArrayInit: = 6 array2[1] = 7 array2[2] = 8 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 34 4.5 Truyền tham số cho hàm • Dùng tên mảng, bỏ cặp ngoặc vuông – Truyền mảng myArray cho hàm myFunction int myArray[ 24 ]; myFunction( myArray, 24 ); – Kích thước mảng thường được truyền, nhưng không nhất thiết • Có ích khi dùng để duyệt tất cả các phần tử • Mảng được truyền bằng tham chiếu (passed-by-reference) – Hàm có thể thay đổi dữ liệu gốc của mảng – Tên mảng có giá trị bằng địa chỉ của phần tử đầu tiên • Hàm biết mảng được lưu ở đâu. • Hàm có thể sửa đổi dữ liệu ghi trong mảng • Các phần tử mảng được truyền bằng giá trị (passed-byvalue) – Như các biến thông thường – square( myArray[3] ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 35 4.5 Truyền tham số cho hàm • Các hàm dùng mảng làm đối số – Function prototype • void modifyArray( int b[], int arraySize ); • void modifyArray( int [], int ); – Trong prototype, tên không bắt buộc • cả hai hàm lấy đối số là một mảng số nguyên và 1 số nguyên – Không ghi cần kích thước mảng trong cặp ngoặc • Trình biên dịch bỏ qua – Nếu khai báo 1 tham số là const • đối số đó sẽ không thể bị thay đổi (chương trình dịch báo lỗi) • void doNotModify( const int [] ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 36 // Fig. 4.14: fig04_14.cpp // Passing arrays and individual array elements to functions. #include <iostream> fig04_14.cpp (1 of 3) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> Cú pháp cho mảng trong danh sách tham số 9 10 using std::setw; 11 12 13 void modifyArray( int [], int ); void modifyElement( int ); 14 15 16 17 18 int main() { const int arraySize = 5; int a[ arraySize ] = { 0, 1, 2, 3, 4 }; // appears strange // size of array a // initialize a 19 20 21 cout << "Effects of passing entire array by reference:" << "\n\nThe values of the original array are:\n"; 22 23 24 25 // output original array for ( int i = 0; i < arraySize; i++ ) cout << setw( 3 ) << a[ i ]; CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 27 28 29 30 31 32 Truyền tên mảng (a) và kích thước cho hàm. Mảng truyền bằng tham chiếu cout << endl; // pass array a to modifyArray by reference modifyArray( a, arraySize ); cout << "The values of the modified array are:\n"; 33 34 35 36 // output modified array for ( int j = 0; j < arraySize; j++ ) cout << setw( 3 ) << a[ j ]; 37 38 39 40 41 // output value of a[ 3 ] cout << "\n\n\n" << "Effects of passing array element by value:" << "\n\nThe value of a[3] is " << a[ 3 ] << '\n'; 1 phần tử mảng được truyền bằng giá trị; giá trị phần tử gốc không thể bị thay đổi. 42 43 44 // pass array element a[ 3 ] by value modifyElement( a[ 3 ] ); 45 46 47 // output value of a[ 3 ] cout << "The value of a[3] is " << a[ 3 ] << endl; 48 49 return 0; 50 51 fig04_14.cpp (2 of 3) // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 37 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 Tuy đặt tên là b, khi được gọi, mảng chỉ // in function modifyArray, "b" points to đến mảng a, nên hàm có thể thay đổi dữ // the original array "a" in memory liệu của a. fig04_14.cpp void modifyArray( int b[], int sizeOfArray ) (3 of 3) { // multiply each array element by 2 for ( int k = 0; k < sizeOfArray; k++ ) b[ k ] *= 2; } // end function modifyArray // in function modifyElement, "e" is a local copy of // array element a[ 3 ] passed from main Các phần tử đơn lẻ của mảng được void modifyElement( int e ) truyền bằng giá trị, và các giá trị gốc { không thể bị thay đổi. // multiply parameter by 2 cout << "Value in modifyElement is " << ( e *= 2 ) << endl; } // end function modifyElement ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 38 39 Effects of passing entire array by reference: The values of 0 1 2 3 The values of 0 2 4 6 the original array are: 4 the modified array are: 8 fig04_14.cpp output (1 of 1) Effects of passing array element by value: The value of a[3] is 6 Value in modifyElement is 12 The value of a[3] is 6 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 40 1 2 3 // Fig. 4.15: fig04_15.cpp // Demonstrating the const type qualifier. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 void tryToModifyArray( const int [] ); 9 10 11 12 int main() { int a[] = { 10, 20, 30 }; 13 14 tryToModifyArray( a ); 15 16 cout << a[ 0 ] << ' ' << a[ 1 ] << ' ' << a[ 2 ] << '\n'; 17 18 return 0; 19 20 Tham số mảng được khai báo là const. Mảng không thểfig04_15.cpp bị sửa đổi, kể cả khi nó được (1 of 2) truyền bằng tham chiếu. // function prototype // indicates successful termination } // end main 21 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 23 24 25 26 27 28 29 30 // In function tryToModifyArray, "b" cannot be used // to modify the original array "a" in main. void tryToModifyArray( const int b[] ) { b[ 0 ] /= 2; // error b[ 1 ] /= 2; // error b[ 2 ] /= 2; // error 41 fig04_15.cpp (2 of 2) } // end function tryToModifyArray ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 42 4.6 Sắp xếp mảng • Sắp xếp dữ liệu – Là một ứng dụng quan trọng – Hầu hết mọi cơ quan/tổ chức đều phải sắp xếp dữ liệu • Một khối lượng khổng lồ dữ liệu cần được sắp xếp • Xếp nổi bọt (Bubble sort) – Duyệt mảng vài lần – So sánh cặp phần tử liên tiếp • Nếu thứ tự tăng (hoặc bằng nhau), không thay đổi gì • Nếu thứ tự giảm, tráo đổi hai phần tử – Lặp lại các bước trên cho mọi phần tử © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 43 4.6 Sắp xếp mảng • Ví dụ: – Đi từ trái sang phải, và tráo các phần tử khi cần thiết • Một lần duyệt cho mỗi phần tử – – – – – – – Dãy gốc: 3 4 2 7 6 Lần duyệt 1: 3 2 4 6 7 (tráo đổi phần tử) Lần duyệt 2: 2 3 4 6 7 Lần duyệt 3: 2 3 4 6 7 (không cần thay đổi) Lần duyệt 4: 2 3 4 6 7 Lần duyệt 5: 2 3 4 6 7 Phần tử nhỏ “nổi" lên trên (như số 2 trong ví dụ) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 44 4.6 Sắp xếp mảng • Tráo đổi các biến int x = 3, y = 4; y = x; x = y; • Cái gì xảy ra? – Cả x và y đều là 3! – Cần có biến tạm • Giải pháp int x = 3, temp = x; x = y; y = temp; y = 4, temp = 0; // temp là 3 // x là 4 // y là 3 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 45 // Fig. 4.16: fig04_16.cpp // This program sorts an array's values into ascending order. #include <iostream> fig04_16.cpp (1 of 3) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 using std::setw; 11 12 13 14 15 16 int main() { const int arraySize = 10; // size of array a int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; int hold; // temporary location used to swap array elements 17 18 cout << "Data items in original order\n"; 19 20 21 22 // output original array for ( int i = 0; i < arraySize; i++ ) cout << setw( 4 ) << a[ i ]; 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Duyệt 1 lần cho mỗi phần tử // bubble sort của mảng. // loop to control number of passes for ( int pass = 0; pass < arraySize - 1; pass++ ) // loop to control number of comparisons per pass for ( int j = 0; j < arraySize - 1; j++ ) fig04_16.cpp (2 of 3) // compare side-by-side elements and swap them if // first element is greater than second element if ( a[ j ] > a[ j + 1 ] ) { Nếu phần tử bên trái (chỉ số j) hold = a[ j ]; lớn hơn phần tử bên phải (chỉ số a[ j ] = a[ j + 1 ]; j + 1), thì ta tráo đổi chúng. a[ j + 1 ] = hold; Nhớ sử dụng biến tạm. } // end if 39 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 46 47 40 cout << "\nData items in ascending order\n"; 41 42 43 44 // output sorted array for ( int k = 0; k < arraySize; k++ ) cout << setw( 4 ) << a[ k ]; fig04_16.cpp (3 of 3) 45 46 cout << endl; 47 48 fig04_16.cpp output (1 of 1) return 0; 49 50 // indicates successful termination } // end main Data items in original order 2 6 4 8 10 12 89 68 Data items in ascending order 2 4 6 8 10 12 37 45 45 37 68 89 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 4.7 Ví dụ: sử dụng mảng để tính Mean, Median và Mode 48 • Mean – Giá trị trung bình (tổng/số phần tử) • Median – Giá trị ở giữa dãy đã được sắp xếp – 1, 2, 3, 4, 5 (3 là median) – Nếu số phần tử là số chẵn, lấy trung bình của 2 số giữa • Mode – Giá trị xuất hiện nhiều nhất – 1, 1, 1, 2, 3, 3, 4, 5 (1 là mode) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 4.17: fig04_17.cpp // This program introduces the topic of survey data analysis. // It computes the mean, median, and mode of the data. #include <iostream> 5 6 7 8 9 using using using using 10 11 #include <iomanip> 12 13 14 using std::setw; using std::setprecision; 15 16 17 18 19 20 void void void void void 21 22 23 24 int main() { const int responseSize = 99; 49 fig04_17.cpp (1 of 8) std::cout; std::endl; std::fixed; std::showpoint; mean( const int [], int ); median( int [], int ); mode( int [], int [], int ); bubbleSort( int[], int ); printArray( const int[], int ); 25 // size of array responses ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 int frequency[ 10 ] = { 0 }; 27 28 29 30 31 32 33 34 35 36 37 38 39 // initialize array responses int response[ responseSize ] = { 6, 7, 8, 9, 8, 7, 8, 9, 7, 8, 9, 5, 9, 8, 7, 8, 6, 7, 8, 9, 3, 9, 8, 7, 7, 8, 9, 8, 9, 8, 9, 7, 6, 7, 8, 7, 8, 7, 9, 8, 7, 8, 9, 8, 9, 8, 9, 7, 5, 6, 7, 2, 5, 3, 9, 4, 7, 8, 9, 6, 8, 7, 8, 9, 7, 4, 4, 2, 5, 3, 8, 7, 4, 5, 6, 1, 6, 5, 7, 8, 40 41 42 43 44 // process responses mean( response, responseSize ); median( response, responseSize ); mode( frequency, response, responseSize ); 45 46 return 0; 47 48 50 // initialize array frequency 8, 9, 7, 8, 8, 7, 8, 9, 9, 2, 5, 3, 6, 4, 7, 8, 5, 6, 7 }; fig04_17.cpp (2 of 8) // indicates successful termination } // end main 49 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 50 51 52 53 54 55 cout << "********\n 56 57 58 59 // total response values for ( int i = 0; i < arraySize; i++ ) total += answer[ i ]; 60 61 62 // format and output results cout << fixed << setprecision( 4 ); 63 64 65 66 67 68 69 70 71 cout << << << << << << << << 72 73 51 // calculate average of all response values void mean( const int answer[], int arraySize ) { int total = 0; Mean\n********\n"; fig04_17.cpp (3 of 8) "The mean is the average value of the data\n" "items. The mean is equal to the total of\n" "all the data items divided by the number\n" "of data items (" << arraySize Đổi sang double để được giá trị "). The mean value for\nthis run is: " trung bình bằng số thực (thay vì giá trị total << " / " << arraySize << " = " nguyên). static_cast< double >( total ) / arraySize "\n\n"; } // end function mean 74 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 52 // sort array and determine median element's value void median( int answer[], int size ) { cout << "\n********\n Median\n********\n" << "The unsorted array of responses is"; printArray( answer, size ); bubbleSort( answer, size ); fig04_17.cpp (4 of 8) Sắp xếp mảng bằng cách // output unsorted array // sort array truyền nó cho một hàm. Bảo vệ tính modun của chương trình cout << "\n\nThe sorted array is"; printArray( answer, size ); // output sorted array // display median element cout << "\n\nThe median is element " << size / 2 << " of\nthe sorted " << size << " element array.\nFor this run the median is " << answer[ size / 2 ] << "\n\n"; } // end function median 95 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 96 // determine most frequent response 97 void mode( int freq[], int answer[], int size ) 98 { 99 int largest = 0; // represents largest frequency 100 int modeValue = 0; // represents most frequent response 101 102 cout << "\n********\n 103 104 105 106 // initialize frequencies to 0 for ( int i = 1; i <= 9; i++ ) freq[ i ] = 0; 107 108 109 110 // summarize frequencies for ( int j = 0; j < size; j++ ) ++freq[ answer[ j ] ]; 111 112 113 114 115 116 // output headers for result columns cout << "Response" << setw( 11 ) << "Frequency" << setw( 19 ) << "Histogram\n\n" << setw( 55 ) << "1 1 2 2\n" << setw( 56 ) << "5 0 5 0 5\n\n"; 53 fig04_17.cpp (5 of 8) Mode\n********\n"; 117 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 118 119 120 121 122 123 124 125 126 127 128 54 // output results for ( int rating = 1; rating <= 9; rating++ ) { cout << setw( 8 ) << rating << setw( 11 ) << freq[ rating ] << " "; fig04_17.cpp (6 of 8) // keep track of mode value and largest fequency value if ( freq[ rating ] > largest ) { mode là giá trị xuất hiện largest = freq[ rating ]; nhiều nhất (có giá trị cao nhất modeValue = rating; trong mảng freq). } // end if 129 130 131 132 // output histogram bar representing frequency value for ( int k = 1; k <= freq[ rating ]; k++ ) cout << '*'; 133 134 cout << '\n'; 135 136 } // end outer for 137 138 139 140 141 // display the mode value cout << "The mode is the most frequent value.\n" << "For this run the mode is " << modeValue << " which occurred " << largest << " times." << endl; // begin new line of output 142 143 } // end function mode CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 144 145 // function that sorts an array with bubble sort algorithm 146 void bubbleSort( int a[], int size ) 147 { 148 int hold; // temporary location used to swap elements 149 150 151 152 153 154 55 fig04_17.cpp (7 of 8) // loop to control number of passes for ( int pass = 1; pass < size; pass++ ) // loop to control number of comparisons per pass for ( int j = 0; j < size - 1; j++ ) 155 156 157 158 159 160 // swap elements if out of order if ( a[ j ] > a[ j + 1 ] ) { hold = a[ j ]; a[ j ] = a[ j + 1 ]; a[ j + 1 ] = hold; 161 162 } // end if 163 164 } // end function bubbleSort 165 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 56 166 // output array contents (20 values per row) 167 void printArray( const int a[], int size ) 168 { 169 for ( int i = 0; i < size; i++ ) { 170 171 172 if ( i % 20 == 0 ) cout << endl; 173 174 cout << setw( 2 ) << a[ i ]; 175 176 fig04_17.cpp (8 of 8) // begin new line every 20 values } // end for 177 178 } // end function printArray ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt ******** Mean ******** The mean is the average value of the data items. The mean is equal to the total of all the data items divided by the number of data items (99). The mean value for this run is: 681 / 99 = 6.8788 ******** Median ******** The unsorted array of responses is 6 7 8 9 8 7 8 9 8 9 7 8 9 5 9 8 7 8 7 8 6 7 8 9 3 9 8 7 8 7 7 8 9 8 9 8 9 7 8 9 6 7 8 7 8 7 9 8 9 2 7 8 9 8 9 8 9 7 5 3 5 6 7 2 5 3 9 4 6 4 7 8 9 6 8 7 8 9 7 8 7 4 4 2 5 3 8 7 5 6 4 5 6 1 6 5 7 8 7 The sorted 1 2 2 2 3 5 6 6 6 6 7 7 7 7 7 8 8 8 8 8 9 9 9 9 9 array 3 3 3 6 6 6 7 7 7 8 8 8 9 9 9 is 4 4 6 6 7 7 8 8 9 9 4 7 7 8 9 4 7 7 8 9 4 7 7 8 9 5 7 8 8 9 5 7 8 8 9 5 7 8 8 9 5 7 8 8 9 5 7 8 8 9 5 7 8 8 9 57 fig04_17.cpp output (1 of 2) 5 7 8 8 The median is element 49 of the sorted 99 element array. For this run the median is 7 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt ******** Mode ******** Response 58 Frequency Histogram 5 1 0 1 5 2 0 2 5 fig04_17.cpp output (2 of 2) 1 1 * 2 3 *** 3 4 **** 4 5 ***** 5 8 ******** 6 9 ********* 7 23 *********************** 8 27 *************************** 9 19 ******************* The mode is the most frequent value. For this run the mode is 8 which occurred 27 times. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 59 4.8 Tìm kiếm trên mảng: Tìm kiếm Tuyến tính và tìm kiếm Nhị phân • Tìm một giá trị khoá (key value) trên mảng • Tìm kiếm tuyến tính – So sánh từng phần tử của mảng với key • Bắt đầu từ một đầu, đi đến đầu kia của mảng – Hữu dụng cho mảng nhỏ và chưa sắp xếp • Không hiệu quả • Nếu giá trị cần tìm không có trong mảng thì phải kiểm tra tất cả các phần tử © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 60 4.8 Tìm kiếm trên mảng: Tìm kiếm Tuyến tính và tìm kiếm Nhị phân • Tìm kiếm nhị phân – Chỉ sử dụng cho mảng đã sắp xếp – So sánh phần tử ở giữa (middle) với key • Nếu bằng, tìm thấy • Nếu key < middle – Lặp lại ở nửa đầu của mảng • Nếu key > middle – Lặp lại ở nửa cuối – Rất nhanh • Nhiều nhất là N bước với 2 N > số phần tử của mảng • mảng 30 phần tử cần nhiều nhất 5 bước 5 2 > 30 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 61 // Fig. 4.19: fig04_19.cpp // Linear search of an array. #include <iostream> 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 int linearSearch( const int [], int, int ); 10 11 12 13 14 15 int main() { const int arraySize = 100; int a[ arraySize ]; int searchKey; Lấy đối số là một mảng, khoá cần tìm, và kích thước mảng. // prototype // size of array a // create array a // value to locate in a 16 17 18 for ( int i = 0; i < arraySize; i++ ) a[ i ] = 2 * i; 19 20 21 cout << "Enter integer search key: "; cin >> searchKey; 22 23 24 // attempt to locate searchKey in array a int element = linearSearch( a, searchKey, arraySize ); 25 fig04_19.cpp (1 of 2) // create some data ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 // display results if ( element != -1 ) cout << "Found value in element " << element << endl; else cout << "Value not found" << endl; 31 32 return 0; 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 62 fig04_19.cpp (2 of 2) Enter integer search key: 36 Found value in element 18 // indicates successful termination Enter integer search key: 37 Value not found } // end main // compare key to every element of array until location is // found or until end of array is reached; return subscript of // element if key or -1 if key not found int linearSearch( const int array[], int key, int sizeOfArray ) { for ( int j = 0; j < sizeOfArray; j++ ) if ( array[ j ] == key ) return j; return -1; // if found, // return location of key // key not found } // end function linearSearch ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 63 // Fig. 4.20: fig04_20.cpp // Binary search of an array. #include <iostream> fig04_20.cpp (1 of 6) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 #include <iomanip> 10 11 using std::setw; 12 13 14 15 16 // function prototypes int binarySearch( const int [], int, int, int, int ); void printHeader( int ); void printRow( const int [], int, int, int, int ); 17 18 19 20 21 22 int main() { const int arraySize = 15; int a[ arraySize ]; int key; 23 24 25 // size of array a // create array a // value to locate in a for ( int i = 0; i < arraySize; i++ ) a[ i ] = 2 * i; 26 CuuDuongThanCong.com // create some data ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 64 27 28 cout << "Enter a number between 0 and 28: "; cin >> key; 29 30 printHeader( arraySize ); 31 32 33 34 // search for key in array a int result = binarySearch( a, key, 0, arraySize - 1, arraySize ); 35 36 37 38 39 40 41 // display results if ( result != -1 ) cout << '\n' << key << " found in array element " << result << endl; else cout << '\n' << key << " not found" << endl; 42 43 return 0; 44 45 fig04_20.cpp (2 of 6) // indicates successful termination } // end main 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 47 48 49 50 51 52 53 54 65 // function to perform binary search of an array int binarySearch( const int b[], int searchKey, int low, int high, int size ) { int middle; fig04_20.cpp (3 of 6) // loop until low subscript is greater than high subscript while ( low <= high ) { Xác định phần tử ở giữa 55 56 57 // determine middle element of subarray being searched middle = ( low + high ) / 2; 58 59 60 // display subarray used in this loop iteration printRow( b, low, middle, high, size ); 61 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 62 63 64 // if searchKey matches middle element, return middle if ( searchKey == b[ middle ] ) // match return middle; 65 66 else 67 68 69 70 71 Sử dụng tìm Nhị phân: fig04_20.cpp Nếu key bằng middle, (4 oftìm 6) thấy Nếu nhỏ hơn, tìm nửa thấp // if searchKey less than middle element, // set new high element Nếu lớn hơn, tìm nửa cao if ( searchKey < b[ middle ] ) high = middle - 1; // search low end of array 72 73 74 75 76 77 } 78 79 return -1; 80 81 66 // if searchKey greater than middle element, // set new low element Vòng lặp tạo low, middle và high tự động. Nếu tìm else nửa cao, thì phần tử low mới sẽ cao hơn middle. low = middle + 1; // search high end of array // searchKey not found } // end function binarySearch ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 82 83 84 85 86 67 // print header for output void printHeader( int size ) { cout << "\nSubscripts:\n"; 87 88 89 90 // output column heads for ( int j = 0; j < size; j++ ) cout << setw( 3 ) << j << ' '; 91 92 cout << '\n'; 93 94 95 96 // output line of - characters for ( int k = 1; k <= 4 * size; k++ ) cout << '-'; 97 98 cout << endl; fig04_20.cpp (5 of 6) // start new line of output // start new line of output 99 100 } // end function printHeader 101 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 102 103 104 105 106 107 108 // print one row of output showing the current // part of the array being processed void printRow( const int b[], int low, int mid, int high, int size ) { // loop through entire array for ( int m = 0; m < size; m++ ) 109 110 111 112 // display spaces if outside current subarray range if ( m < low || m > high ) cout << " "; 113 114 115 // display middle element marked with a * else 116 117 118 if ( m == mid ) // mark middle value cout << setw( 3 ) << b[ m ] << '*'; 119 120 121 122 // display other elements in subarray else cout << setw( 3 ) << b[ m ] << ' '; 123 124 cout << endl; 68 fig04_20.cpp (6 of 6) // start new line of output 125 126 } // end function printRow CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 69 Enter a number between 0 and 28: 6 Subscripts: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -----------------------------------------------------------0 2 4 6 8 10 12 14* 16 18 20 22 24 26 28 0 2 4 6* 8 10 12 fig04_20.cpp output (1 of 2) 6 found in array element 3 Enter a number between 0 and 28: 25 Subscripts: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -----------------------------------------------------------0 2 4 6 8 10 12 14* 16 18 20 22 24 26 28 16 18 20 22* 24 26 28 24 26* 28 24* 25 not found ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 70 Enter a number between 0 and 28: 8 Subscripts: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -----------------------------------------------------------0 2 4 6 8 10 12 14* 16 18 20 22 24 26 28 0 2 4 6* 8 10 12 8 10* 12 8* fig04_20.cpp output (2 of 2) 8 found in array element 4 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 71 4.9 Mảng nhiều chiều • Đa chỉ số – int a[ 3 ][ 4 ]; – a[ i ][ j ] – Các bảng có dòng và cột – Dòng trước, cột sau – “Mảng của mảng” • a[0] là một mảng 4 phần tử • a[0][0] là phần tử đầu tiên của mảng Row 0 Column 0 a[ 0 ][ 0 ] Column 1 a[ 0 ][ 1 ] Column 2 a[ 0 ][ 2 ] Column 3 a[ 0 ][ 3 ] Row 1 a[ 1 ][ 0 ] a[ 1 ][ 1 ] a[ 1 ][ 2 ] a[ 1 ][ 3 ] Row 2 a[ 2 ][ 0 ] a[ 2 ][ 1 ] a[ 2 ][ 2 ] a[ 2 ][ 3 ] Column subscript (chỉ số cột) Array name Row subscript (chỉ số dòng) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 72 4.9 Mảng nhiều chiều • Khởi tạo – Mặc định là 0 – Khởi tạo, mỗi dòng trong 1 cặp ngoặc int b[ 2 ][ 2 ] = { { 1, 2 }, { 3, 4 } }; Row 0 Row 1 int b[ 2 ][ 2 ] = { { 1 }, { 3, 4 } }; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com 1 2 3 4 1 0 3 4 Chương 4. https://fb.com/tailieudientucntt 73 4.9 Mảng nhiều chiều • Truy nhập đến như bình thường cout << b[ 0 ][ 1 ]; – In ra 0 1 0 3 4 – Không sử dụng dấu phẩy (,) cout << b[ 0, 1 ]; • Lỗi cú pháp • Function prototype – Phải chỉ rõ kích thước của các chỉ số • Không đòi hỏi kích thước cho chỉ số đầu tiên, cũng như mảng 1 chiều – void printArray( int [][ 3 ] ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 74 // Fig. 4.22: fig04_22.cpp // Initializing multidimensional arrays. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 void printArray( int [][ 3 ] ); 9 10 11 12 13 14 int main() { int array1[ 2 ][ 3 ] = { { 1, 2, 3 }, { 4, 5, 6 } }; int array2[ 2 ][ 3 ] = { 1, 2, 3, 4, 5}; int array3[ 2 ][ 3 ] = { { 1, 2 }, { 4 } }; Chú ý cấu trúc của prototype. Chú ý nhiều cách khởi tạo. Các phần tử trong array2 được gán từ dòng thứ nhất rồi đến dòng thứ hai. 15 16 17 cout << "Values in array1 by row are:" << endl; printArray( array1 ); 18 19 20 cout << "Values in array2 by row are:" << endl; printArray( array2 ); 21 22 23 cout << "Values in array3 by row are:" << endl; printArray( array3 ); 24 25 26 return 0; // indicates successful termination } // end main CuuDuongThanCong.com fig04_22.cpp (1 of 2) ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 28 29 30 31 32 33 34 35 36 37 38 39 40 41 75 // function to output array with two rows and three columns void printArray( int a[][ 3 ] ) Vòng lặp for thường được fig04_22.cpp { dùng để quét qua mảng. Sử of 2) for ( int i = 0; i < 2; i++ ) { // for each row dụng vòng lặp(2 lồng nhau cho for ( int j = 0; j < 3; j++ ) cout << a[ i ][ j ] << ' '; cout << endl; mảng nhiều chiều. fig04_22.cpp // output column values output (1 of 1) // start new line of output } // end outer for structure } // end function printArray Values in array1 by row are: 1 2 3 4 5 6 Values in array2 by row are: 1 2 3 4 5 0 Values in array3 by row are: 1 2 0 4 0 0 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 76 4.9 Mảng nhiều chiều • Tiếp theo: chương trình ví dụ về khởi tạo mảng – – – – Chương trình lưu trữ điểm của sinh viên Mảng nhiều chiều (bảng) Dòng là sinh viên Cột là điểm Quiz1 Quiz2 Student0 95 85 Student1 89 80 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 4. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 using using using using 9 10 #include <iomanip> 11 12 13 using std::setw; using std::setprecision; 14 15 16 const int students = 3; const int exams = 4; 17 18 19 20 21 22 23 77 // Fig. 4.23: fig04_23.cpp // Double-subscripted array example. #include <iostream> fig04_23.cpp (1 of 6) std::cout; std::endl; std::fixed; std::left; // number of students // number of exams // function prototypes int minimum( int [][ exams ], int, int ); int maximum( int [][ exams ], int, int ); double average( int [], int ); void printArray( int [][ exams ], int, int ); ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 29 30 78 int main() { // initialize student grades for three students (rows) int studentGrades[ students ][ exams ] = { { 77, 68, 86, 73 }, { 96, 87, 89, 78 }, { 70, 90, 86, 81 } }; fig04_23.cpp (2 of 6) 31 32 33 34 // output array studentGrades cout << "The array is:\n"; printArray( studentGrades, students, exams ); 35 36 37 38 39 40 // determine smallest and largest grade values cout << "\n\nLowest grade: " << minimum( studentGrades, students, exams ) << "\nHighest grade: " << maximum( studentGrades, students, exams ) << '\n'; 41 42 cout << fixed << setprecision( 2 ); 43 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 44 45 46 47 48 49 // calculate average grade for each student for ( int person = 0; person < students; person++ ) cout << "The average grade for student " << person << " is " << average( studentGrades[ person ], exams ) << endl; 50 51 return 0; // indicates successful termination 79 fig04_23.cpp (3 of 6) Tính điểm trung bình cho sinh viên. Ta truyền dòng chứa điểm của sinh viên vào hàm. Chú ý: studentGrades[0] cũng là một mảng. 52 53 } // end main 54 55 56 57 58 // find minimum grade int minimum( int grades[][ exams ], int pupils, int tests ) { int lowGrade = 100; // initialize to highest possible grade 59 60 61 62 63 64 65 66 67 68 69 for ( int i = 0; i < pupils; i++ ) for ( int j = 0; j < tests; j++ ) if ( grades[ i ][ j ] < lowGrade ) lowGrade = grades[ i ][ j ]; return lowGrade; } // end function minimum CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 80 // find maximum grade int maximum( int grades[][ exams ], int pupils, int tests ) { int highGrade = 0; // initialize to lowest possible grade fig04_23.cpp (4 of 6) for ( int i = 0; i < pupils; i++ ) for ( int j = 0; j < tests; j++ ) if ( grades[ i ][ j ] > highGrade ) highGrade = grades[ i ][ j ]; return highGrade; } // end function maximum 86 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 87 88 89 90 91 92 93 94 95 96 97 98 81 // determine average grade for particular student double average( int setOfGrades[], int tests ) { int total = 0; fig04_23.cpp (5 of 6) // total all grades for one student for ( int i = 0; i < tests; i++ ) total += setOfGrades[ i ]; return static_cast< double >( total ) / tests; // average } // end function maximum ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 99 100 // Print the array 101 void printArray( int grades[][ exams ], int pupils, int tests ) 102 { 103 // set left justification and output column heads 104 cout << left << " [0] [1] [2] [3]"; 105 106 107 fig04_23.cpp (6 of 6) // output grades in tabular format for ( int i = 0; i < pupils; i++ ) { 108 109 110 // output label for row cout << "\nstudentGrades[" << i << "] "; 111 112 113 114 // output one grades for one student for ( int j = 0; j < tests; j++ ) cout << setw( 5 ) << grades[ i ][ j ]; 115 116 82 } // end outer for 117 118 } // end function printArray ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 83 The array is: [0] studentGrades[0] 77 studentGrades[1] 96 studentGrades[2] 70 [1] 68 87 90 [2] 86 89 86 [3] 73 78 81 fig04_23.cpp output (1 of 1) Lowest grade: 68 Highest grade: 96 The average grade for student 0 is 76.00 The average grade for student 1 is 87.50 The average grade for student 2 is 81.75 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 5 – Con trỏ và Xâu ký tự © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 2 Chương 5 – Con trỏ và Xâu ký tự Đề mục 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.10 5.11 5.12 Giới thiệu Khai báo và khởi tạo biến con trỏ Các thao tác trên con trỏ Gọi hàm bằng tham chiếu Sử dụng const với con trỏ Sắp xếp nổi bọt sử dụng Pass-by-Reference Các phép toán trên con trỏ Quan hệ giữa con trỏ và mảng Mảng con trỏ Ví dụ: giả lập tráo và chia bài Con trỏ tới hàm Giới thiệu về xử lý ký tự và xâu 5.12.1 Tổng quát về ký tự và xâu 5.12.2 Các hàm xử lý xâu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 3 5.1 Giới thiệu • Con trỏ (Pointer) – Mạnh, nhưng khó làm chủ – Có tác dụng như truyền tham chiếu (pass-by-reference) – Có liên quan chặt chẽ đến mảng và xâu • Biến con trỏ (Pointer variable) – Chứa địa chỉ vùng nhớ thay vì chứa giá trị – Thông thường, biến chứa giá trị (tham chiếu trực tiếp) – Con trỏ chứa địa chỉ của biến mang giá trị countPtr cụ thể (tham chiếu gián tiếp) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com count 7 count 7 Chương 5. https://fb.com/tailieudientucntt 4 5.2 Khai báo và khởi tạo biến con trỏ • Khai báo con trỏ – * cho biết biến là con trỏ int *myPtr; dữ liệu kiểu int có địa chỉ là myPtr, con trỏ kiểu int * – Mỗi con trỏ cần một dấu sao int *myPtr1, *myPtr2; – Có thể khai báo con trỏ tới bất cứ kiểu dữ liệu nào • Khởi tạo con trỏ (Pointer initialization) – Khởi tạo về 0, NULL, hoặc địa chỉ • 0 hoặc NULL không trỏ đến đâu cả © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 5 Các thao tác đối với con trỏ 5.3 • & Toán tử địa chỉ (address operator) – Trả về địa chỉ vùng nhớ của toán hạng – Ví dụ int y = 5; int *yPtr; yPtr = &y; – yPtr “trỏ đến” y yPtr y 5 // yPtr chứa địa chỉ của y y yptr 12FEA8 12FED4 12FED4 5 địa chỉ của y là giá trị của yptr © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 6 5.3 Các thao tác đối với con trỏ • * phép thâm nhập (indirection/dereferencing) – Trả về đối tượng mà con trỏ trỏ tới – *yPtr trả về y (vì yPtr trỏ đến y). – con trỏ khi bị thâm nhập (dereferenced) là giá trị trái (lvalue) *yptr = 9; // assigns 9 to y • * và & ngược nhau © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 fig05_04.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 9 10 11 int main() { int a; int *aPtr; 12 13 14 a = 7; aPtr = &a; 15 16 17 cout << "The address of a is " << &a << "\nThe value of aPtr is " << aPtr; 18 19 20 cout << "\n\nThe value of a is " << a << "\nThe value of *aPtr is " << *aPtr; 21 22 23 24 25 7 // Fig. 5.4: fig05_04.cpp // Using the & and * operators. #include <iostream> // a is an integer // aPtr is a pointer to an integer // aPtr assigned address of a * và & ngược nhau cout << "\n\nShowing that * and & are inverses of " << "each other.\n&*aPtr = " << &*aPtr << "\n*&aPtr = " << *&aPtr << endl; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 return 0; 8 // indicates successful termination } // end main fig05_04.cpp (2 of 2) The address of a is 0012FED4 The value of aPtr is 0012FED4 fig05_04.cpp output (1 of 1) The value of a is 7 The value of *aPtr is 7 Showing that * and & are inverses of each other. &*aPtr = 0012FED4 * và & ngược nhau; cùng kết quả khi *&aPtr = 0012FED4 cùng sử dụng cả 2 với aPtr ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 9 5.4 Gọi hàm bằng tham chiếu • 3 cách truyền tham số cho hàm – Truyền giá trị (Pass-by-value) – Truyền tham chiếu với đối số là reference with reference arguments) – Truyền tham chiếu pointer arguments) tham chiếu (Pass-by- với đối số là con trỏ (Pass-by-reference with © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 10 5.4 Gọi hàm bằng tham chiếu • Truyền tham chiếu với đối số là tham chiếu – Thay đổi giá trị gốc của tham số – hàm có thể “trả về” nhiều hơn một giá trị • Truyền tham chiếu bằng đối số là con trỏ – Tương tự pass-by-reference • Sử dụng con trỏ và toán tử * – Truyền địa chỉ của đối số bằng toán tử & – Truyền mảng không cần toán tử & vì tên mảng chính là con trỏ – Toán tử thâm nhập * được dùng cùng con trỏ để tạo một tên khác cho biến được truyền vào © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 fig05_06.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 int cubeByValue( int ); 9 10 11 12 int main() { int number = 5; 13 14 11 // Fig. 5.6: fig05_06.cpp // Cube a variable using pass-by-value. #include <iostream> // prototype cout << "The original value of number is " Truyền << number; number bằng giá trị; kết quả được trả về bởi cubeByValue 15 16 17 // pass number by value to cubeByValue number = cubeByValue( number ); 18 19 cout << "\nThe new value of number is " << number << endl; 20 21 return 0; 22 23 24 // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 29 30 // calculate and return cube of integer argument int cubeByValue( int n ) { cubeByValue nhận tham return n * n * n; // cube local variable n and return result số passed-by-value } // end function cubeByValue Tính lập phương và trả về biến địa phương (local variable) n 12 fig05_06.cpp (2 of 2) fig05_06.cpp output (1 of 1) The original value of number is 5 The new value of number is 125 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.7: fig05_07.cpp // Cube a variable using pass-by-reference // with a pointer argument. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 void cubeByReference( int * ); 10 11 12 13 int main() { int number = 5; 13 fig05_07.cpp (1 of 2) Prototype cho biết tham số là con trỏ trỏ đến dữ liệu kiểu int // prototype Dùng toán tử địa chỉ & để truyền địa chỉ của number tới cubeByReference 14 15 cout << "The original value of number is " << number; 16 17 18 // pass address of number to cubeByReference cubeByReference( &number ); 19 20 cout << "\nThe new value of number is " << number << endl; 21 22 return 0; 23 24 25 // indicates successful termination cubeByReference thay đổi biến number } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 31 14 // calculate cube of *nPtr; modifies variable number in main void cubeByReference( int *nPtr ) { *nPtr = *nPtr * *nPtr * *nPtr; // cube *nPtr } // end function cubeByReference The original value of number is 5 The new value of number is 125 fig05_07.cpp cubeByReference nhận địa chỉ (2 of 2) của biến kiểu int, tức là con trỏ trỏ đến một số int fig05_07.cpp output (1 of 1) Thay đổi và truy nhập biến kiểu int sử dụng toán tử thâm nhập * ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 15 5.5 Sử dụng const với con trỏ • Tính chất của const – Giá trị của biến không thay đổi – const được sử dụng cho một biến khi hàm không cần thay đổi biến đó. • Nguyên tắc quyền ưu tiên tối thiểu – Chỉ cho hàm đủ quyền truy nhập để thực hiện nhiệm vụ của mình, không cho nhiều quyền hơn. • Bốn cách truyền con trỏ cho hàm – Con trỏ thường trỏ đến dữ liệu thường • Khả năng truy cập cao nhất – Con trỏ thường trỏ đến hằng dữ liệu – Hằng con trỏ trỏ đến dữ liệu thường – Hằng con trỏ trỏ đến hằng dữ liệu • Ít quyền truy cập nhất © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.10: fig05_10.cpp // Converting lowercase letters to uppercase letters // using a non-constant pointer to non-constant data. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 #include <cctype> 10 11 void convertToUppercase( char * ); 12 13 14 15 int main() { char phrase[] = "characters and $32.98"; fig05_10.cpp (1 of 2) Con trỏ thường đến dữ liệu thường // prototypes for islower and toupper convertToUppercase thay đổi biến phrase 16 17 18 19 20 cout << "The phrase before conversion is: " << phrase; convertToUppercase( phrase ); cout << "\nThe phrase after conversion is: " << phrase << endl; 21 22 return 0; 23 24 25 16 // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 31 32 33 34 35 36 37 38 // convert string to uppercase letters void convertToUppercase( char *sPtr ) { sPtr là con trỏ thường trỏ fig05_10.cpp while ( *sPtr != '\0' ) { // current character '\0' đếnis dữ not liệu thường 17 (2 of 2) if ( islower( *sPtr ) ) // if character is lowercase, *sPtr = toupper( *sPtr ); // convert to uppercase ++sPtr; Hàm islower trả về true nếu ký tự là chữ thường fig05_10.cpp output (1 of 1) // move sPtr to next character in string Hàm toupper trả về chữ hoa nếu ký tự ban đầu là chữ thường; nếu không toupper trả về ký tự đó (chữ hoa) } // end while } // end function convertToUppercase Khi dùng toán tử ++ cho con trỏ trỏ đến mảng, địa chỉ vùng nhớ lưu trong con trỏ sẽ được sửa để con trỏ trỏ đến phần tử tiếp theo của mảng. The phrase before conversion is: characters and $32.98 The phrase after conversion is: CHARACTERS AND $32.98 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.11: fig05_11.cpp // Printing a string one character at a time using // a non-constant pointer to constant data. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 void printCharacters( const char * ); 10 11 12 13 int main() { char phrase[] = "print characters of a string"; fig05_11.cpp (1 of 2) Tham số là con trỏ thường trỏ đến hằng dữ liệu 14 15 16 17 cout << "The string is:\n"; printCharacters( phrase ); cout << endl; 18 19 return 0; 20 21 22 18 Truyền con trỏ phrase cho hàm printCharacters. // indicates successful termination } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 28 29 30 19 // sPtr cannot modify the character to which it points, // i.e., sPtr is a "read-only" pointer void printCharacters( const char *sPtr ) fig05_11.cpp { (2 of 2) for ( ; *sPtr != '\0'; sPtr++ ) // no initialization cout << *sPtr; sPtr là con trỏ thường trỏ đến hằng } // end function printCharacters fig05_11.cpp dữ liệu; không thể thay đổi ký tự mà output (1 of 1) sPtr trỏ đến. Tăng sPtr để trỏ đến ký tự tiếp theo. The string is: print characters of a string ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 20 // Fig. 5.12: fig05_12.cpp // Attempting to modify data through a // non-constant pointer to constant data. 4 5 void f( const int * ); 6 7 8 9 int main() { int y; fig05_12.cpp (1 of 1) // prototype Tham số là con trỏ thường trỏ đến hằng dữ liệu. 10 11 f( &y ); // f attempts illegal modification 12 13 return 0; // indicates successful termination fig05_12.cpp output (1 of 1) Truyền địa chỉ của biến y để thử thay đổi một cách không hợp lệ. 14 15 } // end main 16 17 18 19 20 21 // xPtr cannot modify the value of the variable // to which it points Cố thay đổi đối tượng hằng (const object) void f( const int *xPtr ) mà xPtr trỏ đến. { *xPtr = 100; // error: cannot modify a const object 22 23 } // end function f Lỗi sinh ra khi biên dịch. d:\cpphtp4_examples\ch05\Fig05_12.cpp(21) : error C2166: l-value specifies const object CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 21 5.5 Sử dụng const với con trỏ • const pointers - hằng con trỏ – Luôn trỏ đến vùng nhớ cố định – là mặc định cho tên mảng – Phải được khởi tạo khi khai báo © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 22 // Fig. 5.13: fig05_13.cpp // Attempting to modify a constant pointer to // non-constant data. fig05_13.cpp (1 of 1) int main() { int x, y; fig05_13.cpp output (1 of 1) ptr là hằng con trỏ trỏ tới số nguyên. 8 9 10 11 12 // ptr is a constant pointer to an integer that can Có thể thay đổi x (trỏ bởi // be modified throughptr) ptr, but ptrphải always points to the vì x không là hằng // same memory location. Không thể cho ptr trỏ đến int * const ptr = &x; 13 14 15 *ptr = 7; ptr = &y; // allowed: *ptr is not const // error: ptr is const; cannot assign new address 16 17 return 0; // indicates successful termination 18 19 địa chỉ mới vì ptr là hằng Dòng 15 sinh ra lỗi biên dịch vì thay đổi địa chỉ mới cho constant pointer. } // end main d:\cpphtp4_examples\ch05\Fig05_13.cpp(15) : error C2166: l-value specifies const object ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 5 6 using std::cout; using std::endl; 7 8 9 10 int main() { int x = 5, y; 11 12 13 14 15 16 17 23 // Fig. 5.14: fig05_14.cpp // Attempting to modify a constant pointer to constant data. #include <iostream> fig05_14.cpp (1 of 1) ptr là hằng con trỏ trỏ tới hằng số nguyên. // ptr is a constant pointer to a constant integer. // ptr always points to the same location; the integer // at that location cannot be modified. Không thể thay đổi x (trỏ bởi ptr) vì const int *const ptr = &x; khai báo *ptr là hằng. cout << *ptr << endl; Không thể cho ptr trỏ đến địa chỉ mới vì ptr được khai báo là hằng. 18 19 20 *ptr = 7; ptr = &y; // error: *ptr is const; cannot assign new value // error: ptr is const; cannot assign new address 21 22 return 0; // indicates successful termination 23 24 } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 5.6 Sắp xếp nổi bọt sử dụng truyền tham chiếu • bubbleSort dùng con trỏ – Hàm swap truy nhập các phần tử của mảng • Các phần tử đơn của mảng: dữ liệu vô hướng (scalars) – Mặc định là pass by value • Truyền tham chiếu bằng toán tử địa chỉ & © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.15: fig05_15.cpp // This program puts values into an array, sorts the values into // ascending order, and prints the resulting array. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 #include <iomanip> 10 11 using std::setw; 12 13 14 void bubbleSort( int *, const int ); void swap( int * const, int * const ); 15 16 17 18 19 int main() { const int arraySize = 10; int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 }; 20 21 22 23 24 25 25 fig05_15.cpp (1 of 3) // prototype // prototype cout << "Data items in original order\n"; for ( int i = 0; i < arraySize; i++ ) cout << setw( 4 ) << a[ i ]; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 bubbleSort( a, arraySize ); 27 28 cout << "\nData items in ascending order\n"; 26 // sort the array 29 30 31 for ( int j = 0; j < arraySize; j++ ) cout << setw( 4 ) << a[ j ]; 32 33 cout << endl; 34 35 return 0; fig05_15.cpp (2 of 3) Khai báo là int *array (thay vì int array[]) để cho hàm bubbleSort nhận mảng 1 chiều. // indicates successful termination Hai cách khai báo này là như nhau. 36 37 } // end main 40 41 42 43 void bubbleSort( int *array, const int size ) { // loop to control passes for ( int pass = 0; pass < size - 1; pass++ ) Nhận tham số kích thước của mảng; 38 khai báo là const để chắc chắn 39 // sort an array of integers using bubble sort algorithm rằng size sẽ không bị thay đổi. 44 45 46 47 48 49 50 // loop to control comparisons during each pass for ( int k = 0; k < size - 1; k++ ) // swap adjacent elements if they are out of order if ( array[ k ] > array[ k + 1 ] ) swap( &array[ k ], &array[ k + 1 ] ); CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 51 52 53 54 55 56 57 58 59 60 61 62 27 } // end function bubbleSort fig05_15.cpp // swap values at memory locations to which (3 of 3) // element1Ptr and element2Ptr point void swap( int * const element1Ptr, int * const element2Ptr ) fig05_15.cpp { output (1 of 1) int hold = *element1Ptr; *element1Ptr = *element2Ptr; Truyền tham chiếu, cho phép *element2Ptr = hold; hàm tráo giá trị tại vùng nhớ. } // end function swap Data items in original order 2 6 4 8 10 12 89 68 Data items in ascending order 2 4 6 8 10 12 37 45 45 37 68 89 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 28 5.6 Sắp xếp nổi bọt sử dụng truyền tham chiếu • sizeof – Toán tử trả về kích thước byte của toán hạng – Với mảng, sizeof trả về giá trị ( kích thước 1 phần tử ) * ( số phần tử ) – Nếu sizeof( int ) = 4, thì int myArray[10]; cout << sizeof(myArray); sẽ in ra 40 • sizeof có thể dùng với – Tên biến cout << "sizeof c = " << sizeof c – Tên kiểu dữ liệu cout << sizeof( char ) – Hằng số © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.16: fig05_16.cpp // Sizeof operator when used on an array name // returns the number of bytes in the array. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 size_t getSize( double * ); 10 11 12 13 int main() { double array[ 20 ]; 29 fig05_16.cpp (1 of 2) // prototype sizeof trả về tổng số byte của mảng. 14 15 16 cout << "The number of bytes in the array is " << sizeof( array ); 17 18 19 cout << "\nThe number of bytes returned by getSize is " << getSize( array ) << endl; 20 21 return 0; 22 23 24 // indicates successful termination Hàm getSize trả về số byte được dùng để lưu địa chỉ mảng array. } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 // return size of ptr size_t getSize( double *ptr ) { return sizeof( ptr ); 29 30 } // end function getSize 30 fig05_16.cpp (2 of 2) sizeof trả về số byte của con trỏ. fig05_16.cpp output (1 of 1) The number of bytes in the array is 160 The number of bytes returned by getSize is 4 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 31 5.7 Các phép toán đối với con trỏ • Các phép toán con trỏ – Tăng/giảm con trỏ (++ hoặc --) – Cộng/trừ 1 số nguyên với 1 con trỏ ( + hoặc += , - hoặc -=) – Con trỏ có thể trừ lẫn nhau – Cộng trừ với con trỏ là vô nghĩa trừ khi dùng cho con trỏ mảng • Ví dụ: Mảng 5 phần tử int trên máy dùng kiểu int 4 byte – vPtr trỏ đến phần tử thứ nhất v[ 0 ], tại địa chỉ 3000 vPtr = 3000 – vPtr += 2; trỏ vPtr tới 3008 vùng nhớ 3000 3004 3008 3012 3016 vPtr trỏ tới v[ 2 ] v[0] v[1] v[2] v[3] v[4] biến con trỏ vPtr © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 32 5.7 Các phép toán đối với con trỏ • Trừ con trỏ (Subtracting pointers) – Trả về số phần tử giữa 2 địa chỉ vPtr2 = v[ 2 ]; vPtr = v[ 0 ]; vPtr2 - vPtr == 2 • Gán con trỏ (Pointer assignment) – Một con trỏ có thể được gán cho con trỏ khác nếu cả hai cùng kiểu – Nếu không cùng kiểu thì phải đổi kiểu (cast) – Ngoại lệ: con trỏ tới void (kiểu void *) • con trỏ tổng quát, đại diện cho kiểu bất kỳ • không cần đổi kiểu để chuyển sang con trỏ sang dạng void pointer • Không thể (dùng *) lấy dữ liệu của con trỏ kiểu void © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 33 5.7 Các phép toán đối với con trỏ • So sánh con trỏ (Pointer comparison) – Sử dụng các toán tử quan hệ để so sánh địa chỉ chứa trong con trỏ – Ví dụ: có hai con trỏ trỏ đến hai phần tử của một mảng, chỉ ra con trỏ trỏ đến phần tử được đánh số thứ tự cao – So sánh là vô nghĩa trừ khi các con trỏ trỏ đến các phần tử của cùng một mảng – Thường dùng để xác định khi con trỏ có giá trị bằng 0 (null) (không trỏ đến đâu cả) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 34 5.8 Quan hệ giữa Con trỏ và Mảng • Mảng và con trỏ có quan hệ chặt chẽ – Tên mảng cũng như hằng con trỏ (constant pointer) – Có thể dùng chỉ số đối với các con trỏ • Dùng con trỏ để truy nhập các phần tử mảng – Phần tử b[ n ] có thể truy nhập bởi *( bPtr + n ) • ký hiệu pointer/offset – Địa chỉ • &b[ 3 ] tương đương bPtr + 3 – Tên mảng có thể coi như con trỏ • b[ 3 ] tương đương *( b + 3 ) – Con trỏ có thể viết với cặp ngoặc vuông (ký hiệu pointer/subscript) • bPtr[ 3 ] tương đương b[ 3 ] © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 // Fig. 5.20: fig05_20.cpp // Using subscripting and pointer notations with arrays. 3 4 #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 11 12 int main() { int b[] = { 10, 20, 30, 40 }; int *bPtr = b; // set bPtr to point to array b 13 14 15 16 // output array b using array subscript notation cout << "Array b printed with:\n" << "Array subscript notation\n"; 17 18 19 for ( int i = 0; i < 4; i++ ) cout << "b[" << i << "] = " << b[ i ] << '\n'; 20 21 22 23 24 25 35 fig05_20.cpp (1 of 2) Sử dụng ký hiệu chỉ số mảng. // output array b using the array name and // pointer/offset notation cout << "\nPointer/offset notation where " << "the pointer is the array name\n"; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 36 for ( int offset1 = 0; offset1 < 4; offset1++ ) cout << "*(b + " << offset1 << ") = " << *( b + offset1 ) << '\n'; fig05_20.cpp (2 of 2) 29 30 31 // output array b using bPtr and array subscript notation cout << "\nPointer subscript notation\n"; Sử dụng tên mảng và ký hiệu pointer/offset. 32 33 34 for ( int j = 0; j < 4; j++ ) cout << "bPtr[" << j << "] = " << bPtr[ j ] << '\n'; 35 36 cout << "\nPointer/offset notation\n"; 37 38 39 40 41 // output array b using bPtr and pointer/offset notation for ( int offset2 = 0; offset2 < 4; offset2++ ) cout << "*(bPtr + " << offset2 << ") = " << *( bPtr + offset2 ) << '\n'; 42 43 return 0; 44 45 Sử dụng ký hiệu chỉ số cho con trỏ. // indicates successful termination Sử dụng bPtr và ký hiệu pointer/offset. } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 37 Array b printed with: Array subscript notation b[0] = 10 b[1] = 20 b[2] = 30 b[3] = 40 fig05_20.cpp output (1 of 1) Pointer/offset notation where the pointer is the array name *(b + 0) = 10 *(b + 1) = 20 *(b + 2) = 30 *(b + 3) = 40 Pointer bPtr[0] bPtr[1] bPtr[2] bPtr[3] subscript notation = 10 = 20 = 30 = 40 Pointer/offset notation *(bPtr + 0) = 10 *(bPtr + 1) = 20 *(bPtr + 2) = 30 *(bPtr + 3) = 40 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 5.21: fig05_21.cpp // Copying a string using array notation // and pointer notation. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 void copy1( char *, const char * ); void copy2( char *, const char * ); 11 12 13 14 15 16 17 int main() { char string1[ 10 ]; char *string2 = "Hello"; char string3[ 10 ]; char string4[] = "Good Bye"; fig05_21.cpp (1 of 2) // prototype // prototype 18 19 20 copy1( string1, string2 ); cout << "string1 = " << string1 << endl; 21 22 23 copy2( string3, string4 ); cout << "string3 = " << string3 << endl; 24 25 return 0; // indicates successful termination CuuDuongThanCong.com 38 ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 39 26 27 } // end main 28 29 30 31 32 33 // copy s2 to s1 using array notation void copy1( char *s1, const char *s2 ) { for ( int i = 0; ( s1[ i ] = s2[ i ] ) != '\0'; i++ ) ; // do nothing in body 34 35 } // end function copy1 36 37 38 39 40 41 // copy s2 to s1 using pointer notation Sử dụng ký hiệu con trỏ để copy xâu void copy2( char *s1, const char *s2 ) tại s2 vào mảng ký tự s1. { for ( ; ( *s1 = *s2 ) != '\0'; s1++, s2++ ) ; // do nothing in body 42 43 } // end function copy2 Sử dụng chỉ số mảng để copy xâu tại s2 vào mảng ký tự s1. fig05_21.cpp (2 of 2) string1 = Hello string3 = Good Bye fig05_21.cpp output (1 of 1) Tăng cả hai con trỏ để trỏ đến phần tử tiếp theo trong mảng tương ứng. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 40 5.9 Mảng con trỏ • Mảng chứa con trỏ – Thường dùng để lưu mảng của xâu char *suit[ 4 ] = {"Hearts", "Diamonds", "Clubs", "Spades" }; – Mỗi phần tử của suit trỏ đến char * (1 xâu) – Mảng không chứa xâu, chỉ trỏ đến xâu suit[0] ’H’ ’e’ ’a’ ’r’ ’t’ ’s’ ’\0’ suit[1] ’D’ ’i’ ’a’ ’m’ ’o’ ’n’ ’d’ suit[2] ’C’ ’l’ ’u’ ’b’ ’s’ ’\0’ suit[3] ’S’ ’p’ ’a’ ’d’ ’e’ ’s’ ’s’ ’\0’ ’\0’ – Mảng suit có kích thước cố định, nhưng xâu thì không © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 41 5.10 Ví dụ: Giả lập tráo bài và chia bài Tú-lơ-khơ • Chương trình tráo bài (Card shuffling program) – Dùng một mảng gồm các con trỏ trỏ đến xâu để lưu trữ tên các chất (suit), i.e. cơ (hearts), rô (diamonds), pích (spades), tép (clubs) – Sử dụng một mảng hai chiều (hàng: chất, cột: giá trị) Ace 0 Hearts 0 Diamonds 1 Clubs 2 Spades 3 Two 1 Three Four 2 3 Five 4 Six 5 Seven Eight Nine 6 7 8 Ten 9 Jack 10 Queen King 11 12 deck[2][12] biểu diễn K-tép Clubs King – Ghi các số từ 1-52 vào mảng để làm thứ tự chia các con bài © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 42 5.10 Ví dụ: Giả lập tráo bài và chia bài Tú-lơ-khơ Làm mịn lần hai • Thuật toán tráo (shuffle) và chia (deal) bài Ban đầu Initialize the suit array Initialize the face array Initialize the deck array Shuffle the deck (tráo bài) Deal 52 cards (chia bài) Làm mịn Choose slot of deck randomly While chosen slot of deck has been previously chosen (Trong khi ô vừa chọn đã bị chọn từ trước) For each of the 52 cards Place card number in randomly selected unoccupied slot of deck (Đặt chỉ số quân bài vào một ô ngẫu nhiên còn trống trong desk) Choose slot of deck randomly (chọn ngẫu nhiên một ô) Place card number in chosen slot of deck (đặt chỉ số quân bài vào ô được chọn) For each of the 52 cards For each slot of the deck array Find card number in deck array and print face and suit of card (Tìm chỉ số quân bài trong mảng desk và in ra số hiệu và chất của quân bài) If slot contains card number Print the face and suit of the card © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 43 // Fig. 5.24: fig05_24.cpp // Card shuffling dealing program. #include <iostream> fig05_24.cpp (1 of 4) 4 5 6 7 using std::cout; using std::left; using std::right; 8 9 #include <iomanip> 10 11 using std::setw; 12 13 14 #include <cstdlib> #include <ctime> 15 16 17 18 // prototypes void shuffle( int [][ 13 ] ); void deal( const int [][ 13 ], const char *[], const char *[] ); 19 20 21 22 23 24 25 // prototypes for rand and srand // prototype for time mảng suit chứa các con trỏ int main() trỏ đến các mảng char. { // initialize suit array const char *suit[ 4 ] = { "Hearts", "Diamonds", "Clubs", "Spades" }; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 // initialize face array const char *face[ 13 ] = { "Ace", "Deuce", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" }; 31 32 33 // initialize deck array int deck[ 4 ][ 13 ] = { 0 }; 34 35 srand( time( 0 ) ); 36 37 38 shuffle( deck ); deal( deck, face, suit ); 39 40 return 0; 41 42 44 fig05_24.cpp (2 of 4) mảng face chứa các con trỏ trỏ đến các mảng char. // seed random number generator // indicates successful termination } // end main 43 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 44 45 46 47 48 49 50 51 45 // shuffle cards in deck void shuffle( int wDeck[][ 13 ] ) { int row; int column; fig05_24.cpp (3 of 4) // for each of the 52 cards, choose slot of deck randomly for ( int card = 1; card <= 52; card++ ) { 52 53 54 55 56 57 // choose new random location until unoccupied slot found do { Vị trí hiện tại có dòng và cột được chọn row = rand() % 4; ngẫu nhiên. column = rand() % 13; } while ( wDeck[ row ][ column ] != 0 ); // end do/while 58 59 60 // place card number in chosen slot of deck wDeck[ row ][ column ] = card; 61 62 63 64 } // end for } // end function shuffle 65 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 66 67 68 69 70 71 72 73 74 75 76 77 46 // deal cards in deck void deal( const int wDeck[][ 13 ], const char *wFace[], const char *wSuit[] ) { // for each of the 52 cards for ( int card = 1; card <= 52; card++ ) // loop through rows of wDeck for ( int row = 0; row <= 3; row++ ) fig05_24.cpp (4 of 4) Căn lề phải trong một vùng gồm 5 ký tự. // loop through columns of wDeck for current rowCăn lề trái trong một vùng gồm for ( int column = 0; column <= 12; column++ ) 8 ký tự. 78 79 80 81 82 83 84 // if slot contains current card, display card if ( wDeck[ row ][ column ] == card ) { cout << setw( 5 ) << right << wFace[ column ] << " of " << setw( 8 ) << left << wSuit[ row ] << ( card % 2 == 0 ? '\n' : '\t' ); 85 86 } // end if 87 88 } // end function deal ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Nine Five Queen Jack Jack Three Ten Ace Seven Six Ace Nine Six Ten Four Ten Eight Jack Four Seven Queen Nine Deuce King Queen Five of of of of of of of of of of of of of of of of of of of of of of of of of of Spades Spades Diamonds Spades Diamonds Clubs Clubs Hearts Spades Hearts Clubs Hearts Spades Spades Clubs Hearts Hearts Hearts Diamonds Hearts Spades Clubs Hearts Clubs Clubs Hearts Seven Eight Three Five Three Six Nine Queen Deuce Deuce Deuce Seven Eight King Ace Four Eight Ten King King Four Six Jack Three Five Ace CuuDuongThanCong.com of of of of of of of of of of of of of of of of of of of of of of of of of of Clubs Clubs Hearts Diamonds Diamonds Clubs Diamonds Hearts Spades Clubs Diamonds Diamonds Diamonds Hearts Spades Spades Spades Diamonds Diamonds Spades Hearts Diamonds Clubs Spades Clubs Diamonds 47 fig05_24.cpp output (1 of 1) ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 48 5.11 Con trỏ tới hàm (Function Pointer) • Con trỏ tới hàm – chứa địa chỉ của hàm – Tên mảng có giá trị là địa chỉ của phần tử đầu tiên của mảng – Tương tự, tên hàm có giá trị là địa chỉ bắt đầu của đoạn mã định nghĩa hàm • Các con trỏ tới hàm có thể – – – – được truyền vào trong hàm được trả về từ hàm được lưu trong mảng được gán cho các con trỏ hàm khác © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 49 5.11 Con trỏ tới hàm • Gọi hàm bằng con trỏ tới hàm – giả sử compare được khai báo là con trỏ tới hàm có kiểu tham số và kiểu trả về như sau: • bool ( *compare ) ( int, int ) – gọi hàm bằng một trong hai cách • ( *compare ) ( int1, int2 ) – thâm nhập con trỏ để chạy hàm được con trỏ trỏ tới HOẶC • compare( int1, int2 ) – dễ nhầm lẫn • người dùng có thể tưởng compare là tên của hàm thực trong chương trình © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 fig05_25.cpp (1 of 5) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 #include <iomanip> 10 11 using std::setw; 12 13 14 15 16 17 // prototypes void bubble( int [], const int, bool (*)( int, int ) ); void swap( int * const, int * const ); bool ascending( int, int ); bool descending( int, int ); 18 19 20 21 22 23 24 }; 25 50 // Fig. 5.25: fig05_25.cpp // Multipurpose sorting program using function pointers. #include <iostream> Tham số thứ ba là con trỏ tới một hàm nhận 2 tham số int và trả về kết quả kiểu bool. int main() { const int arraySize = 10; int order; int counter; int a[ arraySize ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 31 32 33 51 cout << "Enter 1 to sort in ascending order,\n" << "Enter 2 to sort in descending order: "; cin >> order; cout << "\nData items in original order\n"; fig05_25.cpp (2 of 5) // output original array for ( counter = 0; counter < arraySize; counter++ ) cout << setw( 4 ) << a[ counter ]; 34 35 36 37 38 39 40 // sort array in ascending order; pass function ascending // as an argument to specify ascending sorting order if ( order == 1 ) { bubble( a, arraySize, ascending ); cout << "\nData items in ascending order\n"; } 41 42 43 44 45 46 47 // sort array in descending order; pass function descending // as an agrument to specify descending sorting order else { bubble( a, arraySize, descending ); cout << "\nData items in descending order\n"; } 48 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 52 49 50 51 // output sorted array for ( counter = 0; counter < arraySize; counter++ ) cout << setw( 4 ) << a[ counter ]; 52 53 fig05_25.cpp cout << endl; (3 of 5) compare là con trỏ tới một hàm nhận return 0; // indicates successful termination 2 tham số kiểu int và trả về giá trị kiểu bool. 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 } // end main // multipurpose bubble sort; parameter compare is a pointer to // the comparison function that determines sorting order void bubble( int work[], const int size, bool (*compare)( int, int ) ) { Dùng ngoặc để chỉ rõ đây là con trỏ tới hàm // loop to control passes for ( int pass = 1; pass < size; pass++ ) gọi hàm compare được truyền vào; thâm nhập con trỏ để chạy hàm. // loop to control number of comparisons per pass for ( int count = 0; count < size - 1; count++ ) // if adjacent elements are out of order, swap them if ( (*compare)( work[ count ], work[ count + 1 ] ) ) swap( &work[ count ], &work[ count + 1 ] ); } // end function bubble CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 53 75 76 77 78 79 80 81 82 // swap values at memory locations to which // element1Ptr and element2Ptr point fig05_25.cpp void swap( int * const element1Ptr, int * const element2Ptr ) (4 of 5) { Enter 1 to sort in ascending order, int hold = *element1Ptr; Enter 2 to sort in descending order: 1 *element1Ptr = *element2Ptr; Data items in original order *element2Ptr = hold; 83 84 } // end function swap 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 2 6 4 8 10 12 89 68 Data items in ascending order 2 4 6 8 10 12 37 45 45 37 68 89 // determine whether elements are out of order // for an ascending order sort Enter 1 to sort in ascending order, bool ascending( int a, int b ) Enter 2 to sort in descending order: 2 { return b < a;// swap if b is less than a Data items in original order } // end function ascending 2 6 4 8 10 12 89 68 Data items in descending order 89 68 45 37 12 10 8 6 45 37 4 2 // determine whether elements are out of order // for a descending order sort bool descending( int a, int b ) { return b > a; // swap if b is greater than a } // end function descending CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 54 5.11 Con trỏ tới hàm • Mảng gồm các con trỏ hàm – Thường dùng cho các hệ thống điều khiển bằng thực đơn (menu-driven system) – Các con trỏ đến từng hàm được lưu trong mảng con trỏ hàm • các hàm đều phải có kiểu dữ liệu trả về giống nhau, và kiểu dữ liệu của tham số như nhau – Ánh xạ (lựa chọn thực đơn Æ chỉ số trong mảng con trỏ tới hàm) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 55 // Fig. 5.26: fig05_26.cpp // Demonstrating an array of pointers to functions. #include <iostream> fig05_26.cpp (1 of 3) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 // function prototypes void function1( int ); void function2( int ); void function3( int ); 13 14 15 16 17 18 int main() { // initialize array of 3 pointers to functions that each // take an int argument and return void void (*f[ 3 ])( int ) = { function1, function2, function3 }; 19 20 21 22 23 24 Mảng được khởi tạo với tên của ba hàm, tên của hàm chính là con trỏ. int choice; cout << "Enter a number between 0 and 2, 3 to end: "; cin >> choice; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 56 // process user's choice while ( choice >= 0 && choice < 3 ) { 27 28 29 30 // invoke function at location choice in array f // and pass choice as an argument (*f[ choice ])( choice ); 31 32 33 34 } 35 36 cout << "Program execution completed." << endl; 37 38 return 0; cout << "Enter a number between 0 and 2, 3 to end: "; cin >> choice; Gọi hàm được chọn bằng cách thâm nhập vào (dereferencing) phần tử tương ứng trong mảng. // indicates successful termination 39 40 } // end main 41 42 43 44 45 void function1( int a ) { cout << "You entered " << a << " so function1 was called\n\n"; 46 47 48 fig05_26.cpp (2 of 3) } // end function1 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 49 50 51 52 void function2( int b ) { cout << "You entered " << b << " so function2 was called\n\n"; 53 54 } // end function2 55 56 57 58 59 void function3( int c ) { cout << "You entered " << c << " so function3 was called\n\n"; 60 61 } // end function3 57 fig05_26.cpp (3 of 3) fig05_26.cpp output (1 of 1) Enter a number between 0 and 2, 3 to end: 0 You entered 0 so function1 was called Enter a number between 0 and 2, 3 to end: 1 You entered 1 so function2 was called Enter a number between 0 and 2, 3 to end: 2 You entered 2 so function3 was called Enter a number between 0 and 2, 3 to end: 3 Program execution completed. CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 58 5.12.1 Tổng kết về ký tự và xâu ký tự • Hằng ký tự - Character constant – Giá trị nguyên biểu diễn dưới dạng một ký tự viết trong 2 dấu nháy – 'z' là giá trị nguyên của ký tự z • Mã 122 trong bảng mã ASCII • Xâu ký tự - String – Chuỗi các ký tự được coi như là một single unit – Có thể bao gồm chữ cái, chữ số, ký tự đặc biệt +, -, * ... – Hằng xâu ký tự - String literal (string constants) • Viết trong cặp nháy kép, ví dụ: "I like C++" – Mảng của các ký tự, kết thúc với ký tự rỗng (null character) '\0' – Xâu là một hằng con trỏ (constant pointer) • Trỏ đến ký tự đầu tiên của xâu – Giống như với mảng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 59 5.12.1 Tổng kết về ký tự và xâu ký tự • Gán giá trị cho xâu - String assignment – Mảng của ký tự • char color[] = "blue"; – Tạo mảng color 5 phần tử kiểu char • phần tử cuối cùng là '\0' – Biến kiểu char * • char *colorPtr = "blue"; – Tạo con trỏ colorPtr trỏ đến chữ b trong xâu "blue" • "blue" ở đâu đó trong bộ nhớ – Một cách khác cho mảng ký tự • char color[] = { 'b', 'l', 'u', 'e', '\0' }; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 60 5.12.1 Tổng kết về ký tự và xâu ký tự • Đọc xâu – Đọc dữ liệu cho mảng ký tự word[ 20 ] cin >> word • Đọc các ký tự cho đến khi gặp ký tự trắng hoặc EOF • Xâu có thể vượt quá kích thước mảng cin >> setw( 20 ) >> word; • Đọc 19 ký tự (để lại chỗ cho '\0') • cin.getline – Đọc 1 dòng văn bản – cin.getline( array, size, delimiter ); – Lưu input vào mảng array đến khi xảy ra một trong hai trường hợp • Kích thước dữ liệu đạt đến size – 1 • Ký tự delimiter được nhập vào – Ví dụ char sentence[ 80 ]; cin.getline( sentence, 80, '\n' ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 61 5.12.2 Các hàm xử lý xâu ký tự • Thư viện xử lý xâu <cstring> cung cấp các hàm – – – – thao tác với dữ liệu kiểu xâu so sánh xâu tìm kiếm trên xâu các ký tự hoặc xâu khác chia xâu thành các từ tố (tokenize strings) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 62 5.12.2 Các hàm xử lý xâu ký tự char *strcpy( char *s1, const char *s2 ); Copy xâu s2 vào xâu s1. Trả về giá trị của s1. char *strncpy( char *s1, const char *s2, size_t n ); Copy nhiều nhất n ký tự của xâu s2 vào xâu s1. Trả về giá trị của s1. char *strcat( char *s1, const char *s2 ); Thêm xâu s2 vào sau xâu s1. Ký tự đầu tiên của s2 ghi đè lên ký tự null của s1. Trả về giá trị của s1. char *strncat( char *s1, const char *s2, size_t n ); Thêm xâu nhiều nhất là n ký tự của s2 vào sau xâu s1. Ký tự đầu tiên của s2 ghi đè lên ký tự null của s1. Trả về giá trị của s1. int strcmp( const char *s1, const char *s2 ); So sánh xâu s1 và xâu s2. Hàm trả về giá trị 0, nhỏ hơn 0, hoặc lớn hơn 0 nếu s1 bằng, nhỏ hơn hoặc lớn hơn s2. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 63 5.12.2 Các hàm xử lý xâu ký tự int strncmp( const char *s1, const char *s2, size_t n ); So sánh n ký tự xâu s1 và xâu s2. Hàm trả về giá trị 0, nhỏ hơn 0 hoặc lớn hơn 0 nếu s1 bằng, nhỏ hơn hoặc lớn hơn s2. char *strtok( char *s1, const char *s2 ); Một chuỗi lời gọi đến strtok chia xâu s1 thành các “tokens”—từ tố, chẳng hạn các từ trong một dòng văn bản—phân tách nhau bởi các ký tự chứa trong xâu s2. Lời gọi đầu tiên lấy s1 làm tham số thứ nhất, các lời gọi tiếp sau (với NULL là tham số thứ nhất) tiếp tục lấy các từ tố từ chính xâu đó. Mỗi lời gọi trả về một con trỏ tới từ tố vừa nhận được. Nếu không còn từ tố nào, hàm sẽ trả về giá trị NULL. size_t strlen( const char *s ); Xác định độ dài của xâu s. Trả về số ký tự của xâu (không tính ký tự null). © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 64 5.12.2 Các hàm xử lý xâu ký tự • Copy xâu – char *strcpy( char *s1, const char *s2 ) • Copy tham số thứ hai vào tham số thứ nhất – Tham số thứ nhất phải có kích thước đủ lớn để chứa xâu và ký tự null – char *strncpy( char *s1, const char *s2, size_t n ) • Xác định rõ số ký tự được copy từ xâu vào mảng • Không nhất thiết copy ký tự null © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 65 // Fig. 5.28: fig05_28.cpp // Using strcpy and strncpy. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <cstring> 9 10 11 12 13 14 int main() { char x[] = "Happy Birthday to You"; char y[ 25 ]; Copy toàn bộ xâu trong mảng char z[ 15 ]; x vào mảng y. 15 16 17 18 19 20 21 22 23 24 25 <cstring> chứa prototype cho strcpy và strncpy. strcpy( y, x ); fig05_28.cpp (1 of 2) // prototypes for strcpy and strncpy // copy contents of x into y Copy 14 ký tự đầu tiên của mảng x vào mảng y. Chú ý rằng lệnh này không viết ký tự null. cout << "The string in array x is: " << x << "\nThe string in array y is: " << y << '\n'; // copy first 14 characters of x into z strncpy( z, x, 14 ); // does not copy null character z[ 14 ] = '\0'; // append '\0' to z's contents cout << "The string in array z is: " << z << endl; CuuDuongThanCong.com Thêm ký tự null. ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 26 27 28 29 66 return 0; // indicates successful termination } // end main fig05_28.cpp (2 of 2) Xâu gốc. The string in array x is: Happy Birthday to You The string in array y is: Happy Birthday to You The string in array z is: Happy Birthday fig05_28.cpp output (1 of 1) Copy xâu bằng strcpy. Copy 14 ký tự đầu tiên bằng strncpy. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 67 5.12.2 Các hàm xử lý xâu ký tự • Nối xâu - Concatenating strings – char *strcat( char *s1, const char *s2 ) • Nối xâu thứ hai vào sau xâu thứ nhất • Ký tự đầu tiên của tham số thứ hai thay thế ký tự null của tham số thứ nhất • Phải chắc chắn rằng tham số thứ nhất có kích thước đủ lớn để chứa thêm phần nối vào và ký tự null kết thúc xâu. – char *strncat( char *s1, const char *s2, size_t n ) • Thêm n ký tự của tham số thứ hai vào sau tham số thứ nhất • Thêm ký tự null vào kết quả © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 68 // Fig. 5.29: fig05_29.cpp // Using strcat and strncat. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <cstring> 9 10 11 12 13 14 int main() { char s1[ 20 ] = "Happy "; char s2[] = "New Year "; char s3[ 40 ] = ""; fig05_29.cpp (1 of 2) <cstring> chứa prototype cho strcat và strncat. // prototypes for strcat and strncat Thêm s2 vào sau s1. 15 16 cout << "s1 = " << s1 << "\ns2 = " << s2; 17 18 strcat( s1, s2 ); 19 20 21 cout << "\n\nAfter strcat(s1, s2):\ns1 = " << s1 << "\ns2 = " << s2; Thêm 6 ký tự đầu tiên của s1 vào sau s3. 22 23 24 25 // concatenate s2 to s1 // concatenate first 6 characters of s1 to s3 strncat( s3, s1, 6 ); // places '\0' after last character ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 cout << "\n\nAfter strncat(s3, s1, 6):\ns1 = " << s1 << "\ns3 = " << s3; Thêm s1 vào sau s3. 28 29 30 31 strcat( s3, s1 ); // concatenate s1 to s3 cout << "\n\nAfter strcat(s3, s1):\ns1 = " << s1 << "\ns3 = " << s3 << endl; 32 33 34 35 return 0; // indicates successful termination 69 fig05_29.cpp (2 of 2) fig05_29.cpp output (1 of 1) } // end main s1 = Happy s2 = New Year After strcat(s1, s2): s1 = Happy New Year s2 = New Year After strncat(s3, s1, 6): s1 = Happy New Year s3 = Happy After strcat(s3, s1): s1 = Happy New Year s3 = Happy Happy New Year CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 70 5.12.2 Các hàm xử lý xâu ký tự • So sánh xâu - Comparing strings – Các ký tự được biểu diễn bằng mã dạng số (numeric code) • các mã đó được dùng để so sánh các xâu ký tự – Các bộ mã ký tự (Character codes / character sets) • ASCII “American Standard Code for Information Interchage” • EBCDIC “Extended Binary Coded Decimal Interchange Code” • Các hàm so sánh xâu – int strcmp( const char *s1, const char *s2 ) • So sánh từng ký tự một, theo thứ tự từ điển • Trả về – 0 nếu xâu bằng nhau – Giá trị âm nếu xâu thứ nhất nhỏ hơn xâu thứ hai – Giá trị dương nếu xâu thứ nhất lớn hơn xâu thứ hai – int strncmp( const char *s1, const char *s2, size_t n ) • So sánh n ký tự đầu tiên • Dừng so sánh nếu gặp ký tự null của 1 trong 2 tham số © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 fig05_30.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> <cstring> chứa prototype cho strcmp và strncmp. 9 10 using std::setw; 11 12 #include <cstring> 13 14 15 16 17 18 19 20 21 22 23 24 25 71 // Fig. 5.30: fig05_30.cpp // Using strcmp and strncmp. #include <iostream> // prototypes for strcmp and strncmp int main() { char *s1 = "Happy New Year"; char *s2 = "Happy New Year"; char *s3 = "Happy Holidays"; cout << << << << << << So sánh s1 với s2. So sánh s1 với s3. "s1 = " << s1 << "\ns2 = " << s2 "\ns3 = " << s3 << "\n\nstrcmp(s1, s2) = " setw( 2 ) << strcmp( s1, s2 ) So sánh s3 với s1. "\nstrcmp(s1, s3) = " << setw( 2 ) strcmp( s1, s3 ) << "\nstrcmp(s3, s1) = " setw( 2 ) << strcmp( s3, s1 ); ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 72 So sánh 6 ký tự đầu tiên của s1 với s3. 26 27 28 29 30 31 cout << << << << << 32 33 return 0; 34 35 "\n\nstrncmp(s1, s3, 6) = " << setw( 2 ) strncmp( s1, s3, 6 ) << "\nstrncmp(s1, s3, 7) = " setw( 2 ) << strncmp( s1, s3, 7 ) "\nstrncmp(s3, s1, 7) = " setw( 2 ) << strncmp( s3, s1, 7 ) << endl; fig05_30.cpp (2 of 2) fig05_30.cpp So sánh 7 kýoutput tự đầu (1 of 1) // indicates successful termination } // end main tiên của s1 với s3. So sánh 7 ký tự đầu tiên của s3 với s1. s1 = Happy New Year s2 = Happy New Year s3 = Happy Holidays strcmp(s1, s2) = 0 strcmp(s1, s3) = 1 strcmp(s3, s1) = -1 strncmp(s1, s3, 6) = 0 strncmp(s1, s3, 7) = 1 strncmp(s3, s1, 7) = -1 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 73 5.12.2 Các hàm xử lý xâu ký tự • Phân tích từ tố - Tokenizing – Chia xâu thành các từ tố, phân tách bởi các ký tự ngăn cách (delimiting character) – Các từ tố thường là các đơn vị logic (logical units), chẳng hạn các từ (tách nhau bởi các dấu trống) – "This is my string" có 4 từ tố (tách nhau bởi các dấu trống) – char *strtok( char *s1, const char *s2 ) • Cần gọi nhiều lần – Lần gọi đầu cần 2 tham số, xâu cần phân tích từ tố và xâu chứa các ký tự ngăn cách • Tìm ký tự ngăn cách tiếp theo và thay bằng ký tự null – Những lời gọi tiếp theo tiếp tục phân tích từ tố trên xâu đó • Gọi hàm với tham số thứ nhất là NULL © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 74 // Fig. 5.31: fig05_31.cpp // Using strtok. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <cstring> 9 10 11 12 13 int main() { char sentence[] = "This is a sentence with 7 tokens"; char *tokenPtr; 14 15 16 17 18 19 fig05_31.cpp (1 of 2) <cstring> chứa prototype cho strtok. // prototype for strtok cout << "The string to be tokenized is:\n" << sentence << "\n\nThe tokens are:\n\n"; Lời gọi strtok đầu tiên khởi đầu việc phân tích từ tố. // begin tokenization of sentence tokenPtr = strtok( sentence, " " ); 20 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 21 22 23 24 // continue tokenizing sentence until tokenPtr becomes NULL while ( tokenPtr != NULL ) { cout << tokenPtr << '\n'; tokenPtr = strtok( NULL, " " ); // get next token 25 26 } // end while 27 28 cout << "\nAfter strtok, sentence = " << sentence << endl; 29 30 return 0; 31 32 75 fig05_31.cpp (2 of 2) // indicates successful termination Các lời gọi strtok tiếp sau với NULL là tham số đầu để tiếp tục việc phân tích từ tố trên xâu sentence. } // end main The string to be tokenized is: This is a sentence with 7 tokens The tokens are: This is a sentence with 7 tokens After strtok, sentence = This CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 76 5.12.2 Các hàm xử lý xâu ký tự • Xác định độ dài xâu – size_t strlen( const char *s ) • Trả về số ký tự của xâu – Không tính đến ký tự null © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 5. https://fb.com/tailieudientucntt 1 2 3 77 // Fig. 5.32: fig05_32.cpp // Using strlen. #include <iostream> 4 5 6 using std::cout; using std::endl; 7 8 #include <cstring> 9 10 11 12 13 14 int main() { char *string1 = "abcdefghijklmnopqrstuvwxyz"; char *string2 = "four"; char *string3 = "Boston"; 15 16 17 18 19 20 21 cout << << << << << << 22 23 return 0; 24 25 fig05_32.cpp (1 of 1) <cstring> chứa prototype cho strlen. // prototype for strlen "The length of \"" << string1 "\" is " << strlen( string1 ) "\nThe length of \"" << string2 "\" is " << strlen( string2 ) "\nThe length of \"" << string3 "\" is " << strlen( string3 ) << endl; Sử dụng strlen để xác định độ dài xâu. // indicates successful The termination length of "abcdefghijklmnopqrstuvwxyz" is 26 The length of "four" is 4 The length of "Boston" is 6 } // end main CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 6 – Cấu trúc dữ liệu trừu tượng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 2 Chương 6: Cấu trúc dữ liệu trừu tượng Đề mục 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 6.11 6.12 6.13 6.14 6.15 Giới thiệu Cấu trúc - struct Truy nhập các thành viên của struct Cài đặt kiểu dữ liệu người dùng Time bằng struct Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class Phạm vi lớp và truy nhập các thành viên của lớp Tách giao diện ra khỏi cài đặt Quản lý quyền truy nhập thành viên Các hàm truy nhập và các hàm tiện ích Khởi tạo các đối tượng: Constructor Sử dụng các đối số mặc định cho Constructor Destructor - hàm hủy Khi nào Constructor và Destructor được gọi Sử dụng các hàm Set và Get Phép gán đối tượng mặc định © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 3 Tài liệu đọc thêm • Day 6. TY21 (lập trình cơ bản) • Chap 4,5. Introduction to OOP Using C++ (IOOP) (khái niệm hướng đối tượng) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 4 6.1 Giới thiệu • các kiểu dữ liệu phức hợp cấu tạo từ các thành phần thuộc các kiểu dữ liệu khác – tạo kiểu dữ liệu mới - kiểu dữ liệu người dùng tự định nghĩa (user-defined data type) • bản ghi – gồm nhiều trường, mỗi trường lưu trữ một thành viên dữ liệu thuộc một kiểu dữ liệu cài sẵn hoặc một kiểu dữ liệu người dùng khác. • ví dụ – Thời gian(giờ, phút, giây) – Họ tên (họ, đệm, tên) 17:10:02, 04:23:12,... (Nguyễn, Văn, An), (Lê, Thị, Bình),... © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 5 6.1 Giới thiệu • C++: – struct và class - kiểu bản ghi – đối tượng (một thể hiện của một kiểu struct hay class nào đó) - bản ghi – thành viên dữ liệu - trường – hàm thành viên/phương thức - thao tác trên các thành viên dữ liệu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6 6.2 Cấu trúc - struct • struct definition struct Time { int hour; int minute; int second; }; Structure tag Structure members • quy tắc đặt tên cho các thành viên của cấu trúc – trong cùng struct: không thể trùng tên – trong các struct khác nhau: có thể trùng tên • định nghĩa struct phải kết thúc bằng dấu chấm phảy. – Các biến kiểu cấu trúc được khai báo như các biến thuộc các loại khác – Ví dụ: khai báo biến đơn, mảng, con trỏ, tham chiếu... • • • • Time Time Time Time timeObject; timeArray[ 10 ]; *timePtr; &timeRef = timeObject; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 7 6.2 Cấu trúc - struct • Self-referential structure - cấu trúc đệ quy – thành viên của một cấu trúc không thể thuộc kiểu cấu trúc đó – thành viên của một cấu trúc có thể là con trỏ đến kiểu cấu trúc đó (self-referential structure - cấu trúc đệ quy) • sử dụng cho danh sách liên kết (linked list), hàng đợi (queue), ngăn xếp (stack), và cây (tree) struct Node { int data; Node* next; }; © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 8 6.3 Truy nhập các thành viên của struct • các toán tử truy nhập thành viên (member access operator) – Toán tử dấu chấm (.) truy nhập trực tiếp đến các thành viên của cấu trúc/lớp – Toán tử mũi tên (->) truy nhập các thành viên qua con trỏ đến đối tượng – Ví dụ: in thành viên hour của đối tượng timeObject: cout << timeObject.hour; hoặc timePtr = &timeObject; cout << timePtr->hour; – timePtr->hour tương đương ( *timePtr ).hour • Cần có cặp ngoặc do * không được ưu tiên bằng . © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 fig06_01.cpp (1 of 3) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 11 using std::setfill; using std::setw; 12 13 14 15 16 17 18 19 20 21 22 23 9 // Fig. 6.1: fig06_01.cpp // Create a structure, set its members, and print it. #include <iostream> Định nghĩa kiểu cấu trúc Time với 3 thành viên là số nguyên. // structure definition struct Time { int hour; // 0-23 (24-hour clock format) int minute; // 0-59 int second; // 0-59 Truyền tham chiếu tới hằng Time để tránh sao chép tham số. }; // end struct Time void printUniversal( const Time & ); void printStandard( const Time & ); // prototype // prototype ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 int main() { Time dinnerTime; 10 dụng ký hiệu dấu chấm để // variable ofSử new type Time khởi tạo các thành viên cấu trúc.fig06_01.cpp (2 of 3) // set hour member of dinnerTime 27 28 29 30 dinnerTime.hour = 18; dinnerTime.minute = 30; dinnerTime.second = 0; 31 32 33 34 35 36 cout << "Dinner will be held at "; printUniversal( dinnerTime ); cout << " universal time,\nwhich is "; printStandard( dinnerTime ); cout << " standard time.\n"; 37 38 39 40 41 42 43 44 45 46 47 48 dinnerTime.hour = 29; dinnerTime.minute = 73; // set minute member of dinnerTime // set second member of dinnerTime Quyền truy nhập trực tiếp tới dữ liệu cho phép gán các giá trị không hợp lệ. // set hour to invalid value // set minute to invalid value cout << "\nTime with invalid values: "; printUniversal( dinnerTime ); cout << endl; return 0; } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 49 50 51 52 53 54 // print time in universal-time format void printUniversal( const Time &t ) { cout << setfill( '0' ) << setw( 2 ) << t.hour << ":" << setw( 2 ) << t.minute << ":" << setw( 2 ) << t.second; 11 fig06_01.cpp (3 of 3) fig06_01.cpp Sử dụng manipulator setfill. output (1 of 1) 55 56 } // end function printUniversal 57 58 59 60 61 62 63 64 65 // print time in standard-time format Dùng dấu chấm để truy nhập void printStandard( const Time &t ) các thành viên dữ liệu. { cout << ( ( t.hour == 0 || t.hour == 12 ) ? 12 : t.hour % 12 ) << ":" << setfill( '0' ) << setw( 2 ) << t.minute << ":" << setw( 2 ) << t.second << ( t.hour < 12 ? " AM" : " PM" ); 66 67 } // end function printStandard Dinner will be held at 18:30:00 universal time, which is 6:30:00 PM standard time. Time with invalid values: 29:73:00 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 6.4 Cài đặt kiểu dữ liệu người dùng Time bằng struct 12 • Truyền tham số: – Mặc định struct được truyền bằng giá trị – Nên truyền struct bằng tham chiếu để tránh được việc phải sao chép cấu trúc © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 13 6.4 Cài đặt kiểu dữ liệu người dùng Time bằng struct • struct kiểu C – không có giao diện giữa bên trong và bên ngoài cấu trúc • Nếu cài đặt thay đổi, mọi chương trình sử dụng struct đó phải được sửa đổi theo – không thể in ra như là một biến đơn • Phải in/định dạng cho từng thành viên – không thể so sánh hai struct theo kiểu thông thường • Phải so sánh từng thành viên • struct kiểu C++ – C++ mở rộng: struct có chức năng như class – thông lệ: struct chỉ được dùng cho các cấu trúc chỉ gồm dữ liệu; class dùng cho các lớp có cả dữ liệu và hàm thành viên. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class 14 • Các lớp - Classes – mô hình các đối tượng • Thuộc tính - Attributes (data members) • Hành vi - Behaviors (member functions) – từ khoá class – các hàm thành viên – member functions • còn được gọi là các phương thức - method • được gọi để trả lời các thông điệp © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt Class definition bắt đầu bằng từ khoá class. 15 Class body bắt đầu bằng ngoặc mở. Class Time definition (1 of 1) Function prototype cho các public member function. 1 class Time { 2 3 4 5 6 7 public: Time(); // constructor void setTime( int, int, int ); // set hour, minute, second void printUniversal(); // print universal-time format void printStandard(); // print standard-time format 8 9 10 11 12 private: int hour; int minute; int second; 13 14 }; // end class Time Constructor: thành viên trùng tên với tên class, Time, và không có giá trị trả về. Nhãn quyền truy nhập // 0 - 23 (24-hour clock format) // 0 - 59 // 0 - 59 Class body kết thúc bằng ngoặc đóng. CuuDuongThanCong.com private data member chỉ có thể được truy nhập từ các member function. Definition kết thúc bằng dấu chấm phảy. ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class • Nhãn quyền truy nhập – Member access specifiers 16 – quy định quyền truy nhập các thành viên của lớp từ các đoạn trình bên ngoài định nghĩa lớp – public: • thành viên có thể được truy nhập từ trong toàn bộ phạm vi của đối tượng – private: • thành viên chỉ có thể được truy nhập từ các hàm thành viên của chính lớp đó – protected: • dùng cho quan hệ thừa kế © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class • Constructor – phương thức khởi tạo 17 – hàm thành viên đặc biệt • khởi tạo các thành viên dữ liệu • trùng tên với tên lớp – được gọi khi đối tượng được tạo, ví dụ khi biến được khai báo – có thể có vài constructor • hoạt động theo nguyên tắc hàm gọi chồng – không có giá trị trả về và không có kiểu giá trị trả về class Time { public: Time(); … }; … Time::Time() { hour = minute = second = 0; } // end Time constructor © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class • Destructor – phương thức hủy 18 – trùng tên với tên lớp • bắt đầu bằng dấu (~) – không có tham số – tối đa 1 destructor, không thể bị gọi chồng – dành cho việc dọn dẹp, chẳng hạn bộ nhớ class Time { public: Time(); ~Time(); … }; … Time::~Time() { //empty } // end Time destructor © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class 19 • các đối tượng của một lớp – Kể từ sau class definition • tên lớp trở thành tên kiểu mới - type specifier – C++ là ngôn ngữ mở rộng được • có thể khai báo đối tượng, mảng đối tượng, con trỏ và tham chiếu tới đối tượng – Ví dụ: Tên lớp trở thành tên kiểu dữ liệu mới. Time Time Time Time sunset; arrayOfTimes[ 5 ]; *pointerToTime; &dinnerTime = sunset; // // // // object of type Time array of Time objects pointer to a Time object reference to a Time object © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class 20 • Các hàm thành viên được định nghĩa bên ngoài lớp – toán tử phạm vi (::) • gắn tên thành viên với tên lớp • xác định duy nhất các hàm của một lớp nào đó • các lớp khác nhau có thể có các hàm thành viên trùng tên – Công thức định nghĩa hàm thành viên ReturnType ClassName::MemberFunctionName( ) { … } – như nhau đối với hàm public hay private © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class 21 • Các hàm thành viên được định nghĩa bên trong lớp – Không cần toán tử phạm vi (::) và tên lớp – Trình biên dịch sẽ chuyển thành hàm inline nếu có thể • Bên ngoài lớp, các hàm inline cần từ khoá inline © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 fig06_03.cpp (1 of 5) 4 5 6 using std::cout; using std::endl; 7 8 #include <iomanip> 9 10 11 using std::setfill; using std::setw; 12 13 14 15 16 17 18 19 20 21 22 // Fig. 6.3: fig06_03.cpp // Time class. #include <iostream> // Time abstract data type (ADT) definition class Time { Định nghĩa lớp Time. public: Time(); // constructor void setTime( int, int, int ); // set hour, minute, second void printUniversal(); // print universal-time format void printStandard(); // print standard-time format ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private: int hour; int minute; int second; 23 // 0 - 23 (24-hour clock format) // 0 - 59 // 0 - 59 fig06_03.cpp (2 of 5) }; // end class Time // Time constructor initializes each data member to zero and // ensures all Time objects start in a consistent state Time::Time() Constructor khởi tạo các thành { viên dữ liệu private về 0. hour = minute = second = 0; } // end Time constructor // set new Time value using universal time, perform validity // checks on the data values and set invalid values to zero void Time::setTime( int h, int m, int s ) { hour = ( h >= 0 && h < 24 ) ? h : 0; minute = ( m >= 0 && m < 60 ) ? m : 0; second = ( s >= 0 && s < 60 ) ? s : 0; Hàm thành viên public kiểm tra tính hợp lệ của giá trị các đối số trước khi gán trị cho các thành viên dữ liệu private } // end function setTime 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 47 48 49 50 51 52 // print Time in universal format void Time::printUniversal() { cout << setfill( '0' ) << setw( 2 ) << hour << ":" << setw( 2 ) << minute << ":" << setw( 2 ) << second; 53 54 } // end function printUniversal 55 56 57 58 59 60 61 62 // print Time in standard format void Time::printStandard() { cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) << ":" << setfill( '0' ) << setw( 2 ) << minute << ":" << setw( 2 ) << second << ( hour < 12 ? " AM" : " PM" ); 63 64 } // end function printStandard 65 66 67 68 int main() { Time t; fig06_03.cpp (3 of 5) Không có tham số (ngầm hiểu mục đích là in các thành viên dữ liệu); lời gọi hàm thành viên ngắn gọn hơn lời gọi hàm thường. Khai báo biến t là đối tượng thuộc lớp Time. // instantiate object t of class Time 69 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 70 71 72 // output Time object t's initial values cout << "The initial universal time is "; t.printUniversal(); // 00:00:00 73 74 75 fig06_03.cpp (4 of 5) để in cout << "\nThe initial standard time is "; Gọi các hàm thành viên public thời gian. t.printStandard(); // 12:00:00 AM 76 77 t.setTime( 13, 27, 6 ); 78 79 80 81 // output Time object t's new values cout << "\n\nUniversal time after setTime is "; Thử gán các giá trị không hợp lệ cho các thành viên t.printUniversal(); // 13:27:06 // change time Dùng hàm thành viên public để gán trị cho các thành viên dữ liệu. dữ liệu bằng cách sử dụng hàm thành viên public 82 83 84 cout << "\nStandard time after setTime is "; t.printStandard(); // 1:27:06 PM 85 86 t.setTime( 99, 99, 99 ); 87 88 89 90 91 // output t's values after specifying invalid values cout << "\n\nAfter attempting invalid settings:" << "\nUniversal time: "; t.printUniversal(); // 00:00:00 // attempt invalid settings 92 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 93 94 95 96 97 98 99 26 cout << "\nStandard time: "; t.printStandard(); // 12:00:00 AM cout << endl; fig06_03.cpp (5 of 5) return 0; fig06_03.cpp output (1 of 1) } // end main The initial universal time is 00:00:00 The initial standard time is 12:00:00 AM Universal time after setTime is 13:27:06 Standard time after setTime is 1:27:06 PM After attempting invalid settings: Universal time: 00:00:00 Standard time: 12:00:00 AM Các thành viên dữ liệu được gán về 0 sau khi thử các giá trị không hợp lệ. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 6.5 Cài đặt một kiểu dữ liệu trừu tượng Time bằng một lớp - class 27 • lợi ích khi dùng lớp – đơn giản hóa việc lập trình – các giao diện – Interfaces • che dấu phần cài đặt – Hide implementation – tái sử dụng phần mềm – Software reuse • khả năng tích hợp – Composition (aggregation) – các thành viên của một lớp có thể là đối tượng thuộc lớp khác • thừa kế - Inheritance – các lớp mới được tạo từ lớp cũ © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 28 6.6 Phạm vi lớp và truy nhập các thành viên của lớp • phạm vi lớp – Class scope – gồm thành viên dữ liệu và hàm thành viên của lớp – bên trong phạm vi lớp • Các thành viên của lớp – có thể được truy nhập thẳng từ mọi hàm thành viên – gọi bằng tên – bên ngoài phạm vi lớp • được gọi đến bằng tên đối tượng, tham chiếu/con trỏ tới đối tượng – objectTime.printStandard() © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 29 6.6 Phạm vi lớp và truy nhập các thành viên của lớp • Phạm vi file - File scope – áp dụng cho các hàm không phải thành viên • Phạm vi hàm – Function scope – – – – Gồm các biến được khai báo trong hàm thành viên chỉ được biết đến trong hàm đó bị hủy khi hàm kết thúc các biến trùng tên với biến thuộc phạm vi lớp • biến thuộc phạm vi lớp (class-scope variable) bị che (“hidden”) – truy nhập bằng toán tử phạm vi (::) ClassName::classVariableName © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 30 6.6 Phạm vi lớp và truy nhập các thành viên của lớp • Các toán tử để truy nhập các thành viên của đối tượng – giống các toán tử dành cho struct – toán tử (.) dùng cho • đối tượng • tham chiếu đến đối tượng – toán tử (->) dùng cho • các con trỏ tới đối tượng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 4 5 // Fig. 6.4: fig06_04.cpp // Demonstrating the class member access operators . and -> // // CAUTION: IN FUTURE EXAMPLES WE AVOID PUBLIC DATA! #include <iostream> 6 7 8 using std::cout; using std::endl; 9 10 11 // class Count definition class Count { 12 13 14 public: int x; 15 16 17 18 19 20 21 31 fig06_04.cpp (1 of 2) Thành viên dữ liệu public x minh họa các toán tử truy nhập; thông thường các thành viên dữ liệu đều là private. void print() { cout << x << endl; } }; // end class Count 22 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 int main() { Count counter; Count *counterPtr = &counter; Count &counterRef = counter; 32 // create counter object Sử dụng dấupointer chấm cho to đối counter tượng // create counter. // create reference to counter fig06_04.cpp (2 of 2) 28 29 30 31 fig06_04.cpp cout << "Assign 1 to x and print using the object's name: "; output (1 of 1) counter.x = 1; // assign 1 to data member x Sử dụng dấu chấm cho counterRef là counter.print(); // call member function print 32 33 34 35 cout << "Assign 2 to x and print using a reference: "; counterRef.x = 2; // assignSử2 dụng to data member x mũi tên cho counterPtr counterRef.print(); // call member print là con trỏfunction tới đối tượng. 36 37 38 39 cout << "Assign 3 to x and print using a pointer: "; counterPtr->x = 3; // assign 3 to data member x counterPtr->print(); // call member function print 40 41 return 0; 42 43 tham chiếu đến đối tượng. } // end main Assign 1 to x and print using the object's name: 1 Assign 2 to x and print using a reference: 2 Assign 3 to x and print using a pointer: 3 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 33 6.7 Tách giao diện ra khỏi cài đặt • Tách giao diện khỏi cài đặt – ích lợi • dễ sửa đổi chương trình – bất lợi • phải tạo các file header gồm – một phần của cài đặt • Inline member functions – các hàm inline – gợi ý về phần khác của cài đặt • private members © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 34 6.7 Tách giao diện ra khỏi cài đặt • Các file header – chứa các định nghĩa lớp và các nguyên mẫu hàm – được include trong mỗi file sử dụng lớp đó • #include – mở rộng của file .h • Các file mã nguồn – Source-code files – chứa định nghĩa của các hàm thành viên – trùng tên file với file header tương ứng (không kể phần mở rộng) • đây chỉ là thông lệ, không bắt buộc – được biên dịch và liên kết với file chương trình chính © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 35 Mã tiền xử lý để tránh việc file bị // Fig. 6.5: time1.h // Declaration of class Time. include nhiều lần. // Member functions are defined in time1.cpp time1.h (1 of 1) “If not defined” 4 5 6 7 // prevent multiple inclusions of header file #ifndef TIME1_H #define TIME1_H 8 9 10 // Time abstract data type definition include nến tên TIME1_H đã được định nghĩa. class Time { 11 12 13 14 15 16 public: Time(); // constructor void setTime( int, int, int ); // set hour, minute, second void printUniversal(); // print universal-time format void printStandard(); // print standard-time format 17 18 19 20 21 private: int hour; int minute; int second; 22 23 }; // end class Time 24 25 #endif Mã giữa hai định hướng này không được Định hướng tiền xử lý định nghĩa tên TIME1_H. // 0 - 23 (24-hour clock format) // 0 - 59 // 0 - 59 Thông lệ đặt tên: tên header file với dấu gạch dưới thay cho dấu chấm. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 36 // Fig. 6.6: time1.cpp // Member-function definitions for class Time. #include <iostream> time1.cpp (1 of 3) 4 5 using std::cout; 6 7 #include <iomanip> 8 9 10 using std::setfill; using std::setw; 11 12 13 // include definition of class Time from time1.h #include "time1.h" 14 15 16 17 18 19 // Time constructor initializes each data member to zero. // Ensures all Time objects start in a consistent state. Time::Time() Tên của header file đặt trong ngoặc kép; { cặp ngoặc nhọn làm trình biên dịch cho hour = minute = second = 0; 20 21 } // end Time constructor Include header file time1.h. rằng đó là một phần của thư viện chuẩn C++ (C++ Standard Library). 22 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 28 29 // Set new Time value using universal time. Perform validity // checks on the data values. Set invalid values to zero. void Time::setTime( int h, int m, int s ) { hour = ( h >= 0 && h < 24 ) ? h : 0; minute = ( m >= 0 && m < 60 ) ? m : 0; second = ( s >= 0 && s < 60 ) ? s : 0; 30 31 } // end function setTime 32 33 34 35 36 37 38 // print Time in universal format void Time::printUniversal() { cout << setfill( '0' ) << setw( 2 ) << hour << ":" << setw( 2 ) << minute << ":" << setw( 2 ) << second; 39 40 } // end function printUniversal 37 time1.cpp (2 of 3) 41 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 42 43 44 45 46 47 48 // print Time in standard format void Time::printStandard() { cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) << ":" << setfill( '0' ) << setw( 2 ) << minute << ":" << setw( 2 ) << second << ( hour < 12 ? " AM" : " PM" ); 49 50 } // end function printStandard 38 time1.cpp (3 of 3) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 6.7: fig06_07.cpp // Program to test class Time. // NOTE: This file must be compiled with time1.cpp. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 11 12 13 14 39 fig06_07.cpp (1 of 2) Include time1.h để đảm bảo tạo đúng và để tính kích thước đối tượng thuộc lớp Time. // include definition of class Time from time1.h #include "time1.h" int main() { Time t; // instantiate object t of class Time 15 16 17 18 19 20 // output Time object t's initial values cout << "The initial universal time is "; t.printUniversal(); // 00:00:00 cout << "\nThe initial standard time is "; t.printStandard(); // 12:00:00 AM 21 22 t.setTime( 13, 27, 6 ); // change time 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 // output Time object t's new values cout << "\n\nUniversal time after setTime is "; t.printUniversal(); // 13:27:06 cout << "\nStandard time after setTime is "; t.printStandard(); // 1:27:06 PM 29 30 t.setTime( 99, 99, 99 ); 31 32 33 34 35 36 37 38 // output t's values after specifying invalid values cout << "\n\nAfter attempting invalid settings:" << "\nUniversal time: "; t.printUniversal(); // 00:00:00 cout << "\nStandard time: "; t.printStandard(); // 12:00:00 AM cout << endl; 39 40 return 0; 41 42 40 fig06_07.cpp (2 of 2) fig06_07.cpp output (1 of 1) // attempt invalid settings The initial universal time is 00:00:00 The initial standard time is 12:00:00 AM } // end main Universal time after setTime is 13:27:06 Standard time after setTime is 1:27:06 PM ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 41 6.8 Quản lý quyền truy nhập thành viên • các kiểu truy nhập – Access – private • kiểu mặc định - Default access mode • chỉ có các hàm thành viên và các hàm friend là có thể truy nhập các thành viên private – public • truy nhập được từ mọi hàm trong chương trình. – protected • dành cho quan hệ thừa kế, hiện tại chưa nói đến © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 6.8: fig06_08.cpp // Demonstrate errors resulting from attempts // to access private class members. #include <iostream> 5 6 using std::cout; 7 8 9 // include definition of class Time from time1.h #include "time1.h" 10 11 12 13 int main() { Time t; // create Time object fig06_08.cpp (1 of 1) hour là thành viên private; truy nhập các thành viên private sẽ gây lỗi. 14 15 16 t.hour = 7; 17 18 19 // error: 'Time::minute' is not accessible cout << "minute = " << t.minute; 20 21 return 0; 22 23 42 // error: 'Time::hour' is not accessible minute cũng là private; } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 43 6.8 Quản lý quyền truy nhập thành viên • quyền truy nhập các thành viên của class – mặc định private – phải đặt tường minh public, protected • quyền truy nhập các thành viên của struct – mặc định public – phải đặt tường minh private, protected • truy nhập dữ liệu private của lớp – các hàm truy nhập (accessor method) • Get function – hàm đọc dữ liệu – đọc dữ liệu private • Set function – hàm ghi dữ liệu – ghi dữ liệu private © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 44 6.9 Các hàm truy nhập và các hàm tiện ích • Các hàm truy nhập – Access functions – public – các hàm đọc và hiển thị dữ liệu – các hàm ghi dữ liệu (kèm kiểm tra tính hợp lệ) – các hàm mệnh đề – Predicate functions • kiểm tra các điều kiện • Các hàm tiện ích – Utility functions – private – chỉ hỗ trợ hoạt động của các hàm thành viên kiểu public – không nhằm mục đích để cho client trực tiếp sử dụng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 4 5 // Fig. 6.9: salesp.h // SalesPerson class definition. // Member functions defined in salesp.cpp. #ifndef SALESP_H #define SALESP_H 6 7 class SalesPerson { 8 9 10 11 12 13 14 15 16 17 public: SalesPerson(); // void getSalesFromUser(); // void setSales( int, double ); // void printAnnualSales(); // private: double totalAnnualSales(); double sales[ 12 ]; 18 19 }; // end class SalesPerson 20 21 #endif 45 salesp.h (1 of 1) hàm ghi dữ liệu thực hiện việc kiểm tra tính hợp lệ của dữ liệu (validity checks). constructor input sales from keyboard set sales for a month summarize and print sales hàm tiện ích private // utility function // 12 monthly sales figures ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 46 // Fig. 6.10: salesp.cpp // Member functions for class SalesPerson. #include <iostream> salesp.cpp (1 of 3) 4 5 6 7 8 using using using using 9 10 #include <iomanip> 11 12 using std::setprecision; 13 14 15 // include SalesPerson class definition from salesp.h #include "salesp.h" 16 17 18 19 20 21 // initialize elements of array sales to 0.0 SalesPerson::SalesPerson() { for ( int i = 0; i < 12; i++ ) sales[ i ] = 0.0; 22 23 } // end SalesPerson constructor std::cout; std::cin; std::endl; std::fixed; 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 47 // get 12 sales figures from the user at the keyboard void SalesPerson::getSalesFromUser() { double salesFigure; salesp.cpp (2 of 3) 29 30 31 32 33 for ( int i = 1; i <= 12; i++ ) { cout << "Enter sales amount for month " << i << ": "; cin >> salesFigure; setSales( i, salesFigure ); 34 35 } // end for hàm ghi dữ liệu thực hiện việc kiểm tra tính hợp lệ của dữ liệu (validity checks). 36 37 } // end function getSalesFromUser 38 39 40 41 42 43 44 45 // set one of the 12 monthly sales figures; function subtracts // one from month value for proper subscript in sales array void SalesPerson::setSales( int month, double amount ) { // test for valid month and amount values if ( month >= 1 && month <= 12 && amount > 0 ) sales[ month - 1 ] = amount; // adjust for subscripts 0-11 46 47 48 else // invalid month or amount value cout << "Invalid month or sales figure" << endl; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 48 49 50 } // end function setSales 51 52 53 54 55 56 57 // print total annual sales (with help of utility function) void SalesPerson::printAnnualSales() { cout << setprecision( 2 ) << fixed << "\nThe total annual sales are: $" << totalAnnualSales() << endl; // call utility function 58 59 } // end function printAnnualSales salesp.cpp (3 of 3) Hàm tiện ích private phục vụ hàm printAnnualSales; 60 61 // private utility function to total annual sales đóng gói thao tác trên mảng sales. 62 63 64 65 66 67 68 69 70 71 double SalesPerson::totalAnnualSales() { double total = 0.0; // initialize total for ( int i = 0; i < 12; i++ ) total += sales[ i ]; // summarize sales results return total; } // end function totalAnnualSales ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 49 // Fig. 6.11: fig06_11.cpp // Demonstrating a utility function. // Compile this program with salesp.cpp fig06_11.cpp (1 of 1) // include SalesPerson class definition from salesp.h #include "salesp.h" int main() { SalesPerson s; s.getSalesFromUser(); s.printAnnualSales(); Chuỗi gọi hàm đơn giản; logic chương trình được đóng gói trong các hàm thành viên. // create SalesPerson object s // note simple sequential code; no // control structures in main return 0; } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter Enter sales sales sales sales sales sales sales sales sales sales sales sales amount amount amount amount amount amount amount amount amount amount amount amount for for for for for for for for for for for for month month month month month month month month month month month month 1: 5314.76 2: 4292.38 3: 4589.83 4: 5534.03 5: 4376.34 6: 5698.45 7: 4439.22 8: 5893.57 9: 4909.67 10: 5123.45 11: 4024.97 12: 5923.92 50 fig06_11.cpp output (1 of 1) The total annual sales are: $60120.59 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 51 6.10 Khởi tạo các đối tượng: Constructor • Constructors – khởi tạo các thành viên dữ liệu • hoặc có thể gán trị cho các thành viên dữ liệu sau – trùng tên với tên lớp – không có kiểu trả về • Các giá trị khởi tạo – Initializers – được truyền dưới dạng đối số cho constructor – khi khai báo biến: đặt trong cặp ngoặc đơn trước dấu chấm phảy class Time { public: Time( int, int, int); ... }; // end class Time ... int main() { Time t( 27, 74, 99 ); ... Class-type ObjectName( value1,value2,…); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 52 6.11 Sử dụng các đối số mặc định với constructor • có thể chỉ định các đối số mặc định – tương tự đối số mặc định của hàm thông thường • constructor mặc định: – có thể gọi không cần tham số • Time t; – Tất cả các đối số là mặc định HOẶC thực sự không nhận tham số • Time(int = 0, int = 0, int = 0); hoặc • Time(); – mỗi lớp chỉ được có tối đa một constructor mặc định © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 53 // Fig. 6.12: time2.h // Declaration of class Time. // Member functions defined in time2.cpp. time2.h (1 of 1) 4 5 6 7 // prevent multiple inclusions of header file #ifndef TIME2_H #define TIME2_H 8 9 10 // Time abstract data type definition class Time { 11 12 13 14 15 16 public: Time( int = 0, int = 0, int = 0); // default constructor void setTime( int, int, int ); // set hour, minute, second void printUniversal(); // print universal-time format void printStandard(); // print standard-time format 17 18 19 20 21 private: int hour; int minute; int second; 22 23 }; // end class Time 24 25 #endif Default constructor chỉ định giá trị mặc định cho mọi đối số. // 0 - 23 (24-hour clock format) // 0 - 59 // 0 - 59 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 time2.cpp (1 of 2) 4 5 using std::cout; 6 7 #include <iomanip> 8 9 10 using std::setfill; using std::setw; 11 12 13 // include definition of class Time from time2.h #include "time2.h" 14 15 16 17 18 19 20 21 54 // Fig. 6.13: time2.cpp // Member-function definitions for class Time. #include <iostream> // Time constructor initializes each data member to zero; // ensures all Time objects start in a consistent state Constructor gọi setTime để kiểm tra các Time::Time( int hr, int min, int sec ) giá trị được truyền vào (hoặc mặc định). { setTime( hr, min, sec ); // validate and set time } // end Time constructor 22 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 28 29 // set new Time value using universal time, perform validity // checks on the data values and set invalid values to zero void Time::setTime( int h, int m, int s ) { hour = ( h >= 0 && h < 24 ) ? h : 0; minute = ( m >= 0 && m < 60 ) ? m : 0; second = ( s >= 0 && s < 60 ) ? s : 0; 30 31 } // end function setTime 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 55 time2.cpp (2 of 2) // print Time in universal format void Time::printUniversal() { cout << setfill( '0' ) << setw( 2 ) << hour << ":" << setw( 2 ) << minute << ":" << setw( 2 ) << second; } // end function printUniversal // print Time in standard format void Time::printStandard() { cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) << ":" << setfill( '0' ) << setw( 2 ) << minute << ":" << setw( 2 ) << second << ( hour < 12 ? " AM" : " PM" ); } // end function printStandard CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 1 2 3 fig06_14.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 9 // include definition of class Time from time2.h #include "time2.h" 10 11 12 13 14 15 16 17 int main() { Time t1; Time t2( Time t3( Time t4( Time t5( 18 19 20 21 22 23 56 // Fig. 6.14: fig06_14.cpp // Demonstrating a default constructor for class Time. #include <iostream> Khởi tạo các đối tượng Time sử dụng các tham số mặc định. // 2 ); // 21, 34 ); // 12, 25, 42 ); // 27, 74, 99 ); // all arguments defaulted minute and second defaulted second defaulted all values specified all bad values specified cout << "Constructed with:\n\n" << "all default arguments:\n "; t1.printUniversal(); // 00:00:00 cout << "\n "; t1.printStandard(); // 12:00:00 AM Khởi tạo đối tượng Time với các giá trị không hợp lệ; khâu kiểm tra tính hợp lệ sẽ gán các giá trị về 0. 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 cout << "\n\nhour specified; default minute and second:\n t2.printUniversal(); // 02:00:00 cout << "\n "; t2.printStandard(); // 2:00:00 AM "; 29 30 31 32 33 cout << "\n\nhour and minute specified; default second:\n t3.printUniversal(); // 21:34:00 cout << "\n "; t3.printStandard(); // 9:34:00 PM "; 34 35 36 37 38 cout << "\n\nhour, minute, and second specified:\n t4.printUniversal(); // 12:25:42 cout << "\n "; t4.printStandard(); // 12:25:42 PM 39 40 41 42 43 44 cout << "\n\nall invalid values specified:\n t5.printUniversal(); // 00:00:00 cout << "\n "; t5.printStandard(); // 12:00:00 AM cout << endl; 45 46 return 0; 47 48 57 fig06_14.cpp (2 of 2) "; "; t5 được xây dựng bằng các đối số không hợp lệ, các giá trị được gán về 0. } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 58 6.12 Destructor – hàm hủy • Destructor – hàm thành viên tự hủy của đối tượng – hàm thành viên đặc biệt – trùng tên với tên lớp • bắt đầu bằng dấu ngã (~) – – – – không nhận đối số không có giá trị trả về không thể bị gọi chồng thực hiện việc dọn dẹp • trước khi hệ thống lấy lại phần bộ nhớ của đối tượng – tái sử dụng cho đối tượng mới – nếu không có destructor được định nghĩa tường minh • trình biên dịch tự tạo destructor "rỗng" – không làm gì hết © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 59 6.13 Khi nào Constructor và Destructor được gọi • các constructor và destructor – được gọi ngầm bởi trình biên dịch • thứ tự gọi hàm – phụ thuộc vào thứ tự thực thi chương trình • khi chương trình vào và ra khỏi phạm vi của các đối tượng – các đối tượng cũng là các biến thông thường, • biến được khởi tạo – constructor được gọi – tại thời điểm bắt đầu tồn tại / phạm vi • biến bị hủy – destructor được gọi – khi kết thúc sự tồn tại / ra khỏi phạm vi – thông thường, các lời gọi destructor theo thứ tự ngược lại với thứ tự gọi các constructor © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 60 6.13 Khi nào Constructor và Destructor được gọi • Thứ tự các lời gọi constructor, destructor – đối với các đối tượng/biến phạm vi toàn cục (global scope objects) • Constructor – được gọi trước mọi hàm khác (kể cả main) • Destructor – được gọi khi main kết thúc (hoặc khi hàm exit được gọi) – không được gọi nếu chương trình kết thúc bằng hàm abort © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 61 6.13 Khi nào Constructor và Destructor được gọi • Thứ tự các lời gọi constructor, destructor – đối với các đối tượng/biến địa phương (automatic local objects) • Constructor – được gọi khi đối tượng được định nghĩa • mỗi khi chương trình vào phạm vi của đối tượng • Destructor – được gọi khi đối tượng ra khỏi phạm vi • chương trình ra khỏi khối nơi đối tượng được định nghĩa – không được gọi nếu chương trình kết thúc bằng exit hay abort © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 62 6.13 Khi nào Constructor và Destructor được gọi • Thứ tự các lời gọi constructor, destructor – các đối tượng tĩnh địa phương (static local objects) • Constructor – đúng một lần – khi chương trình chạy đến chỗ đối tượng được định nghĩa • Destructor – khi hàm main kết thúc hoặc khi hàm exit được gọi – không được gọi nếu chương trình kết thúc bằng hàm abort © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 4 5 // Fig. 6.15: create.h // Definition of class CreateAndDestroy. // Member functions defined in create.cpp. #ifndef CREATE_H #define CREATE_H 6 7 class CreateAndDestroy { 8 9 10 11 public: CreateAndDestroy( int, char * ); ~CreateAndDestroy(); 12 13 14 15 private: int objectID; char *message; 16 17 }; // end class CreateAndDestroy 18 19 #endif 63 create.h (1 of 1) Các hàm thành viên constructor và destructor // constructor // destructor Các thành viên private để minh họa thứ tự các lời gọi constructor và destructor ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 64 // Fig. 6.16: create.cpp // Member-function definitions for class CreateAndDestroy #include <iostream> create.cpp (1 of 2) 4 5 6 using std::cout; using std::endl; 7 8 9 // include CreateAndDestroy class definition from create.h #include "create.h" 10 11 12 13 14 15 16 17 18 19 20 21 // constructor CreateAndDestroy::CreateAndDestroy( int objectNumber, char *messagePtr ) { objectID = objectNumber; message = messagePtr; cout << "Object " << objectID << " << message << endl; Output message để thể hiện thời gian của các lời gọi hàm constructor. constructor runs " } // end CreateAndDestroy constructor 22 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 28 29 30 31 32 65 Output message để thể hiện thời gian // destructor của các lời gọi hàm destructor CreateAndDestroy::~CreateAndDestroy() { create.cpp (2 of 2) // the following line is for pedagogic purposes only cout << ( objectID == 1 || objectID == 6 ? "\n" : "" ); cout << "Object " << objectID << " << message << endl; destructor runs " } // end ~CreateAndDestroy destructor ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 66 1 2 3 4 // Fig. 6.17: fig06_17.cpp // Demonstrating the order in which constructors and // destructors are called. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 // include CreateAndDestroy class definition from create.h #include "create.h" 11 12 void create( void ); 13 14 15 // global object CreateAndDestroy first( 1, "(global before main)" ); 16 17 18 19 int main() Tạo đối tượng tự động địa phương. { cout << "\nMAIN FUNCTION: EXECUTION BEGINS" << endl; 20 21 22 23 24 25 fig06_17.cpp (1 of 2) tạo đối tượng có phạm vi toàn cục // prototype Tạo đối tượng địa phương static. CreateAndDestroy second( 2, "(local automatic in main)" ); static CreateAndDestroy third( 3, "(local static in main)" ); Tạo các đối tượng tự động địa phương. create(); // call function to create objects 26 CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 28 29 30 31 32 33 34 67 cout << "\nMAIN FUNCTION: EXECUTION RESUMES" << endl; CreateAndDestroy fourth( 4, "(local automatic in main)" ); fig06_17.cpp (2 of 2) cout << "\nMAIN FUNCTION: EXECUTION ENDS" << endl; Tạo đối tượng tự động địa phương. return 0; 35 36 } // end main 37 38 39 40 41 // function to create objects void create( void ) Tạo đối tượng tự động địa phương bên trong hàm. { cout << "\nCREATE FUNCTION: EXECUTION BEGINS" << endl; Tạo đối tượng địa phương static bên trong hàm. 42 43 CreateAndDestroy fifth( 5, "(local automatic in create)" ); 44 45 46 static CreateAndDestroy sixth( 6, "(local static in create)" ); 47 48 49 CreateAndDestroy seventh( 7, "(local automatic in create)" ); 50 51 cout << "\nCREATE FUNCTION: EXECUTION ENDS\" << endl; 52 53 } // end function create CuuDuongThanCong.com Tạo đối tượng tự động địa phương bên trong hàm. ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt Object 1 constructor runs MAIN FUNCTION: EXECUTION BEGINS Object 2 constructor runs (local automatic in main) Object 3 constructor runs (local static in main) CREATE Object Object Object FUNCTION: EXECUTION BEGINS 5 constructor runs (local automatic in create) 6 constructor runs (local static in create) 7 constructor runs (local automatic in create) CREATE FUNCTION: EXECUTION ENDS Object 7 destructor runs (local automatic in create) Object 5 destructor runs (local automatic in create) MAIN FUNCTION: EXECUTION RESUMES Object 4 constructor runs (local automatic in main) MAIN FUNCTION: EXECUTION ENDS Object 4 destructor runs Object 2 destructor runs Object 6 destructor runs Object 3 destructor runs (local (local (local (local Object 1 (global before main) destructor runs 68 (global before main) automatic automatic static in static in fig06_17.cpp output (1 of 1) đối static địađối các tượng destructor cho các đối tượng toàn cụcphương được tạo phương tại địa cho đến khi tượng tựtồn động trước khi main bắt đầu các đối tượng tự động địavà bị chương trình kết thúc trong hàm main được đối tượng static địa gọi phương bị hủy sauvới khicác hàm hủy cuối cùng. theo thứđược tự ngược phương tạo tại lời gọivới kết thúc theo thứ tự ngược constructor. hàm thứ tựđầu tạo.tiên và hủy sau khi hàm main kết thúc. in main) in main) create) main) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 69 6.14 Sử dụng các hàm truy nhập • Set functions – các hàm ghi – kiểm tra tính hợp lệ trước khi sửa đổi dữ liệu private – thông báo nếu các giá trị là không hợp lệ – thông báo qua các giá trị trả về • Get functions – các hàm đọc – các hàm truy vấn – “Query” functions – quản lý định dạng của dữ liệu trả về © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 1 2 3 70 // Fig. 6.18: time3.h // Declaration of class Time. // Member functions defined in time3.cpp time3.h (1 of 2) 4 5 6 7 // prevent multiple inclusions of header file #ifndef TIME3_H #define TIME3_H 8 9 class Time { 10 11 12 public: Time( int = 0, int = 0, int = 0 ); // default constructor 13 14 15 16 17 18 Các hàm ghi // set functions void setTime( int, int, int ); // set hour, minute, second void setHour( int ); // set hour void setMinute( int ); // set minute void setSecond( int ); // set second 19 20 21 22 23 // get functions int getHour(); int getMinute(); int getSecond(); các hàm đọc // return hour // return minute // return second 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 71 void printUniversal(); // output universal-time format void printStandard(); // output standard-time format 27 28 29 30 31 private: int hour; int minute; int second; 32 33 }; // end clas Time 34 35 #endif time3.h (2 of 2) // 0 - 23 (24-hour clock format) // 0 - 59 // 0 - 59 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 72 // Fig. 6.19: time3.cpp // Member-function definitions for Time class. #include <iostream> time3.cpp (1 of 4) 4 5 using std::cout; 6 7 #include <iomanip> 8 9 10 using std::setfill; using std::setw; 11 12 13 // include definition of class Time from time3.h #include "time3.h" 14 15 16 17 18 19 20 // constructor function to initialize private data; // calls member function setTime to set variables; // default values are 0 (see class definition) Time::Time( int hr, int min, int sec ) { setTime( hr, min, sec ); 21 22 } // end Time constructor 23 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 73 // set hour, minute and second values void Time::setTime( int h, int m, int s ) { setHour( h ); setMinute( m ); setSecond( s ); } // end function setTime time3.cpp (2 of 4) Gọi các hàm set dể kiểm tra tính hợp lệ. // set hour value void Time::setHour( int h ) { hour = ( h >= 0 && h < 24 ) ? h : 0; } // end function setHour Các hàm set kiểm tra tính hợp lệ trước khi sửa đổi dữ liệu. // set minute value void Time::setMinute( int m ) { minute = ( m >= 0 && m < 60 ) ? m : 0; } // end function setMinute 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 74 Các hàm set kiểm tra tính hợp // set second value lệ trước khi sửa đổi dữ liệu. void Time::setSecond( int s ) { second = ( s >= 0 && s < 60 ) ? s : 0; time3.cpp (3 of 4) } // end function setSecond // return hour value int Time::getHour() { return hour; } // end function getHour Các hàm get cho client đọc dữ liệu // return minute value int Time::getMinute() { return minute; } // end function getMinute 67 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 68 69 70 71 72 73 75 // return second value int Time::getSecond() { return second; time3.cpp (4 of 4) Hàm get cho client đọc dữ liệu } // end function getSecond 74 75 76 77 78 79 80 // print Time in universal format void Time::printUniversal() { cout << setfill( '0' ) << setw( 2 ) << hour << ":" << setw( 2 ) << minute << ":" << setw( 2 ) << second; 81 82 } // end function printUniversal 83 84 85 86 87 88 89 90 // print Time in standard format void Time::printStandard() { cout << ( ( hour == 0 || hour == 12 ) ? 12 : hour % 12 ) << ":" << setfill( '0' ) << setw( 2 ) << minute << ":" << setw( 2 ) << second << ( hour < 12 ? " AM" : " PM" ); 91 92 } // end function printStandard CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 1 2 3 fig06_20.cpp (1 of 3) 4 5 6 using std::cout; using std::endl; 7 8 9 // include definition of class Time from time3.h #include "time3.h" 10 11 void incrementMinutes( Time &, const int ); 12 13 14 15 int main() { Time t; 16 17 18 19 20 76 // Fig. 6.20: fig06_20.cpp // Demonstrating the Time class set and get functions #include <iostream> // prototype // create Time object Gọi các hàm set để gán các giá trị hợp lệ. // set time using individual set functions t.setHour( 17 ); // set hour to valid value t.setMinute( 34 ); // set minute to valid value t.setSecond( 25 ); // set second to valid value 21 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 23 24 25 26 27 28 29 30 31 // use get functions to obtain hour, minute and second cout << "Result of setting all valid values:\n" << " Hour: " << t.getHour() fig06_20.cpp << " Minute: " << t.getMinute() (2 of 3) << " Second: " << t.getSecond(); Cố dùng các hàm set để gán các giá trị không hợp lệ. // set time using individual set functions t.setHour( 234 ); // invalid hour set to 0 t.setMinute( 43 ); // set minute to valid value t.setSecond( 6373 ); // invalid second set to 0 các giá trị không hợp lệ làm các data member bị gán về 0. 32 33 34 35 36 37 38 // display hour, minute and second after setting // invalid hour and second values cout << "\n\nResult of attempting to set invalid hour and" << " second:\n Hour: " << t.getHour() Sửa đổi data member bằng << " Minute: " << t.getMinute() << " Second: " << t.getSecond() << "\n\n"; hàm setTime. 39 40 41 t.setTime( 11, 58, 0 ); incrementMinutes( t, 3 ); 42 43 return 0; 44 45 // set time // increment t's minute by 3 } // end main 46 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 77 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 // add specified number of minutes to a Time object void incrementMinutes( Time &tt, const int count ) { fig06_20.cpp cout << "Incrementing minute " << count (3 of 3) << " times:\nStart time: "; tt.printStandard(); Dùng các hàm get để đọc và for ( int i = 0; i < count; i++ ) { tt.setMinute( ( tt.getMinute() + 1 ) % 60 ); các hàm set để sửa dữ liệu. if ( tt.getMinute() == 0 ) tt.setHour( ( tt.getHour() + 1 ) % 24); 62 63 cout << "\nminute + 1: "; tt.printStandard(); Result of setting all valid values: Hour: 17 Minute: 34 Second: 25 } // end for 64 65 cout << endl; 66 67 78 Result of attempting to set invalid hour and second: Hour: 0 Minute: 43 Second: 0 } // end function incrementMinutes Incrementing minute 3 times: Start time: 11:58:00 AM minute + 1: 11:59:00 AM minute + 1: 12:00:00 PM minute + 1: 12:01:00 PM CuuDuongThanCong.com Cố gắng gán các giá trị không hợp lệ cho các thành viên dữ liệu, kết©2004 quả là thông báo lỗi Trần Minh Châu. và các thành viên bị gán về 0. FOTECH. VNU. https://fb.com/tailieudientucntt 79 6.15 Phép gán mặc định • Gán đối tượng cho đối tượng – Phép gán (=) • có thể gán một đối tượng cho một đối tượng khác thuộc cùng kiểu • Mặc định: gán theo từng thành viên (memberwise assignment) – Mỗi thành viên của đối tượng vế phải được gán cho thành viên tương ứng tại vế trái • được ngầm thực hiện khi – truyền tham số là đối tượng – trả về đối tượng • Đối tượng có thể được truyền làm tham số cho hàm – Đối tượng có thể được hàm trả về – Mặc định: pass-by-value • Bản sao của đối tượng được truyền, trả về – sử dụng 'copy constructor' • sao chép các giá trị gốc vào đối tượng mới © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 6. https://fb.com/tailieudientucntt 80 1 2 3 4 // Fig. 6.24: fig06_24.cpp // Demonstrating that class objects can be assigned // to each other using default memberwise assignment. #include <iostream> 5 6 7 using std::cout; using std::endl; 8 9 10 // class Date definition class Date { 11 12 13 14 public: Date( int = 1, int = 1, int = 1990 ); // default constructor void print(); 15 16 17 18 19 private: int month; int day; int year; 20 21 22 fig06_24.cpp (1 of 3) }; // end class Date ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 23 24 25 26 27 28 // Date constructor with no range checking Date::Date( int m, int d, int y ) { month = m; day = d; year = y; 29 30 } // end Date constructor 31 32 33 34 35 // print Date in the format mm-dd-yyyy void Date::print() { cout << month << '-' << day << '-' << year; 36 37 } // end function print 38 39 40 41 42 int main() { Date date1( 7, 4, 2002 ); Date date2; // date2 defaults to 1/1/1990 81 fig06_24.cpp (2 of 3) 43 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 44 45 46 47 cout << "date1 = "; date1.print(); cout << "\ndate2 = "; date2.print(); 48 49 date2 = date1; 82 phép gán mặc định gán từng thành viên của date1 cho thành viên tương ứng của date2. fig06_24.cpp (3 of 3) // default memberwise assignment 50 51 52 53 cout << "\n\nAfter default memberwise assignment, date2 = "; date2.print(); cout << endl; 54 55 return 0; 56 57 fig06_24.cpp output (1 of 1) } // end main date1 = 7-4-2002 date2 = 1-1-1990 After default memberwise assignment, date2 = 7-4-2002 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 Ngôn ngữ lập trình C++ Chương 7 – Ra vào dữ liệu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 2 Chương 7 : Ra vào dữ liệu Đề mục 7.1 Giới thiệu 7.2 Dòng – Stream 7.2.2 Các file header thư viện iostream 7.2.3 Các đối tượng và các lớp I/O 7.3 Xuất theo dòng 7.3.1 Xuất các biến kiểu char*. 7.4 Nhập theo dòng 7.4.1 Các thành viên get và getline 7.4.2 Các thành viên peek, putback, và ignore 7.5 I/O không định dạng sử dụng read, write, và gcount 7.6 Giới thiệu về các stream manipulator 7.7 Các trạng thái lỗi của dòng 7.8 Đồng bộ một dòng ra và một dòng vào © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 3 Chương 7 : Ra vào dữ liệu Đề mục (tiếp) 7.9 File và dòng (stream) 7.10 File truy nhập tuần tự 7.11 Các hàm định vị cho file truy nhập tuần tự 7.12 Các rắc rối khi cập nhật file truy nhập tuần tự 7.13 File truy nhập ngẫu nhiên 7.13.1 Dữ liệu thô và dữ liệu định dạng 7.13.2 Ghi file truy nhập ngẫu nhiên 7.13.3 Ghi dữ liệu vào vị trí tùy ý trong file truy nhập ngẫu nhiên 7.13.4 Đọc tuần tự dữ liệu từ file truy nhập ngẫu nhiên 7.14 Ví dụ: Chương trình quản lý giao dịch © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 4 7.1 Giới thiệu • C++ I/O – Hướng đối tượng • sử dụng tham chiếu, chồng hàm, chồng toán tử – An toàn về các kiểu dữ liệu • nhạy cảm với kiểu dữ liệu • báo lỗi nếu kiểu không khớp – có thể dùng cho cả kiểu người dùng tự định nghĩa và các kiểu chuẩn • làm cho C++ có khả năng mở rộng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 5 7.2 Dòng - Stream • Stream – dòng: – – – – chuỗi byte, kết thúc bởi ký hiệu end_of_file Input: từ bàn phím, đĩa... vào bộ nhớ Output: từ bộ nhớ ra màn hình, máy in... file cũng được coi là một dòng • Các dòng cổ điển – vào/ra char (1 byte) – các ký tự giới hạn bảng mã ASCII • Các thư viện dòng chuẩn – Một số ngôn ngữ cần các bảng chữ cái đặc biệt – Unicode • kiểu ký tự wchar_t – Có thể thực hiện I/O với các ký tự Unicode © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 6 7.2.2 Các file header thư viện iostream • thư viện iostream – có các header file với hàng trăm chức năng vào/ra – <iostream.h> • • • • vào chuẩn – Standard input (cin) ra chuẩn – Standard output (cout) dòng báo lỗi không có bộ nhớ đệm – Unbuffered error (cerr) dòng báo lỗi có dùng bộ nhớ đệm – Buffered error (clog) – <iomanip.h> • các stream manipulator (có tham số) để định dạng I/O – <fstream.h> • các thao tác xử lý file © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 7 7.2.3 Các đối tượng và các lớp I/O • << và >> – các toán tử chèn và tách dòng • cin – đối tượng istream – nối với input chuẩn (thường là bàn phím) – cin >> grade; • trình biên dịch tự xác định kiểu của grade • gọi toán tử thích hợp (đã được định nghĩa chồng) • không cần thông tin thêm về kiểu dữ liệu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 8 7.2.3 Các đối tượng và các lớp I/O • cout – đối tượng ostream – nối với output chuẩn (thường là màn hình) – cin << grade; • cũng như với cin, không cần thêm thông tin về kiểu • cerr, clog – các đối tượng ostream – nối với thiết bị báo lỗi chuẩn – cerr xuất ngay lập tức – clog sử dụng bộ nhớ đệm trước khi xuất • xuất khi bộ nhớ đệm đầy hoặc khi được xả (flushed) • ưu điểm hiệu năng (giải thích tại môn Hệ điều hành) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 9 7.2.3 Các đối tượng và các lớp I/O • C++ xử lý file tương tự – Các kiểu đối tượng dành cho xuất nhập char • ifstream (file input) • ofstream (file output) • fstream (file I/O) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 10 7.2.3 Các đối tượng và các lớp I/O © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 11 7.3 Xuất theo dòng • Output – sử dụng ostream – định dạng và không định dạng dữ liệu xuất – dành cho các kiểu dữ liệu chuẩn (<<) • các ký tự (hàm put) • các kiểu số nguyên (thập phân, bát phân, cơ số 16) • các số chấm động – quy định độ chính xác, vị trí dấu chấm, ký hiệu khoa học – dữ liệu được căn lề, chèn ký tự trống – điều khiển chữ hoa/chữ thường © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 12 7.3.1 Xuất các biến kiểu char * • C++ tự động xác định kiểu dữ liệu – in giá trị của một char * • địa chỉ bộ nhớ của ký tự đầu tiên • Rắc rối – toán tử << được định nghĩa chồng để in xâu kết thúc bằng null – cách giải quyết: đổi thành void * • sử dụng khi in giá trị của một con trỏ • in dưới dạng một số cơ số 16 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 13 // Fig. 12.3: fig12_03.cpp // Printing the address stored in a char * variable. #include <iostream> fig12_03.cpp (1 of 1) using std::cout; using std::endl; int main() { char *word = "test"; fig12_03.cpp output (1 of 1) Để in giá trị của con trỏ, ta phải đổi sang kiểu void *. Nếu không, chương trình sẽ in xâu ký tự. 11 12 13 14 15 16 // display value of char *, then display value of char * // static_cast to void * cout << "Value of word is: " << word << endl << "Value of static_cast< void * >( word ) is: " << static_cast< void * >( word ) << endl; 17 18 return 0; 19 20 } // end main Value of word is: test Value of static_cast< void *>( word ) is: 0046C070 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 14 7.3.2 Xuất ký tự bằng hàm thành viên put • hàm put – in các ký tự • cout.put( 'A' ); – Có thể gọi liền • cout.put( 'A' ).put( '\n' ); • Toán tử dấu chấm (.) được tính từ trái sang phải – Có thể sử dụng giá trị bằng số (mã ASCII) • cout.put( 65 ); • in ký tự 'A' © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 15 7.4 Nhập theo dòng • dữ liệu vào có định dạng và không định dạng – istream • toán tử >> – Thường bỏ qua các ký tự trắng (blank, tab, newline) • Có thể thay đổi – Trả về 0 khi gặp EOF • nếu không, trả về tham chiếu tới istream • cin >> grade – các bit trạng thái được bật nếu xảy ra lỗi • chi tiết sẽ nói đến sau © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 16 7.4.1 Các hàm thành viên get và getline • hàm get – cin.get() – trả về một ký tự từ dòng (kể cả ký tự trắng) • trả về EOF nếu gặp end-of-file • End-of-file – đánh dấu kết thúc dữ liệu vào • ctrl-z tại DOS/Windows • ctrl-d tại UNIX và Mac – cin.eof() • trả về 1 (true) nếu đã gặp EOF © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 17 // Fig. 12.4: fig12_04.cpp // Using member functions get, put and eof. #include <iostream> fig12_04.cpp (1 of 2) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 int main() { int character; // use int, because char cannot represent EOF 12 13 14 15 // prompt user to enter line of text cout << "Before input, cin.eof() is " << cin.eof() << endl << "Enter a sentence followed by end-of-file:" << endl; 16 17 18 19 // use get to read each character; use put to display it while ( ( character = cin.get() ) != EOF ) cout.put( character ); 20 21 22 23 // display end-of-file character cout << "\nEOF in this system is: " << character << endl; cout << "After input, cin.eof() is " << cin.eof() << endl; 24 25 return 0; Hàm get (không có đối số) trả về đúng một ký tự nhập vào, trừ khi gặp EOF. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 18 } // end main Before input, cin.eof() is 0 Enter a sentence followed by end-of-file: Testing the get and put member functions Testing the get and put member functions ^Z fig12_04.cpp (2 of 2) fig12_04.cpp output (1 of 1) EOF in this system is: -1 After input cin.eof() is 1 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 19 7.4.1 Các hàm thành viên get và getline • get(charRef) – đối số là tham chiếu ký tự – đọc một ký tự, lưu vào charRef • trả về tham chiếu tới istream • nếu hết file, trả về -1 • get(charArray, size, delimiter) – đọc cho đến khi được size-1 ký tự, hoặc đến khi gặp ký tự phân cách • phân cách mặc định '\n' • ký tự phân cách được để lại dòng nhập – có thể loại bỏ bằng cin.get() hoặc cin.ignore() – tự động thêm null vào cuối để kết thúc mảng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 fig12_05.cpp (1 of 2) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 13 14 int main() { // create two char arrays, each with 80 elements const int SIZE = 80; char buffer1[ SIZE ]; cin sẽ chỉ đọc cho đến ký tự char buffer2[ SIZE ]; trắng đầu tiên. 15 16 17 18 19 20 21 22 23 24 25 20 // Fig. 12.5: fig12_05.cpp // Contrasting input of a string via cin and cin.get. #include <iostream> // use cin to input characters into buffer1 cout << "Enter a sentence:" << endl; cin >> buffer1; Không chỉ ra ký tự phân cách, do đó sẽ sử dụng phân cách mặc định (\n). // display buffer1 contents cout << "\nThe string read with cin was:" << endl << buffer1 << endl << endl; // use cin.get to input characters into buffer2 cin.get( buffer2, SIZE ); CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 21 26 27 28 29 // display buffer2 contents cout << "The string read with cin.get was:" << endl << buffer2 << endl; 30 31 return 0; 32 33 fig12_05.cpp (2 of 2) fig12_05.cpp output (1 of 1) } // end main Enter a sentence: Contrasting string input with cin and cin.get The string read with cin was: Contrasting The string read with cin.get was: string input with cin and cin.get ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 22 7.4.1 Các hàm thành viên get và getline • getline(array, size, delimiter) – như phiên bản 3 tham số của get – đọc size-1 ký tự, hoặc cho đến khi thấy ký tự phân cách • mặc định \n – loại bỏ ký tự phân cách khỏi dòng vào – đặt ký tự null vào cuối mảng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 23 // Fig. 12.6: fig12_06.cpp // Inputting characters using cin member function getline. #include <iostream> fig12_06.cpp 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 int main() { const int SIZE = 80; char buffer[ SIZE ]; // create array of 80 characters Enter a sentence: (1 of 1) Using the getline member function The sentence entered is: Using the getline member function 13 14 15 16 // input characters in buffer via cin function getline cout << "Enter a sentence:" << endl; cin.getline( buffer, SIZE ); 17 18 19 // display buffer contents cout << "\nThe sentence entered is:" << endl << buffer << endl; 20 21 return 0; 22 23 } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 24 7.4.2 Các hàm thành viên peek, putback và ignore của istream • ignore() – lấy các ký tự khỏi dòng (mặc định là 1 ký tự) – dừng khi gặp ký tự phân cách • phân cách mặc định là EOF • putback() – đẩy ký tự vừa đọc được bằng get() trở lại dòng • peek() – trả về ký tự tiếp theo trong dòng nhưng không lấy ra khỏi dòng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 7.5 I/O không định dạng sử dụng read, write và gcount 25 • I/O không định dạng – read (hàm thành viên của istream) • đọc các byte thô vào mảng char • nếu đọc được không đủ số ký tự, đặt failbit • gcount() trả về số ký tự đã đọc được tại lần gọi gần nhất – write (hàm thành viên của ostream) • xuất các byte từ mảng char – dừng khi gặp ký tự null char buffer[] = "HAPPY BIRTHDAY"; cout.write( buffer, 10 ); – xuất 10 char đầu tiên © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 fig12_07.cpp (1 of 1) 4 5 6 7 using std::cout; using std::cin; using std::endl; 8 9 10 11 12 int main() { const int SIZE = 80; char buffer[ SIZE ]; // create array of 80 characters 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // Fig. 12.7: fig12_07.cpp // Unformatted I/O using read, gcount and write. #include <iostream> đọc 20 ký tự từ dòng vào. Hiển thị sốbuffer ký tự đọc được, // use function read to input characters into cout << "Enter a sentence:" << endl; sử dụng write và gcount. cin.read( buffer, 20 ); // use functions write and gcount to display buffer characters cout << endl << "The sentence entered was:" << endl; cout.write( buffer, cin.gcount() ); cout << endl; Enter a sentence: Using the read, write, and gcount member functions return 0; The sentence entered was: Using the read, writ } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 27 7.6 Giới thiệu về các Stream Manipulator • Stream manipulator thực hiện việc định dạng – – – – – – – – – đổi hệ cơ số (hex, oct, dec, setbase) độ rộng (ký tự) in ra dành cho dữ liệu xuất (setw) đặt số chữ số sau dấu phảy (setprecision) in/không in phần sau dấu phảy của số nguyên (showpoint/noshowpoint) căn trái/phải/giữa (left/right/internal) ký tự chèn vào các vị trí còn trống (setfill) định dạng khoa học/dấu chấm động (scientific/fixed) in các giá trị bool dạng chữ(true,false)/số (boolalpha/noboolalpha) ... © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 28 7.7 Các trạng thái lỗi của dòng • Kiểm tra trạng thái dòng bằng qua các bit trạng thái – eofbit được bật khi gặp EOF • hàm eof trả về true nếu eofbit được bật • cin.eof() – failbit được bật khi dòng xảy ra lỗi • dữ liệu không mất, lỗi có thể khôi phục được • hàm fail trả về true nếu bit được bật – badbit được bật khi mất dữ liệu • thường là không khôi phục được • hàm bad – goodbit bật khi badbit, failbit và eofbit tắt • hàm good © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 29 7.7 Các trạng thái lỗi của dòng • Các hàm thành viên – rdstate() • trả về trạng thái lỗi của dòng • có thể dùng để kiểm tra goodbit, badbit, v.v... • sử dụng good(), bad() thì hơn – clear() • đối số mặc định là goodbit • đặt dòng trở về trạng thái tốt để có thể tiếp tục I/O • có thể truyền các giá trị khác – cin.clear( ios::failbit ) – bật failbit © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 fig12_22.cpp (1 of 2) 4 5 6 7 using std::cout; using std::endl; using std::cin; 8 9 10 11 int main() { int integerValue; 12 13 14 15 16 17 18 19 20 21 22 23 24 30 // Fig. 12.22: fig12_22.cpp // Testing error states. #include <iostream> in trạng thái ban đầu, sử dụng các hàm thành viên // display results of cin functions cout << "Before a bad input operation:" << "\ncin.rdstate(): " << cin.rdstate() << "\n cin.eof(): " << cin.eof() << "\n cin.fail(): " << cin.fail() << "\n cin.bad(): " << cin.bad() << "\n cin.good(): " << cin.good() << "\n\nExpects an integer, but enter a character: "; cin >> integerValue; // enter character value cout << endl; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 29 30 31 // display results of cin functions after bad input cout << "After a bad input operation:" << "\ncin.rdstate(): " << cin.rdstate() << "\n cin.eof(): " << cin.eof() << "\n cin.fail(): " << cin.fail() << "\n cin.bad(): " << cin.bad() << "\n cin.good(): " << cin.good() << endl << endl; fig12_22.cpp (2 of 2) Gọi hàm clear. 32 33 cin.clear(); // clear stream 34 35 36 37 38 // display results of cin functions after clearing cin cout << "After cin.clear()" << "\ncin.fail(): " << cin.fail() << "\ncin.good(): " << cin.good() << endl; 39 40 return 0; 41 42 31 } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Before a bad input operation: cin.rdstate(): 0 cin.eof(): 0 cin.fail(): 0 cin.bad(): 0 cin.good(): 1 32 fig12_22.cpp output (1 of 1) Expects an integer, but enter a character: A After a bad input operation: cin.rdstate(): 2 cin.eof(): 0 cin.fail(): 1 cin.bad(): 0 cin.good(): 0 After cin.clear() cin.fail(): 0 cin.good(): 1 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 33 7.8 Đồng bộ một dòng ra và một dòng vào • Rắc rối với output có bộ nhớ đệm – chương trình tương tác (hỏi người sử dụng, người sử dụng trả lời) – lời yêu cầu cần hiện ra trước khi nhập • output trong bộ nhớ đệm chỉ hiện ra khi bộ nhớ đệm đầy hoặc được xả (flushed) • hàm thành viên tie – đồng bộ hóa các dòng – Output hiện ra trước các input tiếp theo – được thực hiện tự động với cin và cout, nhưng có thể viết • cin.tie( &cout ) – cần thực hiện tường minh đối với các cặp I/O khác – để bỏ đồng bộ • inputStream.tie( 0 ) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 34 7.9 File và dòng • Lưu trữ dữ liệu – Mảng, biến là dạng lưu trữ tạm thời – File là dạng lưu trữ bền vững • đĩa từ - magnetic disk, đĩa quang - optical disk, băng từ - tape • trong chương này – tạo, cập nhật, xử lý file – truy nhập tuần tự (sequential access) và truy nhập ngẫu nhiên (random access) – xử lý có định dạng và xử lý thô (formatted and raw processing) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 35 7.9 File và dòng • Từ nhỏ nhất tới lớn nhất – Bit (binary digit) – Byte: 8 bits • Có thể lưu trữ 1 ký tự (char) • còn dùng để lưu Unicode dành cho bộ ký tự lớn hơn (wchar_t) – Trường - Field: nhóm ký tự có nghĩa • tên – Bản ghi - Record: nhóm các trường có liên quan • • • • struct hoặc class trong C++ trong hệ thống trả lương (payroll system): tên, mã, địa chỉ, lương mỗi trường liên quan đến cùng một nhân viên. Khóa của bản ghi - Record key: trường dùng để xác định duy nhất bản ghi – File: nhóm các bản ghi có liên quan • danh sách lương cho cả công ty – Cơ sở dữ liệu - Database: nhóm các file có liên quan • danh sách lương, các tài khoản, … © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 36 7.9 File và dòng • C++ coi file là một chuỗi byte - stream – Kết thúc bằng ký hiệu end-of-file 0 1 2 3 4 5 6 7 8 9 ... ... n-1 end-of-file marker • Khi file mở – một đối tượng được tạo và kết nối với một dòng – tạo "đường liên lạc" từ đối tượng tới file – cin, cout, v.v... được tạo khi <iostream> được include • liên lạc giữa chương trình và file/thiết bị © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 37 7.10 File truy nhập tuần tự (sequential-access file) • C++ không quy định cấu trúc file – Khái niệm "bản ghi" phải được cài đặt bởi lập trình viên • Mở file – tạo đối tượng từ các lớp • ifstream (input only - chỉ đọc) • ofstream (output only - chỉ ghi) • fstream (I/O – file vừa đọc vừa ghi) – Constructor lấy tên file và kiểu mở file ofstream outClientFile( "filename", fileOpenMode ); – Hoặc, tạo object trước rồi gắn với một file sau ofstream outClientFile; outClientFile.open( "filename", fileOpenMode); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 38 7.10 File truy nhập tuần tự • Các kiểu mở file - File-open modes Mode Description ios::app Viết tiếp output vào cuối file. ios::ate Mở một file để ghi và di chuyển đến cuối file (thường dùng để nối dữ liệu vào file). Dữ liệu có thể được viết vào vị trí tùy ý trong file. Mở file để đọc Mở file để ghi. Loại bỏ nội dung file nếu nó tồn tại (mặc định đối với ios::out) ios::in ios::out ios::trunc ios::binary Mở file nhị phân (i.e., không phải file text) để đọc hoặc ghi. – theo mặc định, ofstream mở để ghi • ofstream outClientFile( "clients.dat", ios::out ); • ofstream outClientFile( "clients.dat"); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 39 7.10 File truy nhập tuần tự • Các phép toán – Overloaded operator! • !outClientFile hoặc !inClientfile • Trả về nonzero (true) nếu badbit hoặc failbit bật – mở file không tồn tại để đọc, không có quyền mở – Overloaded operator void* • chuyển đổi đối tượng dòng thành con trỏ • 0 khi failbit hoặc badbit được bật, nếu không: nonzero – failbit bật khi gặp EOF • while ( inClientFile >> myVariable ) – lặp cho đến khi gặp EOF – Ghi/đoc file (như cout, cin) • outClientFile << myVariable • inClientFile >> myVariable – Đóng file • outClientFile.close() • đóng tự động khi destructor được gọi © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 40 // Fig. 14.4: fig14_04.cpp // Create a sequential file. #include <iostream> fig14_04.cpp (1 of 2) ................... 7 using std::ios; 8 using std::cerr; 9 using std::endl; 10 11 #include <fstream> 12 13 using std::ofstream; 14 15 #include <cstdlib> 16 17 18 19 20 int main() { // ofstream constructor opens file ofstream outClientFile( "clients.dat", ios::out ); Lưu ý các header file cần cho file I/O. // exit prototype ofstream object được tạo và dùng để mở file clients.dat". Nếu file chưa tồn tại, nó sẽ được tạo. 21 22 23 24 25 // exit program if unable to create file if ( !outClientFile ) { // overloaded ! operator cerr << "File could not be opened" << endl; exit( 1 ); 26 27 } // end if ! operator dùng để kiểm tra xem có xảy ra lỗi khi mở file không. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 28 29 30 41 cout << "Enter the account, name, and balance." << endl << "Enter end-of-file to end input.\n? "; fig14_04.cpp (2 of 2) cin được ngầm đổi thành 1 pointer. Khi gặp EOF, nó trả về 0 và vòng lặp dừng. 31 32 33 34 int account; char name[ 30 ]; double balance; 35 36 37 38 39 40 // read account, name and balance from cin, then place in file while ( cin >> account >> name >> balance ) { outClientFile << account << ' ' << name << ' ' << balance << endl; cout << "? "; 41 42 } // end while 43 44 return 0; 45 46 Ghi dữ liệu ra file như ghi ra một dòng chuẩn. // ofstream destructor closes file } // end main Enter Enter ? 100 ? 200 ? 300 ? 400 ? 500 ? ^Z the account, name, and balance. end-of-file to end input. Jones 24.98 Doe 345.67 White 0.00 Stone -42.16 Rich 224.62 CuuDuongThanCong.com File đóng khi destructor của object được gọi. Có thể đóng một cách tường minh bằng cách gọi close(). ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 12 13 using using using using using using using using using 14 15 #include <fstream> 16 17 using std::ifstream; 18 19 #include <iomanip> 20 21 22 23 24 25 26 27 42 // Fig. 14.7: fig14_07.cpp // Reading and printing a sequential file. #include <iostream> std::cout; std::cin; std::ios; std::cerr; std::endl; std::left; std::right; std::fixed; std::showpoint; fig14_07.cpp (1 of 3) using std::setw; using std::setprecision; #include <cstdlib> // exit prototype void outputLine( int, const char * const, double ); ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 28 29 30 31 int main() { // ifstream constructor opens the file mở file để đọc và kiểm tra. fig14_07.cpp ifstream inClientFile( "clients.dat", ios::in ); (2 of 3) 32 33 34 35 36 // exit program if ifstream could not open file if ( !inClientFile ) { cerr << "File could not be opened" << endl; exit( 1 ); 37 38 } // end if 39 40 41 42 int account; char name[ 30 ]; double balance; 43 44 45 cout << left << setw( 10 ) << "Account" << setw( 13 ) << "Name" << "Balance" << endl << fixed << showpoint; 46 47 48 49 // display each record in file while ( inClientFile >> account >> name >> balance ) outputLine( account, name, balance ); 50 51 return 0; // ifstream destructor closes the file 52 53 Đọc từ file đến khi gặp EOF. ©2004 Trần Minh Châu. FOTECH. VNU. } // end main CuuDuongThanCong.com https://fb.com/tailieudientucntt 43 44 54 55 56 57 58 59 60 61 // display single record from file void outputLine( int account, const char * const name, double balance ) { cout << left << setw( 10 ) << account << setw( 13 ) << name << setw( 7 ) << setprecision( 2 ) << right << balance << endl; 62 63 } // end function outputLine Account 100 200 300 400 500 Name Jones Doe White Stone Rich fig14_07.cpp (3 of 3) fig14_07.cpp output (1 of 1) Balance 24.98 345.67 0.00 -42.16 224.62 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 45 7.11 Các hàm định vị cho file tuần tự • con trỏ vị trí ghi số thứ tự của byte tiếp theo để đọc/ghi • các hàm đặt lại vị trí của con trỏ: – seekg (đặt vị trí đọc cho lớp istream) – seekp (đặt vị trí ghi cho ostream) – seekg và seekp lấy các đối số là offset và mốc • Offset: số byte tương đối kể từ mốc • Mốc (ios::beg mặc định) – ios::beg - đầu file – ios::cur - vị trí hiện tại – ios::end - cuối file • các hàm lấy vị trí hiện tại của con trỏ: – tellg và tellp © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 46 7.11 Các hàm định vị cho file tuần tự • Ví dụ – fileObject.seekg(0) • đến đầu file (vị trí 0), mặc định đối số thứ hai là ios::beg – fileObject.seekg(n) • đến byte thứ n kể từ đầu file – fileObject.seekg(n, ios::cur) • tiến n byte – fileObject.seekg(y, ios::end) • lùi y byte kể từ cuối file – fileObject.seekg(0, ios::cur) • đến cuối file – seekp tương tự – location = fileObject.tellg() • lấy vị trí đọc hiện tại của fileObject © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 47 7.11 Các hàm định vị cho file tuần tự • Ví dụ: – chương trình quản lý tài khoản ngân hàng - Credit manager program – dữ liệu: file clients.dat – các chức năng: 1. in danh sách các tài khoản rỗng (account with zero balance) 2. in danh sách các tài khoản âm (account with credit) 3. in danh sách các tài khoản dương (account with debit) – hoạt động của chương trình 1. menu cho phép người dùng chọn một chức năng hoặc chọn dừng chương trình 2. thực hiện chức năng đã chọn và in kết quả 3. quay lại menu © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 12 13 using using using using using using using using using 14 15 #include <fstream> 16 17 using std::ifstream; 18 19 #include <iomanip> 20 21 22 using std::setw; using std::setprecision; 23 24 25 48 // Fig. 14.8: fig14_08.cpp // Credit-inquiry program. #include <iostream> std::cout; std::cin; std::ios; std::cerr; std::endl; std::fixed; std::showpoint; std::left; std::right; fig14_08.cpp (1 of 6) #include <cstdlib> ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 26 27 28 29 30 enum RequestType { ZERO_BALANCE = 1, CREDIT_BALANCE, DEBIT_BALANCE, END }; int getRequest(); bool shouldDisplay( int, double ); void outputLine( int, const char * const, double ); 31 32 33 34 35 int main() { // ifstream constructor opens the file ifstream inClientFile( "clients.dat", ios::in ); 36 37 38 39 40 // exit program if ifstream could not open file if ( !inClientFile ) { cerr << "File could not be opened" << endl; exit( 1 ); 41 42 } // end if 43 44 45 46 47 int request; int account; char name[ 30 ]; double balance; 49 fig14_08.cpp (2 of 6) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 49 50 // get user's request (e.g., zero, credit or debit balance) request = getRequest(); 51 52 53 // process user's request while ( request != END ) { 54 55 switch ( request ) { 56 57 58 59 case ZERO_BALANCE: cout << "\nAccounts with zero balances:\n"; break; 60 61 62 63 case CREDIT_BALANCE: cout << "\nAccounts with credit balances:\n"; break; 64 65 66 67 case DEBIT_BALANCE: cout << "\nAccounts with debit balances:\n"; break; 68 69 50 fig14_08.cpp (3 of 6) } // end switch 70 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 71 72 // read account, name and balance from file inClientFile >> account >> name >> balance; 73 74 75 // display file contents (until eof) while ( !inClientFile.eof() ) { fig14_08.cpp (4 of 6) 76 77 78 79 // display record if ( shouldDisplay( request, balance ) ) outputLine( account, name, balance ); 80 81 82 // read account, name and balance from file inClientFile >> account >> name >> balance; 83 84 85 86 87 88 } // end inner while Dùng clear để bỏ cờ eof. Dùng seekg để đặt con trỏ định vị file về đầu file. inClientFile.clear(); // reset eof for next input inClientFile.seekg( 0 ); // move to beginning of file request = getRequest(); // get additional request from user 89 90 } // end outer while 91 92 cout << "End of run." << endl; 93 94 return 0; // ifstream destructor closes the file 95 96 51 ©2004 Trần Minh Châu. FOTECH. VNU. } // end main CuuDuongThanCong.com https://fb.com/tailieudientucntt 52 97 98 // obtain request from user 99 int getRequest() 100 { 101 int request; fig14_08.cpp (5 of 6) 102 103 104 105 106 107 108 // display request options cout << "\nEnter request" << endl << " 1 - List accounts with zero balances" << endl << " 2 - List accounts with credit balances" << endl << " 3 - List accounts with debit balances" << endl << " 4 - End of run" << fixed << showpoint; 109 110 111 112 113 // input user request do { cout << "\n? "; cin >> request; 114 115 } while ( request < ZERO_BALANCE && request > END ); 116 117 return request; 118 119 } // end function getRequest 120 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 // determine whether to display given record bool shouldDisplay( int type, double balance ) { // determine whether to display credit balances if ( type == CREDIT_BALANCE && balance < 0 ) return true; 53 fig14_08.cpp (6 of 6) // determine whether to display debit balances if ( type == DEBIT_BALANCE && balance > 0 ) return true; // determine whether to display zero balances if ( type == ZERO_BALANCE && balance == 0 ) return true; return false; } // end function shouldDisplay // display single record from file void outputLine( int account, const char * const name, double balance ) { cout << left << setw( 10 ) << account << setw( 13 ) << name << setw( 7 ) << setprecision( 2 ) << right << balance << endl; } // end function outputLine CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 54 Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 1 Accounts with zero balances: 300 White 0.00 Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 2 Accounts with credit balances: 400 Stone -42.16 fig14_08.cpp output (1 of 2) Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 3 Accounts with debit balances: 100 Jones 24.98 200 Doe 345.67 500 Rich 224.62 Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 4 End of run. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 3 55 fig14_08.cpp output (2 of 2) Accounts with debit balances: 100 Jones 24.98 200 Doe 345.67 500 Rich 224.62 Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 4 End of run. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 56 7.12 Các rắc rối khi cập nhật file truy nhập tuần tự • cập nhật các file truy nhập tuần tự – Rủi ro: ghi đè các dữ liệu khác – Ví dụ: đổi tên từ "White" thành "Worthington" • Dữ liệu cũ 300 White 0.00 400 Jones 32.87 • Chèn dữ liệu mới 300 Worthington 0.00 300 White 0.00 400 Jones 32.87 Dữ liệu bị ghi đè 300 Worthington 0.00ones 32.87 – Định dạng bị rối loạn – Vấn đề có thể tránh được, nhưng biện pháp không hay. © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 57 7.13 Random-Access Files (các file truy nhập ngẫu nhiên) • Truy nhập tức thời - Instant access – muốn định vị bản ghi một cách nhanh chóng • các hệ thống đặt vé máy bay (airline reservations), máy rút tiền tự động (ATM) – các file tuần tự phải duyệt qua từng bản ghi một • Giải pháp: các file truy nhập ngẫu nhiên – khả năng truy nhập tức thời – chèn bản ghi mà không phá các dữ liệu khác – cập nhật/xóa một phần tử dữ liệu mà không làm thay đổi các dữ liệu khác © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 58 7.13 File truy nhập ngẫu nhiên (random-access file) • C++ không quy định quy cách file – lập trình viên phải tự tạo quy cách cho các file truy nhập ngẫu nhiên – cách đơn giản nhất: các bản ghi độ dài cố định • tính toán được vị trí trong file từ kích thước bản ghi và khóa 0 100 200 300 400 500 } byte offsets } } } } } } 100 100 100 100 100 100 bytes bytes bytes bytes bytes bytes © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 59 7.13.1 Dữ liệu thô và dữ liệu định dạng • Ví dụ: "1234567" (char *) và 1234567 (int) – định dạng: char * cần 8 byte (1 byte cho mỗi ký tự + null) – thô: int lấy một số cố định byte (có thể là 4) • 123 có cùng kích thước theo byte với 1234567 • các phép toán << và >> dành cho dữ liệu định dạng – outFile << number • ghi number (int) dưới dạng char * • số lượng byte không cố định • hàm write()và read() dành cho dữ liệu thô – outFile.write( const char *, size ); • ghi ra các byte dạng thô • lấy tham số là con trỏ tới địa chỉ bộ nhớ, số byte cần ghi – sao chép dữ liệu trực tiếp từ bộ nhớ sang file – Không đổi thành char * © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 60 7.13.2 Ghi file truy nhập nhẫu nhiên • Ví dụ hàm write() outFile.write( reinterpret_cast<const char *>(&number), sizeof( number ) ); – &number là int * • đổi thành const char * bằng reinterpret_cast – sizeof(number) • kích thước của number (một số int) tính theo byte – tương tự đối với hàm read (more later) – Chú ý: • chỉ dùng write/read giữa các máy tương thích – mở file kiểu ios::binary để đọc/ghi thô • thường dùng để ghi toàn bộ một struct hoặc một đối tượng ra file © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 61 7.13.2 Ghi file truy nhập ngẫu nhiên • Bài toán – chương trình quản lý tài khoản – Lưu trữ tối đa 100 bản ghi kích thước cố định – Bản ghi • Mã tài khoản - Account number (khóa) • Họ và tên - First and last name • Số tiền hiện có trong tài khoản - Balance – Các thao tác: • cập nhật, tạo mới, xóa, liệt kê tất cả các tài khoản ra một file • Tiếp theo: chương trình tạo file chứa 100 bản ghi rỗng © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 4 // Fig. 14.10: clientData.h // Class ClientData definition used in Fig. 14.12–Fig. 14.15. #ifndef CLIENTDATA_H #define CLIENTDATA_H 5 6 #include <iostream> 7 8 using std::string; 9 10 class ClientData { 11 12 public: 62 clientData.h (1 of 2) Class ClientData lưu thông tin về từng người. 100 đối tượng ClientData rỗng sẽ được ghi ra 1 file. 13 14 15 // default ClientData constructor ClientData( int = 0, string = "", string = "", double = 0.0 ); 16 17 18 19 // accessor functions for accountNumber void setAccountNumber( int ); int getAccountNumber() const; 20 21 22 23 // accessor functions for lastName void setLastName( string ); string getLastName() const; 24 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 25 26 27 28 29 30 31 63 // accessor functions for firstName void setFirstName( string ); string getFirstName() const; // accessor functions for balance void setBalance( double ); double getBalance() const; 32 33 34 35 36 37 private: int accountNumber; char lastName[ 15 ]; char firstName[ 10 ]; double balance; 38 39 }; // end class ClientData 40 41 #endif clientData.h (2 of 2) Đặt giới hạn kích thước tên và họ. accountNumber (một số int) và balance (double) đã có kích thước cố định. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 64 // Fig. 14.11: ClientData.cpp // Class ClientData stores customer's credit information. #include <iostream> ClientData.cpp (1 of 4) using std::string; #include <cstring> #include "clientData.h" // default ClientData constructor ClientData::ClientData( int accountNumberValue, string lastNameValue, string firstNameValue, double balanceValue ) { setAccountNumber( accountNumberValue ); setLastName( lastNameValue ); setFirstName( firstNameValue ); setBalance( balanceValue ); } // end ClientData constructor // get account-number value int ClientData::getAccountNumber() const { return accountNumber; } // end function getAccountNumber CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 65 28 29 30 31 32 // set account-number value void ClientData::setAccountNumber( int accountNumberValue ) { accountNumber = accountNumberValue; 33 34 } // end function setAccountNumber 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ClientData.cpp (2 of 4) // get last-name value string ClientData::getLastName() const { return lastName; } // end function getLastName // set last-name value void ClientData::setLastName( string lastNameString ) { // copy at most 15 characters from string to lastName const char *lastNameValue = lastNameString.data(); int length = strlen( lastNameValue ); length = ( length < 15 ? length : 14 ); strncpy( lastName, lastNameValue, length ); // append null character to lastName lastName[ length ] = '\0'; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 66 54 55 } // end function setLastName 56 57 58 59 60 // get first-name value string ClientData::getFirstName() const { return firstName; 61 62 } // end function getFirstName 63 64 65 66 67 68 69 70 71 // set first-name value void ClientData::setFirstName( string firstNameString ) { // copy at most 10 characters from string to firstName const char *firstNameValue = firstNameString.data(); int length = strlen( firstNameValue ); length = ( length < 10 ? length : 9 ); strncpy( firstName, firstNameValue, length ); 72 73 74 75 76 ClientData.cpp (3 of 4) // append new-line character to firstName firstName[ length ] = '\0'; } // end function setFirstName 77 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 78 79 80 81 // get balance value double ClientData::getBalance() const { return balance; 82 83 } // end function getBalance 84 85 86 87 88 // set balance value void ClientData::setBalance( double balanceValue ) { balance = balanceValue; 89 90 } // end function setBalance 67 ClientData.cpp (4 of 4) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 1 2 3 68 // Fig. 14.12: fig14_12.cpp // Creating a randomly accessed file. #include <iostream> fig14_12.cpp (1 of 2) 4 5 6 7 using std::cerr; using std::endl; using std::ios; 8 9 #include <fstream> 10 11 using std::ofstream; 12 13 14 #include <cstdlib> #include "clientData.h" 15 16 17 18 int main() { ofstream outCredit( "credit.dat", ios::binary ); // ClientData class definition 19 20 21 22 23 // exit program if ofstream could not open file if ( !outCredit ) { cerr << "File could not be opened." << endl; exit( 1 ); 24 25 } // end if Mở 1 file để ghi thô, sử dụng một đối tượng ofstream và ios::binary. ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 69 26 27 28 // create ClientData with no information Tạo một đối tượng rỗng. ClientData blankClient; 29 30 31 32 33 34 // output 100 blank records to file for ( int i = 0; i < 100; i++ ) outCredit.write( reinterpret_cast< const char * >( &blankClient ), sizeof( ClientData ) ); 35 36 return 0; 37 38 Dùng write để ghi dữ liệu fig14_12.cpp thô ra 1 file (truyền tham số (2 of 2) là địa chỉ đối tượng và kích thước đối tượng). } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 70 7.13.3 Ghi dữ liệu vào vị trí tùy ý trong file truy nhập ngẫu nhiên • Dùng seekp để ghi vào vị trí chính xác trong file – Bản ghi đầu tiên bắt đầu từ đâu? • Byte 0 – Bản ghi thứ hai? • Byte 0 + sizeof(object) – Bản ghi bất kỳ? • (Recordnum - 1) * sizeof(object) © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 ...... 19 #include <cstdlib> 20 #include "clientData.h" 21 22 23 24 25 26 27 71 // Fig. 14.13: fig14_13.cpp // Writing to a random access file. #include <iostream> int main() { int accountNumber; char lastName[ 15 ]; char firstName[ 10 ]; double balance; fig14_13.cpp (1 of 3) // ClientData class definition Mở file để ghi thô (binary writing). 28 29 ofstream outCredit( "credit.dat", ios::binary ); 30 31 32 33 34 // exit program if ofstream cannot open file if ( !outCredit ) { cerr << "File could not be opened." << endl; exit( 1 ); 35 36 } // end if ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 38 39 40 41 42 43 44 45 46 47 48 cout << "Enter account number " << "(1 to 100, 0 to end input)\n? ";Nhập account number, ghi vào đối tượng. Nó chưa được viểt ra file. fig14_13.cpp // require user to specify account number (2 of 3) ClientData client; cin >> accountNumber; client.setAccountNumber( accountNumber ); // user enters information, which is copied into file while ( client.getAccountNumber() > 0 && client.getAccountNumber() <= 100 ) { 49 50 51 52 53 54 // user enters last name, first name and balance cout << "Enter lastname, firstname, balance\n? "; cin >> setw( 15 ) >> lastName; cin >> setw( 10 ) >> firstName; cin >> balance; 55 56 57 58 59 // set record lastName, firstName and balance values client.setLastName( lastName ); client.setFirstName( firstName ); client.setBalance( balance ); ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 72 73 60 61 62 63 Đặt outCredit vào vị trí thích hợp trong file (dựa vào account number). fig14_13.cpp (3 of 3) // seek position in file of user-specified record outCredit.seekp( ( client.getAccountNumber() - 1 ) * sizeof( ClientData ) ); Ghi đối tượng ClientData vào file tại vị trí đó. 64 65 66 67 68 // write user-specified information in file outCredit.write( reinterpret_cast< const char * >( &client ), sizeof( ClientData ) ); 69 70 71 72 73 // enable user to specify another account number cout << "Enter account number\n? "; cin >> accountNumber; client.setAccountNumber( accountNumber ); 74 75 } // end while 76 77 return 0; 78 79 } // end main ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt Enter account number (1 to ? 37 Enter lastname, firstname, ? Barker Doug 0.00 Enter account number ? 29 Enter lastname, firstname, ? Brown Nancy -24.54 Enter account number ? 96 Enter lastname, firstname, ? Stone Sam 34.98 Enter account number ? 88 Enter lastname, firstname, ? Smith Dave 258.34 Enter account number ? 33 Enter lastname, firstname, ? Dunn Stacey 314.33 Enter account number ? 0 74 100, 0 to end input) balance Lưu ý các account có thể được tạo theo thứ tự tùy ý. fig14_13.cpp output (1 of 1) balance balance balance balance ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 7.13.4 Đọc tuần tự dữ liệu từ file truy nhập ngẫu nhiên 75 • read - tương tự write – Đọc các byte thô từ file vào bộ nhớ – inFile.read( reinterpret_cast<char *>( &number ), sizeof( int ) ); • &number: địa chỉ để lưu dữ liệu • sizeof(int): số byte cần đọc – Không dùng inFile >> number cho dữ liệu thô - nhị phân • >> nhận char * • Chương trình tiếp theo – lấy dữ liệu từ một file random-access – duyệt tuần tự qua từng bản ghi • If no data (accountNumber == 0) then skip © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 // Fig. 14.14: fig14_14.cpp 2 // Reading a random access file. ...... 25 #include "clientData.h" // ClientData class definition 26 27 void outputLine( ostream&, const ClientData & ); 28 29 int main() 30 { 31 ifstream inCredit( "credit.dat", ios::in ); 32 33 // exit program if ifstream cannot open file 34 if ( !inCredit ) { 35 cerr << "File could not be opened." << endl; 36 exit( 1 ); 37 38 } // end if 39 40 41 42 cout << left << setw( 10 ) << "Account" << setw( 16 ) << "Last Name" << setw( 11 ) << "First Name" << left << setw( 10 ) << right << "Balance" << endl; 43 44 ClientData client; // create record 45 46 47 48 // read first record from file inCredit.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); CuuDuongThanCong.com 76 fig14_14.cpp (1 of 2) Đọc sizeof(ClientData) byte và ghi vào đối tượng client. Đây có thể là một bản ghi rỗng. ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt 77 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 // read all records from file while ( inCredit && !inCredit.eof() ) { // display record if ( client.getAccountNumber() != 0 ) outputLine( cout, client ); fig14_14.cpp Vòng lặp dừng khi có lỗi đọc of 2)gặp EOF (inCredit == 0)(2hoặc (inCredit.eof() == 1) // read next from file inCredit.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); Output non-empty accounts. Lưu ý outputLine lấy 1 tham số kiểu ostream. Ta có thể dễ dàng output ra một file khác (mở bằngmột ofstream object, là dẫn xuất của ostream). } // end while return 0; } // end main // display single record void outputLine( ostream &output, const ClientData &record ) { output << left << setw( 10 ) << record.getAccountNumber() << setw( 16 ) << record.getLastName().data() << setw( 11 ) << record.getFirstName().data() << setw( 10 ) << setprecision( 2 ) << right << fixed << showpoint << record.getBalance() << endl; } // end outputLine CuuDuongThanCong.com ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt Account 29 33 37 88 96 Last Name Brown Dunn Barker Smith Stone First Name Nancy Stacey Doug Dave Sam Balance -24.54 314.33 0.00 258.34 34.98 78 fig14_14.cpp output (1 of 1) ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 79 7.14 Ví dụ: Chương trình xử lý giao dịch • Bài toán: – chương trình quản lý các tài khoản ngân hàng, cho phép truy nhập trực tiếp từng tài khoản – dữ liệu: file truy nhập ngẫu nhiên credit.dat • Các chức năng cho người dùng (các lựa chọn cho menu) – Lựa chọn 1: ghi các account ra file print.txt Account 29 33 37 88 96 Last Name Brown Dunn Barker Smith Stone First Name Nancy Stacey Doug Dave Sam Balance -24.54 314.33 0.00 258.34 34.98 – Lựa chọn 2: cập nhật bản ghi Enter account to update (1 - 100): 37 37 Barker Doug Enter charge (+) or payment (-): +87.99 37 Barker Doug 0.00 87.99 © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 80 7.14 Ví dụ: Chương trình xử lý giao dịch • Các chức năng (tiếp) – Lựa chọn 3: thêm bản ghi Enter new account number (1 - 100): 22 Enter lastname, firstname, balance ? Johnston Sarah 247.45 – Lựa chọn 4: xóa bản ghi Enter account to delete (1 - 100): 29 Account #29 deleted. • Mở file vừa đọc vừa ghi – Dùng fstream object – nhiều file-open mode cùng lúc fstream inOutCredit( "credit.dat", ios::in | ios::out ); © 2004 Trần Minh Châu. FOTECH. VNU CuuDuongThanCong.com Chương 7. https://fb.com/tailieudientucntt 1 2 3 4 5 // Fig. 14.15: fig14_15.cpp // This program reads a random access file sequentially, updates // data previously written to the file, creates data to be placed // in the file, and deletes data previously in the file. #include <iostream> 6 7 using std::cout; ... 15 using std::showpoint; 16 17 #include <fstream> 18 19 20 21 using std::ofstream; using std::ostream; using std::fstream; 22 23 #include <iomanip> 24 25 26 using std::setw; using std::setprecision; 27 28 29 #include <cstdlib> #include "clientData.h" 81 fig14_15.cpp (1 of 14) // exit prototype // ClientData class definition ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 82 30 31 32 33 34 35 36 37 int enterChoice(); void printRecord( fstream& ); void updateRecord( fstream& ); void newRecord( fstream& ); void deleteRecord( fstream& ); void outputLine( ostream&, const ClientData & ); int getAccount( const char * const ); 38 39 enum Choices { PRINT = 1, UPDATE, NEW, DELETE, END }; 40 41 42 43 44 Mở file để đọc và ghi (cần int main() fstream object). { // open file for reading and writing fstream inOutCredit( "credit.dat", ios::in | ios::out ); 45 46 47 48 49 50 51 52 fig14_15.cpp (2 of 14) // exit program if fstream cannot open file if ( !inOutCredit ) { cerr << "File could not be opened." << endl; exit ( 1 ); } // end if ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 83 int choice; // enable user to specify action while ( ( choice = enterChoice() ) != END ) { switch ( choice ) { fig14_15.cpp (4 of 14) Hiện menu và trả về lựa chọn người dùng. // create text file from record file case PRINT: printRecord( inOutCredit ); break; // update record case UPDATE: updateRecord( inOutCredit ); break; // create record case NEW: newRecord( inOutCredit ); break; // delete existing record case DELETE: deleteRecord( inOutCredit ); break; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 80 81 82 83 // display error if user does not select valid choice default: cerr << "Incorrect choice" << endl; break; 84 85 } // end switch 86 87 inOutCredit.clear(); // reset end-of-file indicator 88 89 } // end while 90 91 return 0; 92 93 84 fig14_15.cpp (5 of 14) } // end main 94 95 // enable user to input menu choice 96 int enterChoice() 97 { 98 // display available options 99 cout << "\nEnter your choice" << endl 100 << "1 - store a formatted text file of accounts" << endl 101 << " called \"print.txt\" for printing" << endl 102 << "2 - update an account" << endl 103 << "3 - add a new account" << endl 104 << "4 - delete an account" << endl ©2004 Trần Minh Châu. FOTECH. VNU. 105 << "5 - end program\n? "; CuuDuongThanCong.com https://fb.com/tailieudientucntt 106 107 108 109 110 85 int menuChoice; cin >> menuChoice; // receive choice from user fig14_15.cpp (6 of 14) return menuChoice; 111 112 } // end function enterChoice 113 114 // create formatted text file for printing In ra print.txt. Trước 115 void printRecord( fstream &readFromFile ) tiên, in header của bảng. 116 { 117 // create text file 118 ofstream outPrintFile( "print.txt", ios::out ); 119 120 121 122 123 // exit program if ofstream cannot create file if ( !outPrintFile ) { cerr << "File could not be created." << endl; exit( 1 ); 124 125 } // end if 126 127 128 129 outPrintFile << left << setw( 10 ) << "Account" << setw( 16 ) << "Last Name" << setw( 11 ) << "First Name" << right << setw( 10 ) << "Balance" << endl; 130 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 86 131 132 // set file-position pointer to beginning of record file readFromFile.seekg( 0 ); 133 134 135 136 137 fig14_15.cpp // read first record from record file Đến đầu file, (7 of 14)đọc dữ liệu ClientData client; readFromFile.read( reinterpret_cast< char * >( &client ), về tài khoản, và in bản ghi nếu nó không rỗng. sizeof( ClientData ) ); 138 139 140 // copy all records from record file into text file while ( !readFromFile.eof() ) { Lưu ý outputLine lấy đối số là đối tượng ostream object (lớp cơ sở của ofstream). Nó có thể ghi ra file (như trong trường hợp này) hoặc cout. 141 142 143 144 // write single record to text file if ( client.getAccountNumber() != 0 ) outputLine( outPrintFile, client ); 145 146 147 148 // read next record from record file readFromFile.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); 149 150 } // end while 151 152 } // end function printRecord 153 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 154 // update balance in record 155 void updateRecord( fstream &updateFile ) 156 { 157 // obtain number of account to update 158 int accountNumber = getAccount( "Enter account to update" ); 87 fig14_15.cpp (8 of 14) 159 160 161 162 // move file-position pointer to correct record in file updateFile.seekg( ( accountNumber - 1 ) * sizeof( ClientData ) ); 163 164 165 166 167 // read first record from file cập nhật nó, và ghi balance mới. ClientData client; updateFile.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); 168 169 170 171 // update record if ( client.getAccountNumber() != 0 ) { outputLine( cout, client ); 172 173 174 175 176 Đây là fstream (I/O) vì ta phải đọc balance cũ, // request user to specify transaction cout << "\nEnter charge (+) or payment (-): "; double transaction; // charge or payment cin >> transaction; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 88 177 178 179 180 181 // update record balance double oldBalance = client.getBalance(); client.setBalance( oldBalance + transaction ); outputLine( cout, client ); 182 183 184 185 // move file-position pointer to correct record in file updateFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) ); 186 187 188 189 190 // write updated record over old record in file updateFile.write( reinterpret_cast< const char * >( &client ), sizeof( ClientData ) ); 191 192 } // end if 193 194 195 196 197 // display error if account does not exist else cerr << "Account #" << accountNumber << " has no information." << endl; fig14_15.cpp (9 of 14) 198 199 } // end function updateRecord 200 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 201 // create and insert record 202 void newRecord( fstream &insertInFile ) 203 { 204 // obtain number of account to create 205 int accountNumber = getAccount( "Enter new account number" ); 89 fig14_15.cpp (10 of 14) 206 207 208 209 // move file-position pointer to correct record in file insertInFile.seekg( Đây là fstream vì ta đọc ( accountNumber - 1 ) * sizeof( ClientData ) ); 210 211 212 213 214 // read record from file ClientData client; insertInFile.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); 215 216 217 // create record, if record does not previously exist if ( client.getAccountNumber() == 0 ) { 218 219 220 221 thử để xem đã có sẵn một bản ghi rỗng hay chưa, nếu chưa, ta ghi một bản ghi mới. char lastName[ 15 ]; char firstName[ 10 ]; double balance; ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 90 // user enters last name, first name and balance cout << "Enter lastname, firstname, balance\n? "; cin >> setw( 15 ) >> lastName; cin >> setw( 10 ) >> firstName; cin >> balance; fig14_15.cpp (11 of 14) // use values to populate account values client.setLastName( lastName ); client.setFirstName( firstName ); client.setBalance( balance ); client.setAccountNumber( accountNumber ); // move file-position pointer to correct record in file insertInFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) ); // insert record in file insertInFile.write( reinterpret_cast< const char * >( &client ), sizeof( ClientData ) ); } // end if 245 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 91 // display error if account previously exists else cerr << "Account #" << accountNumber << " already contains information." << endl; fig14_15.cpp (12 of 14) } // end function newRecord // delete an existing record void deleteRecord( fstream &deleteFromFile ) { // obtain number of account to delete int accountNumber = getAccount( "Enter account to delete" ); // move file-position pointer to correct record in file deleteFromFile.seekg( là fstream vì ta đọc để ( accountNumber - 1 ) * sizeof( ClientData ) ); kiểm tra xem account có tồn tại không. Nếu có, ta ghi dữ liệu rỗng (xóa nó). Nếu không, không cần xóa. // read record from file ClientData client; deleteFromFile.read( reinterpret_cast< char * >( &client ), sizeof( ClientData ) ); 267 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 268 269 270 271 272 273 274 275 276 277 278 279 280 281 92 // delete record, if record exists in file if ( client.getAccountNumber() != 0 ) { ClientData blankClient; // move file-position pointer to correct record in file deleteFromFile.seekp( ( accountNumber - 1 ) * sizeof( ClientData ) ); fig14_15.cpp (13 of 14) // replace existing record with blank record deleteFromFile.write( reinterpret_cast< const char * >( &blankClient ), sizeof( ClientData ) ); cout << "Account #" << accountNumber << " deleted.\n"; 282 283 } // end if 284 285 286 287 // display error if record does not exist else cerr << "Account #" << accountNumber << " is empty.\n"; 288 289 } // end deleteRecord 290 ©2004 Trần Minh Châu. FOTECH. VNU. CuuDuongThanCong.com https://fb.com/tailieudientucntt 291 // display single record 292 void outputLine( ostream &output, const ClientData &record ) 293 { 294 output << left << setw( 10 ) << record.getAccountNumber() 295 << setw( 16 ) << record.getLastName().data() 296 << setw( 11 ) << record.getFirstName().data() 297 << setw( 10 ) << setprecision( 2 ) << right << fixed 298 << showpoint << record.getBalance() << endl; 299 300 } // end function outputLine 301 302 // obtain account-number value from user 303 int getAccount( const char * const prompt ) 304 { 305 int accountNumber; // obtain account-number value do { cout << prompt << " (1 - 100): "; cin >> accountNumber; 311 312 } while ( accountNumber < 1 || accountNumber > 100 ); 313 314 return accountNumber; CuuDuongThanCong.com fig14_15.cpp (14 of 14) outputLine rất mềm dẻo, và có thể ghi ra ostream object bất kỳ (chẳng hạn 1 file hoặc cout). 306 307 308 309 310 315 316 } // end function getAccount 93 ©2004 Trần Minh Châu. FOTECH. VNU. https://fb.com/tailieudientucntt