BÀI THỰC HÀNH SỐ 6 Lớp cơ sở trừu tượng trong C++ Xây dựng một lớp cơ sở trừu tượng Vector chứa ba phương thức ảo thuần túy: TinhDoDai tính độ dài, SinGoc tính sin của góc giữa hai Vecto[r]
(1)CuuDuongThanCong.com https://fb.com/tailieudientucntt (2) ĐẶNG NGỌC HOÀNG THÀNH C++ LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG Tài liệu học tập Trang | CuuDuongThanCong.com https://fb.com/tailieudientucntt (3) PHỤ LỤC GIỚI THIỆU MÔI TRƯỜNG PHÁT TRIỂN TÍCH HỢP IDE 10 CHƯƠNG CƠ BẢN VỀ C++ 23 CHƯƠNG BIẾN VÀ CÁC KIỂU DỮ LIỆU 26 Từ khóa 26 Kiểu liệu nguyên thủy 27 Khai báo biến 28 Phạm vi tác dụng biến 29 Khởi tạo giá trị cho biến 30 Khởi tạo giá trị cho biến tĩnh static 31 Giới thiệu xâu kí tự 32 CHƯƠNG HẰNG 34 Hằng số nguyên 34 Hằng số thực có dấu chấm động 34 Hằng kí tự và xâu kí tự 35 Hằng logic 36 Định nghĩa #define 36 Khai báo const 37 CHƯƠNG TOÁN TỬ 38 Toán tử gán 38 Toán tử thực phép toán số học 39 Toán tử gán hợp 40 Toán tử tăng v{ giảm 40 Toán tử logic 42 Toán tử điều kiện 43 Toán tử phân tách 45 Trang |2 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Toán tử so sánh 41 (4) Toán tử dịch bit 45 Toán tử chuyển đổi kiểu liệu 48 Các toán tử khác 49 Thứ tự ưu tiên các toán tử 49 CHƯƠNG XUẤT NHẬP CƠ BẢN 52 Xuất liệu chuẩn cout 52 Nhập liệu chuẩn cin 53 Nhập liệu nhờ lớp stringstream 55 CHƯƠNG CÁC CẤU TRÚC LỆNH ĐIỀU KHIỂN 58 Cấu trúc lệnh có điều kiện: if và else 58 Cấu trúc lặp 60 Cấu trúc lựa chọn: switch 67 CHƯƠNG HÀM 72 Khai báo và sử dụng hàm 73 Phạm vi tác dụng biến 77 Hàm không trả giá trị - Hàm void 78 Tham biến và tham trị 79 Giá trị mặc định tham số hình thức 82 Chồng chất hàm 83 Hàm nội tuyến 84 H{m đệ quy 85 CHƯƠNG CÁC KIỂU DỮ LIỆU CÓ CẤU TRÚC 88 Mảng 88 Xâu kí tự 91 CHƯƠNG CON TRỎ 93 Toán tử tham chiếu & 93 Khai báo biến trỏ 96 Con trỏ, mảng và xâu kí tự 98 Trang |3 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Toán tử tham chiếu ngược * 94 (5) Các phép toán số học trên trỏ 100 Con trỏ trỏ vào trỏ 102 Con trỏ void 104 Con trỏ null 105 Con trỏ hàm 105 CHƯƠNG 10 BỘ NHỚ ĐỘNG 107 Toán tử new và new[] 107 Toán tử delete và delete[] 109 CHƯƠNG 11 KIỂU DỮ LIỆU STRUCT VÀ CON TRỎ STRUCT 110 Struct 110 Con trỏ struct 114 Struct lồng 115 Kích thước nhớ struct 115 CHƯƠNG 12 CÁC KIỂU DỮ LIỆU KHÁC 117 Kiểu liệu tự định nghĩa 117 Kiểu liệu union thường 117 Kiểu liệu union ẩn danh 118 Kiểu liệu enum 118 CHƯƠNG 13 LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG 120 Lịch sử hình thành 120 Lớp v{ đối tượng 126 Hàm tạo và hàm hủy 130 Chồng chất hàm tạo 132 Sao chép hàm tạo 133 Tính đóng gói – Encapsulation 139 Con trỏ đối tượng 140 Con trỏ this 141 Th{nh viên tĩnh – Từ khóa static 143 Trang |4 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lớp khai báo nhờ từ khóa struct và union 141 (6) Hàm bạn và lớp bạn 144 Chồng chất toán tử 147 Tính kế thừa - Inheritance 153 Các mức truy cập 156 Tính đa kế thừa – Multiple Inheritance 159 Tính đa hình – Polymorphism 160 Tính trừu tượng hóa - Abstraction 172 Hàm mẫu – Template Function 173 Lớp mẫu – Template class 173 CHƯƠNG 14 NAMESPACE 178 Từ khóa namespace 178 Từ khóa using 179 Phạm vi namespace 180 T|i định danh cho namespace 181 Namespace std 181 CHƯƠNG 15 NGOẠI LỆ 182 Mệnh đề try…catch 182 Mệnh đề throw 182 Thư viện chuẩn exception 183 CHƯƠNG 16 LÀM VIỆC VỚI FILE 186 Mở file 186 Đóng file 188 File văn 188 Kiểm tra trạng thái các cờ hiệu 189 Con trỏ get và put 190 File nhị phân 192 CHƯƠNG 17 CÁC LỚP THƯ VIỆN 194 Lớp số phức complex 194 Trang |5 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Bộ đệm v{ Đồng hóa 193 (7) Lớp ngăn xếp stack 196 Lớp h{ng đợi queue 197 Lớp vector 198 Lớp string 200 Lớp list 203 Lớp map 203 Lớp set 204 Các lớp thư viện nhập xuất 204 HƯỚNG DẪN THỰC HÀNH 212 BÀI THỰC HÀNH SỐ 212 BÀI THỰC HÀNH SỐ 213 BÀI THỰC HÀNH SỐ 214 BÀI THỰC HÀNH SỐ 215 BÀI THỰC HÀNH SỐ 215 BÀI THỰC HÀNH SỐ 216 BÀI TẬP NÂNG CAO 218 BÀI TẬP LỚN 225 DANH SÁCH HÌNH 228 TRA CỨU TỪ KHÓA 229 C++ TÀI LIỆU THAM KHẢO 230 Trang |6 CuuDuongThanCong.com https://fb.com/tailieudientucntt (8) GIỚI THIỆU Cấu trúc giáo trình Gi|o trình chia làm 17 chương và chương chia làm các mục khác C|c chương xếp theo trình tự từ lập trình hướng thủ tục trên C++ đến lập trình hướng đối tượng và các lớp thư viện Độc giả có thể truy cập vào mục bất kì từ phần phụ lục nằm đầu sách Nhiều mục bao gồm các ví dụ để mô tả cách sử dụng Tôi khuyên các bạn nên đọc các ví dụ này và có thể hiểu đoạn mã chương trình trước đọc chương Một cách thức tốt để tăng lượng kiến thức nhận đó l{ h~y chỉnh sửa, bổ sung mã lệnh dựa trên ví dụ mẫu, theo hướng tư của thân, để từ đó có thể hiểu c|ch đầy đủ nội dung mà ta tiếp thu Sau đọc xong giáo trình, tôi còn cung cấp số bài tập thực hành đề nghị để độc giả nên thử nghiệm Hãy giải bài tập này, chúng hữu ích và giúp các bạn cố lại kiến thức môn học hiểu sâu sắc phần lý thuyết Một điều mà độc giả cần lưu ý: h~y đọc trang cuối cùng s|ch, để nắm số thuật ngữ anh-việt tương ứng sử dụng giáo trình này Tôi có gắng sử dụng tên gọi phù hợp với đại đa số các giáo trình hành Tuy nhiên, độc giả nên nắm các thuật ngữ tiếng anh tương ứng, để có thể tham khảo thêm các tài liệu chuyên môn tiếng anh Một vài chú ý tương thích C và C++ Chuẩn ANSI-C++ tổ chức tiêu chuẩn quốc tế thống đưa Nó chính thức mắt v{o th|ng 11 năm 1997 v{ duyệt lại vào Trang |7 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Khi viết giáo trình này, tôi không thể tránh khỏi sai sót Rất mong đóng góp ý kiến quý báu các bạn độc c|c bạn đồng nghiệp Mọi đóng góp xin liên hệ theo địa email: dnhthanh@hueic.edu.vn Hi vọng với các ý kiến đóng góp các bạn, giáo trình này ngày càng hoàn thiện (9) năm 2003 Tuy nhiên, ngôn ngữ C++ đ~ tồn trước đó thời gian kh| d{i (v{o năm 1980) Trước đó, có nhiều trình dịch không hỗ trợ c|c tính bao gồm chuẩn ANSI-C++ Giáo trình này xây dựng trên c|c chương trình dịch đại hỗ trợ đầy đủ chuẩn ANSI-C++ Tôi đảm bảo các ví dụ hoạt động tốt độc giả sử dụng trình dịch hỗ trợ ANSI-C++ Có nhiều chọn lựa, có thể là miễn phí các phần mềm thương mại Trong giáo trình này, tôi giới thiệu đến các các bạn hai công cụ biên dịch C++ là GCC MinGW – miễn phí và Visual C++ - thương mại Trình biên dịch http://ftp.jaist.ac.jp/pub/eclipse/technology/epp/downloads/release/helios/R/eclipse-cpp-helioswin32.zip http://nchc.dl.sourceforge.net/project/mingw/Automated%20MinGW%20Installer/mingw-getinst/mingw-get-inst-20100831/mingw-get-inst-20100831.exe Trang |8 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Các ví dụ gi|o trình n{y xây dựng chủ yếu trên chế độ console (m{n hình DOS) Điều đó có nghĩa l{ nó sử dụng chế độ văn để hiển thị các kết Mọi trình dịch C++ hỗ trợ chế độ dịch console Với môi trường phát triển tích hợp IDE cho C++ miễn phí, chúng ta có thể sử dụng chương trình Codeblocks Eclipse Chúng là các môi trường phát triển tích hợp hỗ trợ soạn thảo và biên dịch C++ Chúng hỗ trợ môi trường GCC để biên dịch C và C++ Với CodeBlocks, chúng ta có thể tải phần mềm địa http://www.codeblocks.org/downloads Đối với Eclipse, nó là trình soạn thảo và biên dịch ngôn ngữ lập trình chuyên nghiệp ho{n toàn miễn phí (vì ta có thể cấu hình kết hợp với các công cụ biên dịch kh|c để tạo môi trường phát triển tích hợp cho các ngôn ngữ lập trình khác nhau) Chúng ta có thể dùng nó để soạn thảo và biên dịch Java, PHP, JSP, Python… v{ hiển nhiên là C/C++ Đ}y l{ dự án mã nguồn mở, tiêu tốn hàng triệu đôla IBM Để tải thời điểm n{y (năm 2010) l{ Eclipse Helios, ta có thể truy cập đến địa bên dưới1 Đối với Eclipse, chúng ta nên sử dụng kết hợp với trình biên dịch C++ là MinGW, nó l{ dự án mở Chúng ta có thể tải địa bên dưới2 Với Eclipse, thì công việc cấu hình ban đầu tương đối phức tạp Nhưng nó l{ trình soạn thảo tuyệt vời Ta có thể sử dụng nó để soạn thảo nhiều ngôn ngữ lập trình c|ch c{i đặt thêm plugin hỗ trợ Nhiều nhà phát triển đ~ sử dụng Eclipse làm tải (10) cho việc phát triển các ứng dụng mình: Embarcadero sử dụng nó để phát triển JBuider, Adobe sử dụng nó để phát triển Flash Buider và nhiều các hãng phần mềm tiếng khác Nếu là lập trình viên Java, Eclipse là lựa chọn không thể bỏ qua Nếu phát triển Flash theo dự án mã nguồn mở từ Adobe, Eclipse l{ lựa chọn hoàn hảo Nếu phát triển C/C++, với các trình soạn thảo thì Eclipse l{ lựa chọn không tồi Việc sử dụng thành thạo Eclipse là lợi cho chúng ta tiến hành nghiên cứu Java, lập trình Web, Flex, Python… sau này C++ Bên cạnh đó, chúng tôi giới thiệu môi trường phát triển tích hợp IDE Microsoft Visual Studio Đ}y l{ trình biên dịch thương mại và là trình biên dịch chuyên nghiệp và tiếng trên hệ điều hành Windows Ta có thể sử dụng để phát triển các ứng dụng trên NET các ứng dụng Win32 Nếu muốn phát triển các ứng dụng theo hướng Microsoft, ta nên sử dụng Visual Studio Phiên đến thời điểm này là VS 2010 Nhưng cần lưu ý rằng, nghiên cứu Visual C++, hãy chọn lựa phiên dành cho Win32 mà không phải là ứng dụng CLI (common language infrastructure) nó phát triển trên NET Và Visual C++ for NET có số khác biệt so với Visual C++ for Win32 Trang |9 CuuDuongThanCong.com https://fb.com/tailieudientucntt (11) MÔI TRƯỜNG PHÁT TRIỂN TÍCH HỢP IDE CodeBlocks Trước tiên, chúng ta tìm hiểu cách tạo dự án, biên dịch tập tin C++ trên CodeBlocks Độc giả cần lưu ý rằng, CodeBlocks tổ chức công việc theo các dự án Chúng ta có thể biên dịch tập tin cpp c|ch đơn lẻ Tuy nhiên, làm việc theo dự án giúp ích cho chúng ta nhiều làm việc với tác vụ lớn Đầu tiên chúng ta khởi động codeblocks, sau đó v{o File > New > Project Trong hộp thoại ra, chúng ta chọn console application (Hình 1) Và nhấp Go, sau đó nhấp Next Trong hộp thoại tiếp theo, ta chọn C++ và nhấp Next Hình – Tạo dự án CodeBlocks Hộp thoại yêu cầu điền thông tin dự án xuất H~y điền tên dự án, vị trí lưu trữ dự |n Sau đó nhấp Next Cuối cùng nhấp Finish Nếu ta muốn bổ sung các tập tin khác các lớp đối tượng, ta có thể bổ sung chúng từ menu File > New T r a n g | 10 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong cửa sổ quản lý dự án, ta nhấp đôi chuột vào tệp main.cpp Nội dung soạn thảo nhập vào tập tin này (12) Biên dịch chương trình: + Nhấp vào Build > Build and Run + Hoặc nhấp phím F9 Tự động định dạng mã Khi viết chương trình C++ hay bất kì chương trình trên ngôn ngữ lập trình nào khác, ta cần tuân thủ quy phạm định dạng mã nguồn Có nhiều chuẩn mực cho c|c định dạng mã nguồn: chuẩn Hungary, chuẩn lạc đ{ Dù rằng, chúng không ảnh hưởng đến việc biên dịch chương trình, chúng giúp người đọc có thể dễ hiểu chương trình chúng ta Nếu ta không nắm vững các quy phạm này thì có thể sử dụng chức định dạng mã nguồn tự động CodeBlocks Hãy kích chuột phải vào vùng soạn thảo, sau đó chọn Format this file (Astyle) Tự động khởi tạo phần thân các phương thức lớp Để hỗ trợ cho việc soạn thảo, CodeBlocks hỗ trợ chức khởi tạo nhanh mã nguồn Để khởi tạo nhanh phần khai b|o th}n phương thức lớp từ khai báo prototype nó, chúng ta đặt trỏ chuột vào sau khai báo lớp (tức vị trí chèn khai b|o th}n phương thức), sau đó, kích chuột phải, chọn Insert > All class methods without implementation C++ Trong hộp thoại ra, hãy chọn phương thức muốn khởi tạo phần th}n tương ứng, sau đó, nhấp Ok Hình – Khởi tạo th}n phương thức T r a n g | 11 CuuDuongThanCong.com https://fb.com/tailieudientucntt (13) Eclipse Helios Sau tải xong Eclipe Helios máy, hãy tiến hành giải nén tập tin Chương trình Eclipse không yêu cầu chúng ta phải c{i đặt, nó có thể làm việc trên máy tính đ~ c{i máy ảo Java Để tải máy ảo Java, chúng ta có thể truy cập vào trang chủ Sun (nay là Oracle) địa sau đ}y3 Để xây dựng chương trình C/C++ trên Eclipse, chúng ta cần: - Eclipse Helios for C/C++ (nếu phiên tải là dành cho Java, ta cần phải c{i đặt thêm plugin hỗ trợ); có thể sử dụng ấn cũ Eclipse Galileo, Europa… - Công cụ biên dịch GCC – MingW - Máy ảo Java JVM Các bước cấu hình trên Eclipse Helios Bước C{i đặt máy ảo Java Bước Cài MinGW Bước Giải nén Eclipse Helios, sau đó khởi động nó (nhấp vào tập tin eclipse.exe) Thông thường, Eclipse tự động cấu hình MinGW giúp ta Nếu không, hãy thực bước Bước Vào menu Project > Properties Trong hộp thoại xuất hiện, hãy chọn C/C++ Build > Settings Hình – Cấu hình MinGW Eclipse Helios http://javadl.sun.com/webapps/download/AutoDL?BundleId=41723 T r a n g | 12 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong thẻ Tool Settings, ta chọn GCC Assembler > General Sau đó, nhấp vào biểu tượng có dấu cộng mầu xanh Hộp thoại sau ra: (14) Hình – Chọn đường dẫn đến thư mục bin MinGW Ta tiến hành hãy nhập tên đường dẫn đến thư mục bin MinGW (hoặc nhấp v{o nút File system để duyệt đến thư mục này) Mặc định c{i đặt, thư mục này là C:\MinGW\bin Sau đó nhấp Ok Vậy là công việc cấu hình đ~ ho{n tất Xây dựng dự án đầu tiên trên Eclipse Cũng giống CodeBlocks, Eclipse tổ chức chương trình theo dự án Để tạo dự án Eclipse, chúng ta có ba cách: - Vào File > New > C++ Project - Vào biểu tượng tạo dự án trên công cụ, chọn C++ Project - Kích chuột phải vào cửa sổ Project Explorer > chọn New > C++ Project Tiếp đến, hộp thoại sau đ}y xuất Sau đó, hãy nhập vào tên dự án và nhấp Next (nếu chưa cấu hình MinGW), nhấp Finish (nếu đ~ hoàn tất việc cấu hình) Hình - Tạo dự án T r a n g | 13 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong hộp thoại này, chọn dự án khả thi (executable), hãy chọn executable Ta có thể chọn thư viện dll (static library)… Tương ứng với dự án khả thi, chúng ta có thể chọn Empty Project Hello World C++ Project Đối với Empty Project, nó tạo dự án trống Ngược lại với Hello World C++ Project, ta nhận file cpp chứa nội dung mà chúng ta thảo luận chương (15) Tạo file nội dung Eclipse Một chương trình C++ thường chia làm hai loại tệp: cpp và h Tệp cpp thường chứa nội dung chương trình, tệp h thường chứa các khai báo Lời khuyên trước tạo các file: hãy tạo thư mục chung để chứa toàn nội dung sau này, tôi tạm gọi thư mục n{y l{ thư mục src Trong thư mục src, hãy tạo hai thư mục, thư mục cpps và thư mục headers Thư mục cpps chứa toàn tệp cpp, thư mục headers chứa toàn tệp h Tệp Main.cpp chứa hàm main đặt thư mục src Nghĩa l{ ta có cấu trúc tương tự sau: src cpps headers Main.cpp Hiển nhiên ta hoàn toàn không thiết phải thực theo cấu trúc thư mục n{y Tuy nhiên điều này làm cho dự án ta trở nên sáng sủa nhiều Chúng ta có thể bổ sung thêm c|c thư mục phụ kh|c, nên tu}n thủ cấu trúc cây này (ví dụ cần phân biệt các tập tin cpp thành nhiều loại kh|c nhau, thì thư mục cpps, hãy tạo thêm c|c thư mục kh|c…) Hình - Cấu trúc thư mục dự án Biên dịch dự án Để biên dịch dự án, hãy nhấp vào biểu tượng sau đ}y trên thay công cụ Eclipse Hình - Biên dịch dự án C++ Chọn Run As > Local C/C++ Application Một số thủ thuật giúp soạn thảo nhanh T r a n g | 14 CuuDuongThanCong.com https://fb.com/tailieudientucntt (16) Eclipse chứa đựng tập các tiện ích giúp chúng ta soạn thảo nhanh hơn, ít phát sinh lỗi Sau đ}y, tôi xin giới thiệu vài tính giúp các chúng ta soạn thảo nhanh Tạo lớp Vào New > Class Hộp thoại sau đ}y Hình - Hộp thoại tạo class Trong hộp thoại này, cần lưu ý: source folder – thư mục chứa tập tin tạo (thường phân tách thành tệp h và cpp), namespace – phạm vi tác dụng nó namespace định, class name – tên lớp tạo mới, base class – tên lớp cha mà nó thừa kế (bấm vào nút add để chọn các lớp tồn tại), constructor và destructor – cho phép khởi tạo hàm tạo và hàm hủy Chúng ta tìm hiểu khái niệm này làm quen với lập trình hướng đối tượng Nếu khai báo lớp, cùng với các thuộc tính nó, thay vì sử dụng hàm tạo để thiết lập giá trị ban đầu, ta có thể dùng hàm setter; để tiếp nhận giá trị từ các thuộc tính, ta có thể dùng các hàm getter Tôi giới thiệu chi tiết c|c phương thức này phần lập trình hướng đối tượng Trong phần này, tôi hướng dẫn cách tạo chúng thao tác nhấp chuột Vào menu Source, chọn Generate Getters and Setter Trong hộp thoại T r a n g | 15 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Tạo nhanh các phương thức Getter và Setter (17) ra, hãy chọn các thuộc tính cần tạo phương thức getter và setter, sau đó nhấp Ok Một số phím tắt khác Phím tắt Ctrl+Space main – Ctrl+Space Ctrl+Shift+F Ctrl+/ Công dụng Bật chế độ gợi nhắc lệnh Khởi tạo nhanh hàm main Định dạng nhanh mã nguồn Comment vùng m~ đ~ bôi đen, vùng bôi đen đ~ chế độ comment, thì dấu comment bị hủy bỏ Tab Dịch toàn nội dung bị bôi đen sang phải tab Shift+Tab Dịch toàn nội dung bị bôi đen sang tr|i tab Ctrl+1 Chỉnh sửa nhanh toàn các từ giống với từ bôi đen Sau chỉnh sửa xong, nhấp Enter để kết thúc Ctrl+Shift+/ Tạo khối comment cho vùng văn đ~ bị bôi đen Ctrl+Shift+\ Hủy bỏ vùng văn bị comment khối comment Trên đ}y, tôi đ~ giới thiệu sơ qua hai chương trình soạn thảo miễn phí để lập trình C/C++: CodeBlocks và Eclipse Với CodeBlocks, cần tải và cài đặt Môi trường hỗ trợ biên dịch GCC đ~ tích hợp sẵn Với Eclipse, ta phải thực cấu hình để kết hợp với trình biên dịch GCC Nếu l{ người có nhiều trải nghiệm máy tính, thì nên chọn Eclipse nó l{ chương trình soạn thảo chuyên nghiệp Nếu l{ người tiếp xúc máy tính, hãy chọn CodeBlock vì c{i đặt đơn giản Visual Studio 2010 là môi trường biên dịch tích hợp Microsoft Nó là trình biên dịch tốt nhất, đại trên hệ điều hành Windows Chúng ta có thể sử dụng nó để biên dịch C++, C#, Visual Basic, J# Ta tìm hiểu Visual Studio theo hướng tiếp cận với C++ Một điều cần lưu ý, với phiên 2010 này, Visual Studio có hai phiên dành cho C++: C++ for Net và C++ for Win32 Chúng ta tìm hiểu tính C++ for Win32 Trong nội dung giáo trình này, ta xây dựng các ứng dụng Console trên T r a n g | 16 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Visual Studio 2010 dành cho Visual C++ (18) Win32 mà không thảo luận thêm Visual C++ for Net vì nó thuộc phạm trù tương đối khác so với Visual C++ for Win32 Khởi động Visual Studio 2010 Để khởi động VS 2010, ta có thể thực hai cách sau: Nhấp đối chuột vào biểu tượng VS 2010 trên Desktop Vào Start > All Programs > Microsoft Visual Studio 2010, chọn biểu tượng VS 2010 Hình - Giao diện tổng thể Visual Studio 2010 Tạo dự án VS 2010 Trong hộp thoại xuất hiện, chúng ta chọn Win32 Console Application T r a n g | 17 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Cũng Eclipse, VS quản lý theo các workspace và các dự án Trong VS, workspace gọi là Solution Trong workspace có thể chứa nhiều dự án Nếu chưa tạo dự án nào, thì tạo dự án, workspace tự động tạo Để tạo dự án, ta vào File > New Project (hoặc tổ hợp phím tắt Ctrl+Shift+N) (19) Hình 10 - Tạo dự án Win32 Console Mục name: hãy nhập tên dự án mà cần tạo Mục Location: nhấp vào nút Browse để chọn vị trí lưu trữ Mặc định, Visual Studio lưu trữ dự án thư mục Documents Mục Solution name: tạo thư mục thư mục dự án, hay tạo trực tiếp thư mục dự án Hộp thoại Hình 12 Hình 11 - Win32 Application Wizard + Windows application: tạo ứng dụng winform + Console application: tạo ứng dụng chạy trên DOS T r a n g | 18 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Nhóm Application Type (20) + Dll: tạo thư viện dll + Static library: tạo thư viện tĩnh Nhóm Add common header file + Alt: tạo header từ lớp thư viện Alt + MFC: tạo header từ lớp thư viện MFC Nhóm Additional options + Empty project: tạo dự án rỗng không có tập tin + Export symbols: xuất các biểu tượng + Precompiled header: tạo tập tin tiêu đề tiền biên dịch Hãy chọn Console Application và chọn Empty Project Sau đó, nhấp Finish Tạo các tập tin dự án Trong cửa sổ Solution Explorer, hãy kích chuột phải và chọn Add: - Nếu tập tin đ~ tồn tại, hãy chọn Add Existing Items Sau đó, chúng ta duyệt đến vị trí tồn tập tin - Nếu tập tin chưa tồn tại, hãy chọn Add New Items Trong cửa sổ xuất hiện, tùy thuộc vào tập tin mà chúng ta cần, hãy chọn loại tương ứng Thông thường, dự án C++, chúng ta sử dụng hai tập tin l{ tiêu đề h và th}n chương trình cpp Sau đó, h~y nhập tên tập tin và nhấp Ok Tệp tin tiêu đề h thường chứa các khai báo prototype hàm lớp Ngoài ra, nó có thể chứa các hàm macro, các khai báo và biến toàn cục sử dụng toàn chương trình Tập tin cpp thường chứa phần thân các hàm lớp Khi làm việc với các dự án C++, chúng ta nên tách chương trình th{nh nhiều phần và nên sử dụng các tệp tiêu đề để làm cho chương trình gọn gàng và dễ hiểu C++ Sau chọn tập tin cần tạo, hãy nhập tên tập tin, sau đó nhấp nút Add Tập tin bổ sung vào dự án T r a n g | 19 CuuDuongThanCong.com https://fb.com/tailieudientucntt (21) Hình 12 - Bổ sung thêm tập tin - Add Class: bổ sung các lớp đối tượng cho dự án Ở đ}y, chúng ta chọn C++ class Hình 13 - Bổ sung thêm lớp đối tượng C++ Nhập Add Cửa sổ sau đ}y xuất T r a n g | 20 CuuDuongThanCong.com https://fb.com/tailieudientucntt (22) Hình 14 - Tạo lớp Class Wizard - Class name: tên lớp - h file: tên tiêu đề lớp l{ tên tập tin tiêu đề - cpp file: tên tập tin cpp tương ứng với lớp - Base class: lớp tạo thừa kế từ lớp khác, hãy nhập tên lớp sở v{o đ}y - Access: mức thừa kế lớp tạo từ lớp sở - Virtual destructor: tạo phương thức hủy ảo - Inline: tạo phương thức inline Tuy chúng ta có thể sử dụng từ khóa n{y, chế làm việc Visual C++ là tự động bổ sung inline biên dịch phương thức cho là phù hợp để sử dụng inline Điều đó có nghĩa l{ chúng ta không cần dùng đến từ khóa này Biên dịch dự án - Để biên dịch toàn dự án mà không thực thi dự án, chúng ta vào Build, chọn Build Solution Một số phím tắt Visual Studio 2010 T r a n g | 21 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Để biên dịch và thực thi dự án, chúng ta nhấp vào Debug > Start Debugging (hoặc Start without Debugging) (23) - Tạo vùng comment (chú thích): bôi đen vùng m~ cần tạo chú thích, nhấn tổ hợp Ctrl+K, Ctrl+C - Hủy bỏ vùng comment: bôi đen vùng m~ đ~ comment, nhấn tổ hợp Ctrl+K, Ctrl+U - Định dạng mã nguồn: bôi đen vùng m~ cần định dạng, nhấn tổ hợp Ctrl+K, Ctrl+F - Tự động hoàn tất mã và gợi nhắc lệnh: tổ hợp Ctrl+Space Visual Studio 2010 không hỗ trợ c|c tính mạnh mẽ cho việc khởi tạo mã nguồn Tuy nhiên, chúng ta mong muốn làm việc đơn giản và hiệu thì ta có thể sử dụng tiện ích bổ trợ Một tiện ích làm việc khá hiệu là Visual Assist Phiên thời điểm n{y (năm 2010) là 10.6 Xem biểu đồ lớp Hình 15 - Xem biểu đồ lớp T r a n g | 22 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Để quan sát biểu đồ lớp VS 2010, ta nhấp chuột phải vào tên dự án (trong cửa sổ Solution Explorer), chọn Show class diagram Sau đó, chúng ta kéo thả các lớp đối tượng vào vùng biểu đồ (24) Chương Cơ C++ CHƯƠNG CƠ BẢN VỀ C++ Cấu trúc chương trình C++ Một cách thức tốt để học lập trình đó l{ h~y thử viết chương trình đầu tiên Nếu chúng ta đ~ làm quen với ngôn ngữ lập trình n{o đó, thì hẳn biết đến ví dụ kinh điển ngôn ngữ lập trình đó l{ chương trình “Hello, world !” Mã chương trình [1.] [2.] [3.] [4.] [5.] [6.] [7.] [8.] //my first program #include <iostream> using namespace std; int main() { cout<<”Hello, world !”; return 0; } Kết Hello, world ! [1.] Các kí tự nằm sau dấu // không biên dịch m{ nó hiểu là dấu comment (dòng chú thích) Trong C++ C, việc chú thích trên dòng đặt sau dấu // Nếu muốn tạo chú thích nhiều dòng, chúng ta có thể sử dụng dấu /* Tạo chú thích đây */ [2.] Dòng này bắt đầu kí tự #include Tiếp đến là tên tập tin tiêu đề (chứa c|c thư viện) Thư viện iostream đặt dấu <> Nó chứa các hàm xuất nhập Hàm này là phần namespace std [3.] Trong C++, các thành phần thư viện chuẩn khai báo namespace Ở đ}y l{ namespace std Để có thể truy xuất đến các thành phần nó, chúng ta mô tả nó từ khóa using Trong thư viện chuẩn C++, đối tượng cout tổ chức namespace std [4.] Bất kì chương trình C++ n{o phải có h{m main để thực thi chương trình Một hàm khai báo theo cấu trúc trên Từ khóa int mô tả kiểu liệu mà hàm trả là integer Chúng ta cần lưu ý rằng, chương trình C thì ta có thể tùy ý khai báo là void int, C++ thì bắt buộc phải khai báo là int Vậy int hay void T r a n g | 23 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Giải thích chương trình: (25) Chương Cơ C++ trường hợp này có thực quan trọng ? Chúng ta nên luôn khai báo hàm main có kiểu liệu trả là kiểu int Sở dĩ là vì hàm main trả kiểu int thì theo quy ước, chương trình có lỗi, nó trả m~ int kh|c v{ ngược lại, chương trình không có lỗi, nó trả mã int Lỗi đ}y l{ lỗi chương trình liên quan đến quá trình biên dịch, không phải là lỗi liên quan đến cú pháp Chúng ta nhận thấy mã mà nó trả dòng thông báo cuối cùng biên dịch: process returned (0x0) Tên hàm là main Tiếp theo là cặp dấu ngoặc đơn dùng để chứa tham số đính kèm Thông thường chương trình ứng dụng chứa hai tham số hàm main là int argc và char* args[] Các tham số này gọi là tham số dòng lệnh Tiếp theo là dấu {} Bên cặp dấu n{y l{ chương trình chính [5.] Dấu mở khối [6.] Đối tượng cout (đọc là C-out) là chuẩn dùng để xuất liệu màn hình Chúng ta cần lưu ý hàm printf hoạt động tốt trường hợp này Nếu dùng hàm printf thì ta không cần khai b|o thư viện iostream và namespace std trên Khi sử dụng đối tượng cout, chúng ta có thể bỏ qua dòng lệnh [3.] v{ thay v{o đó ta viết std::cout Khi sử dụng đối tượng cout, chúng ta có thêm cách thức để xuống dòng thay vì dùng \n, đó l{ endl Đối tượng cout thường với toán tử xuất << Chúng ta có thể sử dùng nhiều toán tử này muốn xuất nhiều phần tử riêng biệt: cout<<string1<<string2<<….<<endl [7.] Câu lệnh return dùng để trả giá trị hàm main Nếu hàm có trả giá trị, thì cần return giá trị n{o đó cùng kiểu liệu trả với hàm Nếu hàm là void, thì không cần return [8.] Dấu đóng khối tương ứng với mở khối [5] Cũng C, C++ l{ ngôn ngữ phân biệt chữ hoa và chữ thường Kết thúc dòng lệnh C++ phải có dấu ; Một dấu ngoặc đơn (), dấu ngoặc nhọn {} song h{nh Điều đó có nghĩa dùng dấu mở thì phải có dấu đóng tương ứng Dấu ngoặc đơn thường dùng sau tên hàm, và bên nó là tham số hình thức các lệnh có cấu trúc Dấu ngoặc nhọn thường dùng để quy định phạm vi khối lệnh (scope) Một cách thức giúp chúng ta chuyên nghiệp lập trình, là sau dấu mở, ta nên sử dụng tiếp T r a n g | 24 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chú ý: (26) Chương Cơ C++ dấu đóng (thông thường các trình soạn thảo hỗ trợ cách tự động) Sau đó h~y nhập nội dung cần thiết vào bên cặp dấu này Điều đó tránh khỏi nhầm lẫn chương trình có qu| nhiều dấu đóng mở Để nhận biết phạm vi ảnh hưởng các khối lệnh – hãy sử dụng phím tab để tạo lồi lõm viết m~ chương trình Như ví dụ trên, đối tượng cout và hàm return nhảy vào tab so với dấu khối lệnh tương ứng Đừng tiết kiệm sử dụng phím tab và phím enter Nếu sử dụng hợp lí, chương trình sáng sủa và dễ đọc C++ Bài tập 1 Hãy viết chương trình in dòng chữ “Chao ban, ban co khoe khong” Hãy viết chương trình in hai dòng chữ trên hai dòng phân biệt “Vietnam” v{ “Hoa ky” Hãy viết chương trình in tam gi|c với c|c đỉnh là các dấu * * * * T r a n g | 25 CuuDuongThanCong.com https://fb.com/tailieudientucntt (27) Chương Biến và Các kiểu liệu CHƯƠNG BIẾN VÀ CÁC KIỂU DỮ LIỆU Tương ứng với chương trình “Hello world”, chúng ta cần thảo luận vài chi tiết Chúng ta có vài dòng lệnh, biên dịch chúng v{ sau đó chạy chương trình để thu kết Dĩ nhiên ta có thể l{m nhanh hơn, nhiên việc lập trình không đơn l{ in c|c dòng thông b|o đơn giản lên m{n hình Để xa hơn, chúng ta viết chương trình thực thi tác vụ hữu ích là giúp chúng ta tìm hiểu khái niệm biến Giả sử có hai giá trị và Ta cần lưu hai gi| trị này vào nhớ Bây giờ, tôi muốn cộng thêm vào số thứ v{ lưu lại giá trị này cho nó, tôi muốn lấy hiệu số thứ sau thay đổi với số thứ hai Tiến trình xử lý công việc trên có thể viết trên C++ sau: Chương trình int a = 5; int b = 2; a = a + 1; // a=6 int result = a – b; //result = Biến dùng để lưu giá trị và nó có thể thay đổi Một biến quy định kiểu liệu nào đó Trong trường hợp ví dụ chúng ta, biến có kiểu liệu là int Kiểu liệu thường có hai loại: kiểu liệu nguyên thủy (primitive data type) và kiểu liệu tham chiếu (reference data type) Chúng ta thảo luận chi tiết chúng phần Nhưng ta có thể hiểu rằng, kiểu liệu đơn giản và có cấu trúc C là kiểu liệu nguyên thủy Đối với kiểu liệu tham chiếu, tôi giới thiệu phần lập trình hướng đối tượng C++ Từ khóa C++ có thể có nhiều từ Nếu từ khóa có nhiều từ, thì các từ có dấu gạch chân (_) Kí tự trắng và các kí tự đặc biệt không phép sử dụng từ khóa, tên hàm, tên biến Tên chúng không bắt đầu kí tự số T r a n g | 26 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Từ khóa (28) Chương Biến và Các kiểu liệu Bảng từ khóa chuẩn C++ asm, auto, bool, break, case, catch, char, class, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int ,long, mutable, namespace, new, operator, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch, template, this, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while Bảng từ khóa bổ sung C++ and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq Kiểu liệu nguyên thủy Khi lập trình, chúng ta lưu c|c biến nhớ m|y tính, máy tính cần phải biết loại liệu mà chúng ta muốn lưu Điều này giúp bảo đảm đủ số lượng ô nhớ cần thiết để lưu liệu Tên char Mô tả Kí tự số nguyên bé short Số nguyên ngắn int Số nguyên long Số nguyên dài long long Số nguyên cực dài bool float double long Giá trị logic – true/false Số thập phân Số thập phân chấm động Số thập phân chấm động Kích thước Vùng giá trị byte signed: -128 ->127 unsigned: -> 255 byte signed: -215 -> 215-1 unsigned: -> 216-1 byte signed: -231 -> 231-1 unsigned: -> 232-1 byte signed: -231 -> 231-1 unsigned: -> 232-1 byte signed: -263 -> 263-1 unsigned: -> 264-1 byte true và false byte số thập phân byte 15 số thập phân byte 15 số thập phân T r a n g | 27 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong máy tính, nhớ tổ chức theo các byte Một byte là đơn vị đo lường tối thiểu mà chúng ta có thể quản lý C++ Một byte có thể lưu biến char Thêm v{o đó, m|y tính quản lý kiểu liệu phức tạp Bảng sau đ}y liệt kê các kiểu liệu v{ kích thước tương ứng (29) Chương Biến và Các kiểu liệu double dài wchar_t Kí tự dài 2/4 byte Kích thước nhớ và miền giá trị các kiểu liệu còn phụ thuộc vào hệ thống v{ chương trình dịch tương ứng Giá trị đưa đ}y l{ trên hệ thống Windows 32 bit và trình dịch GCC MinGW Nhưng hệ thống khác, các giá trị này có thể thay đổi (ví dụ kiểu int và long trên Windows 32 bit v{ 64 bit l{ byte, trên Linux 32 bit l{ byte v{ trên Linux 64 bit là byte) Khai báo biến Như ví dụ trên, ta thấy rằng, muốn sử dụng biến C++, ta cần khai báo biến với kiểu liệu mà ta mong muốn Cấu trúc khai báo <Tên kiểu liệu> <Tên biến>; Ví dụ int a; //Khai báo biến a kiểu nguyên float mynumber; //Khai báo biến mynumber kiểu float bool istrue; //Khai báo biến istrue kiểu bool long num1, num2, num3; //Khai báo ba biến num1, num2, num3 cùng kiểu long Chú ý: Nếu khai báo biến thuộc các kiểu nguyên mà ta không sử dụng khai báo có dấu (signed) không dấu (unsigned), thì chương trình dịch mặc định quy định là kiểu nguyên có dấu int mynum; Đối với kiểu char thì có ngoại lệ Chúng ta nên khai b|o tường minh là signed char unsigned char Đối với signed int và unsigned int có thể viết đơn giản là signed unsigned T r a n g | 28 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ //tương đương signed int mynum; (30) Chương Biến và Các kiểu liệu Nếu muốn chắn kích thước kiểu liệu mà ta cần sử dụng, hãy sử dụng hàm sizeof để x|c định kích thước nhớ kiểu liệu Hàm sizeof(tên biến) sizeof(kiểu liệu) – trả kiểu liệu nguyên Chương trình Kết #include <iostream> (trên windows 32 bit) using namespace std; int main() { int a; cout<<sizeof(a); //Hoặc có thể viết cout<<sizeof(int); } Phạm vi tác dụng biến Tất các biến mà tôi giới thiệu đ}y sử dụng chương trình cần phải khai báo với kiểu liệu định Một biến khai báo khối lệnh nào, thì nó có tác dụng khối lệnh đó Biến khai báo theo kiểu này gọi là biến cục (hoặc biến địa phương) Nếu biến khai báo ngoài tất các khối lệnh (kể hàm main) thì biến đó có t|c dụng toàn chương trình v{ gọi là biến toàn cục [1.] [2.] #include <iostream> using namespace std; [3.] [4.] [5.] int a; char c; unsigned int d; [6.] [7.] int main() {//Khối lệnh C++ Chương trình T r a n g | 29 CuuDuongThanCong.com https://fb.com/tailieudientucntt (31) Chương Biến và Các kiểu liệu [8.] [9.] [10.] [11.] [12.] [13.] [14.] [15.] signed long m; float n; {//Khối lệnh double x; x = 1; cout<<x; } } Giải thích: Các biến khai báo c|c dòng [3.], [4.] v{ [5.] khai báo ngoài khối lệnh, nó có tác dụng toàn chương trình v{ nó gọi là biến toàn cục (global variable) Các biến khai báo khối lệnh (tương ứng [8.] và [9.]) và khối lệnh (tương ứng [11.] và [12.]) gọi là biến cục (local variable), nó có tác dụng khối lệnh trực tiếp chứa nó Có nghĩa là biến x có tác dụng khối lệnh 2; biến m, n có tác dụng khối lệnh Các biến toàn cục có thể sử dụng toàn chương trình, nó có thể gọi các hàm, hàm chính main Còn biến cục khai báo khối lệnh nào, thì nó có thể sử dụng khối lệnh đó Trong số tình huống, biến có thể khai báo dấu ngoặc đơn (tình này thường gặp nghiên cứu các lệnh có cấu trúc), thì biến n{y gọi là biến cục for (int a = 0; i<10; i++){ …nhập nội dung… } Lúc này, biến có tác dụng khối lệnh tương ứng (khối lệnh nằm vòng lặp for) Khi biến cục khai báo, giá trị mặc định nó chưa tạo Vì vậy, muốn sử dụng biến, ta cần phải khởi tạo giá trị cho biến Có hai c|ch để khởi tạo giá trị biến C++ T r a n g | 30 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Khởi tạo giá trị cho biến (32) Chương Biến và Các kiểu liệu Cú pháp Ví dụ type tên_biến = giá_trị_khởi_tạo; int a = 0; type tên_biến (giá_trị_khởi_tạo); int a (0); Bây giờ, ta có thể viết lại đoạn chương trình tính to|n gi| trị các biến trên, cách sử dụng giá trị khởi tạo mặc định này Khởi tạo theo cách Khởi tạo theo cách #include <iostream> #include <iostream> using namespace std; using namespace std; int main() int main() { { int a = 5; int a (5); int b = 2; int b (2); a = a + 1; // a=6 a = a + 1; // a=6 int result = a – b; //result = int result (a – b); //result = cout<<result<<endl; cout<<result<<endl; result 0; result 0; } } Bài tập Hãy viết chương trình tương đương, sử dụng hai kiểu khởi tạo trên Hãy chọn cách khởi tạo tùy ý, để viết chương trình tính gi| trị biểu thức delta = b*b-4*a*c, với a, b, c nhận các giá trị 1, 5, Một biến khai báo từ khóa static thì nó khởi tạo giá trị đúng lần biến tạo Thông thường biến n{y đặt vào tệp tiêu đề h để sử dụng cho toàn chương trình Ví dụ sau đ}y T r a n g | 31 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Khởi tạo giá trị cho biến tĩnh static (33) Chương Biến và Các kiểu liệu minh họa cho giá trị biến static không khởi tạo lần thứ hai vòng lặp Chương trình #include <iostream> using namespace std; Kết int main() { for (int i=0; i<5; i++) { static int x = 2; x++; cout<<x<<endl; } } Giải thích: biến x khởi tạo vòng lặp for, không có từ khóa static, thì lần lặp, biến này khởi tạo lại và giá trị in luôn là Tuy nhiên, trường hợp này, ta sử dụng từ khóa static, đó, giá trị biến x khởi tạo lần Trong lần lặp tiếp theo, giá trị x lưu lại Kết ta nhận trên Giới thiệu xâu kí tự Một biến có thể dùng để lưu loại liệu không phải số, nó lại chứa nhiều kí tự (không kiểu char) mà chúng ta gọi nó là kiểu xâu kí tự Khai báo nguyên thủy Khai báo tham chiếu #include <iostream> #include <iostream> using namespace std; #include <string> T r a n g | 32 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong thư viện ngôn ngữ lập trình C++, nó cung cấp cho chúng ta kiểu xâu nằm lớp string Cần lưu ý rằng, để biểu diễn biến thuộc kiểu xâu, chúng ta có thể sử dụng khai báo mảng kí tự, trỏ kí tự ngôn ngữ C, sử dụng khai báo kiểu string Khi sử dụng kiểu khai báo tham chiếu lớp string, ta cần có khai báo tệp tiêu đề là string (34) Chương Biến và Các kiểu liệu int main() using namespace std; { int main() char a[] = “abc”; { char* b = “abc”; string a = “abc”; return 0; return 0; } } Chúng ta cần lưu ý rằng, dù là biến thuộc kiểu liệu tham chiếu string, thì ta có thể sử dụng hai kiểu khởi tạo trên Điều này có thể áp dụng cho kiểu string mà thôi Các kiểu liệu tham chiếu khác không thể sử dụng hai cách khởi tạo này C++ Để biết thêm thông tin kiểu string, các bạn nên tham khảo thêm thông tin lớp string cung cấp mục chương 17 cùng giáo trình này T r a n g | 33 CuuDuongThanCong.com https://fb.com/tailieudientucntt (35) Chương Hằng CHƯƠNG HẰNG Hằng: là phần tử có giá trị cố định Giá trị nó khởi tạo tạo Thông thường, người ta sử dụng các chữ c|i để đặt tên cho Tên không chứa các kí tự đặt biệt, kí tự trắng hay bắt đầu số, không trùng với từ khóa Trong C++, tên thường viết hoa toàn Hằng thường chia làm: số nguyên, số thực, kí tự, xâu và logic Hằng số nguyên Hằng số nguyên là các có giá trị là số nguyên Hằng số nguyên có thể biểu diễn dạng thập phân, bát phân, thập lục phân Nếu số nguyên dạng thập phân thì có giá trị số thập ph}n bình thường Nếu là số nguyên bát phân, thì nó bắt đầu số (ví dụ 011) Nếu là số nguyên thập lục phân, thì nó bắt đầu 0x (ví dụ 0x1b) Các quy tắc chuyển đổi số qua lại các hệ đ~ nghiên cứu học phần “Nhập môn tin học” Nếu số là số nguyên có dấu không dấu, có thể có vài c|ch khai b|o tương ứng Hằng nguyên có dấu và không dấu 75 //int 75u //unsigned int 75l //long 75ul //unsigned long Các tiền tố và hậu tố hai cách sử dụng trên có thể viết thường viết hoa (0x12 hay 0X12 l{ nhau; 75ul hay 75UL l{ nhau) Chúng ta khảo sát số thực dạng số thập phân dấu chấm động dạng khoa học (hay còn gọi là dạng lũy thừa dạng E – tương ứng với lũy thừa 10) Ví dụ 314.159e-2 tương ứng với 3.14159 hay 6.02e23 tương ứng với 6.02*1023 T r a n g | 34 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Hằng số thực có dấu chấm động (36) Chương Hằng Một số thực mặc định là double Nếu muốn định kiểu liệu cho nó, ta có thể sử dụng cú ph|p tương tự số nguyên (3.1415L tương ứng long double, 3.1415F tương ứng với float) Các kí tự e, f, l có thể biểu diễn dạng chữ hoa chữ thường Hằng kí tự và xâu kí tự Hằng kí tự sử dụng dấu nh|y đơn, còn xâu kí tự sử dụng dấu nháy kép x Tên biến ‘x’ Kí tự x “x” Xâu kí tự x Trong xâu kí tự, có thể chứa các kí tự đặc biệt kí tự xuống dòng, đặt tab… Sau đ}y l{ vài kí tự đặc biệt đó v{ ý nghĩa chúng Kí hiệu \n \r \t \v \b \f \a \’, \\ Ý nghĩa Xuống dòng Di chuyển toàn kí tự sau dấu \r đè lên c|c kí tự trước đó Nếu số kí tự sau nhiều số kí tự trước dấu \r, thì kết in là toàn kí tự nằm sau Ví dụ “abc\r1234” -> in 1234, “abc\r12” -> in 12c Đặt tab Đặt tab dọc Đặt backspace Đặt dấu form feed Tạo âm beep \”, \?, Tạo các kí tự ‘, “, ?, \ Một xâu kí tự có thể chứa nội dung trên nhiều dòng Khi đó, để viết nội dung dòng mới, thì cuối dòng trước đó, ta bổ sung thêm kí tự \ Các xâu kí tự có thể ghép với nhờ vào kí tự trắng Ví dụ “Hom toi di hoc\ Xâu kí tự viết trên nhiều dòng “Toi “ “yeu “ “lap trinh” Xâu kí tự ghép T r a n g | 35 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ngay mai toi o nha” (37) Chương Hằng Khi sử dụng xâu kí tự với kiểu liệu là wchar_t, ta cần thêm tiền tố L bên trước xâu kí tự đó Ví dụ L”Xau ki tu wchar_t” Các quy tắc trên có thể áp dụng cho bất kì xâu kí tự thuộc kiểu liệu nào (char*, wchar_t*, string mảng kí tự tương ứng) Hằng logic Hằng logic có hai giá trị là true (đúng) và false (sai) Một biểu thức logic có kiểu liệu là bool Nó có thể nhận hai giá trị true và false Trong C, ta có thể sử dụng kiểu số nguyên (short, int, long…) để quy định giá trị biểu thức logic: giá trị nhận là – tương ứng với giá trị sai; ngược lại, giá trị nhận là khác – tương ứng với giá trị đúng C|ch quy định này còn hoạt động tốt trên C++ Tuy nhiên, C++, người ta đ~ định nghĩa hai số true và false và kiểu liệu bool Hằng số true tương ứng với giá trị và số false tương ứng với giá trị Ta hoàn toàn có thể sử dụng giá trị true (hoặc 1) và false (hoặc 0) Định nghĩa #define Khi định nghĩa tên gọi cho hằng, ta có thể sử dụng nó thường xuyên mà không cần phải xếp lại các biến chi phối nhớ Để định nghĩa hằng, ta cần sử dụng từ khóa define Cú pháp #define tên_hằng giá_trị Ví dụ #define PI 3.14 #define NewLine ‘\n’ Trong ví dụ trên, tôi đ~ định nghĩa hai PI và Newline Trong chương trình, tôi cần gọi nó để sử dụng, mà không cần triệu gọi đến giá trị cụ thể nó Chương trình tính diện tích hình tròn Kết #include <iostream> 3.14 using namespace std; C++ #define PI 3.14 int main() T r a n g | 36 CuuDuongThanCong.com https://fb.com/tailieudientucntt (38) Chương Hằng { double r = 1; double s; s = PI*r*r; cout<<s; return 0; } Thực chất, #define không phải là câu lệnh C++, nó là định hướng tiền xử lý (directive for the preprocessor) Cho nên, ta cần lưu ý rằng, kết thúc phần khai báo này, không có dấu chấm phẩy (;) Khai báo const Để khai báo hằng, ta sử dụng từ khóa const Cấu trúc khai báo sau: Cú pháp const kiểu_dữ_liệu tên_hằng = giá_trị; Ví dụ const int a = 10; const char x = ‘\t’; C++ Trong ví dụ trên, ta có thể thấy cách khai báo tương tự khai báo biến, có điểm khác biệt là ta phải bổ sung từ khóa const v{o trước khai báo này Hằng và biến tương tự Chúng khác điểm là giá trị không thể thay đổi, còn biến thì có thể thay đổi T r a n g | 37 CuuDuongThanCong.com https://fb.com/tailieudientucntt (39) Chương To|n tử CHƯƠNG TOÁN TỬ Chúng ta đ~ l{m quen với biến và hằng, bây chúng ta có thể tính toán giá trị chúng Các phép toán thực thi trên các biến gọi là các toán tử V{ đó, c|c biến đó gọi là các toán hạng Toán tử gán Toán tử g|n dùng để gán giá trị cho biến Ví dụ a = 5; Câu lệnh gán thực gán giá trị bên phải cho biến bên trái Ta có thể gán giá trị hai biến cho Ví dụ a = b; Hãy quan sát và suy ngẫm đoạn chương trình sau: Chương trình [1.] [2.] [3.] [4.] [5.] [6.] [7.] [8.] [9.] [10.] [11.] [12.] #include <iostream> using namespace std; int main() { int a, b; a = 10; b = 4; a = b; b = 7; cout<<”a=”<<a<<”, b=”<<b<<endl; return 0; } Dòng lệnh [5.] khai báo hai biến nguyên a, b Khi đó gi| trị chúng chưa khởi tạo Dòng lệnh [6.] khởi tạo giá trị cho biến a là 10, biến b chưa khở tạo Dòng lệnh [7.] khởi tạo giá trị cho biến b là 4, biến a không thay đổi (10) Dòng lệnh [8.] thực việc gán giá trị biến b cho biến a, đó b không thay đổi; a nhận giá trị b, tức là Dòng lệnh T r a n g | 38 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Giải thích: (40) Chương To|n tử [9.] gán giá trị biến b là 7, biến a không thay đổi Do đó, gi| trị cuối cùng a là 4, b là Output chương trình là a=4, b=7 Chúng ta cần chú ý rằng, toán tử gán thực theo nguyên tắc phảisang-tr|i Nghĩa l{ luôn lấy giá trị vế phải để gán cho vế tr|i Khi đó, gi| trị biến vế tr|i thay đổi, còn vế phải không thay đổi Toán tử gán có thể thực các biểu thức phức tạp a = b + 2; a = a + 1; a = b = c = 5; Giá trị a giá trị b cộng thêm Tăng gi| trị a lên G|n đồng thời nhiều giá trị Nó tương ứng với tập các lệnh sau: c = 5; b = c; a = b; Toán tử thực phép toán số học Toán tử Ý nghĩa + Phép cộng Phép trừ * Phép nhân / Phép chia (chia lấy phần nguyên hai số nguyên) % Chia lấy dư (chỉ với hai số nguyên) Chú ý rằng, phép chia có thể thực trên số nguyên số thực Nếu thực phép chia trên hai số nguyên thì đ}y chính l{ kết phép chia lấy phần nguyên Còn nó thực trên hai số thực (hoặc số thực và số nguyên), thì kết thực trên phép chia bình thường Như vậy, theo mặc định, hai số nguyên (hoặc thực) thực phép to|n tương ứng thì nó trả kết nguyên (hoặc thực) Nếu phép toán thực trên số nguyên và số thực, nó tự động chuyển đổi kiểu cao (th{nh số thực) Vậy làm n{o để thực phép chia cho 2, ta muốn nhận kết là 1.5 Ta biết và là hai số nguyên, ta thực phép chia 3/2 thì ta thu số nguyên – là kết phép chia lấy phần nguyên 3/2, tức là Muốn thu kết 1.5, ta cần chuyển đổi và dạng số thực các cách sau: T r a n g | 39 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ngôn ngữ lập trình C++ hỗ trợ các toán tử số học sau: (41) Chương To|n tử Khai báo và là các số thực (bằng c|ch quy định kiểu liệu float a = 3, float b = 3.0, 2.0) Chuyển đổi kiểu liệu (Xem thêm phần toán tử chuyển đổi kiểu liệu) Toán tử gán hợp Khi muốn thay đổi giá trị biến, chúng ta có thể sử dụng cách viết thông thường, C++ nó hỗ trợ các toán tử viết tắt Toán tử += -= *= /= %= &= |= ^= >>= <<= Ví dụ a+=b a-=b a*=b a/=b a%=b a&=b a|=b a^=b a>>=b a<<=b Ý nghĩa a=a+b a=a-b a=a*b a=a/b a=a%b a=a&b a=a|b a=a^b a=a>>b a=a<<b Phạm vi Phép toán số học Phép toán số học Phép toán số học Phép toán số học Phép toán số học Phép toán bit Phép toán bit Phép toán bit Phép toán bit Phép toán bit Toán tử tăng và giảm Một cách viết thu gọn nữa, đó l{ sử dụng toán tử tăng v{ giảm Nếu biểu thức a+=b, với b = thì ta có thể viết th{nh a++ Tương tự, a-=b, b = thì ta có thể viết a Chúng ta lưu ý rằng, toán tử này có chút khác biệt Nó có thể nằm trước nằm sau toán hạng Có nghĩa l{ có thể có a++ ++a (tương ứng a a) Ý nghĩa Thực phép to|n trước, sau đó thực toán tử Thực toán tử trước, sau đó thực phép toán Tương tự a++; Tương tự ++a; Cách thực thi int a = 1; a = 1, b chưa khởi tạo T r a n g | 40 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Phép toán a++; ++a; a ; a; Ví dụ (42) Chương To|n tử int b = 1; a = 1, b = a+=b++; Thực phép to|n a+=b trước, sau đó thực phép toán b++ Tức là a=2, b=2 Thực phép to|n ++b trước, sau đó thực phép toán a+=b Tức là b=2, a=3 a+=++b; Toán tử so sánh Để thực việc so sánh giá trị hai biến hai biểu thức; ta có thể sử dụng toán tử so sánh Giá trị phép toán so sánh trả kiểu bool Toán tử == != > < >= <= Ví dụ Tên gọi Giá trị biểu thức “a Toán tử b” Đúng Sai Bằng Nếu a b Nếu a khác b Khác Nếu a khác b Nếu a b Lớn Nếu a lớn b Nếu a nhỏ b Bé Nếu a nhỏ b Nếu a lớn b Lớn Nếu a lớn Nếu a nhỏ b bằng b Bé Nếu a nhỏ Nếu a lớn b b Kết #include <iostream> Kết 1: using namespace std; Kết 2: int main() Kết 3: C++ { int a = 1; T r a n g | 41 CuuDuongThanCong.com https://fb.com/tailieudientucntt (43) Chương To|n tử int b =2; cout<<”Kết 1:”<<(a==a); cout<<”Kết 2:”<< (a>=b); cout<<”Kết 3:”<< (a<=b); } Ta cần chú ý ví dụ này, giống C, C++ chấp nhận giá trị và để quy định cho kiểu logic Theo quy ước: true tương ứng với giá trị (hoặc khác 0), false tương ứng với giá trị Mặc dù C++ hỗ trợ kiểu liệu bool, nó dùng số nguyên v{ để biểu diễn tính đúng sai Ta có thể tạm hiểu true và false là hai số đ~ định nghĩa sẵn, tương ứng với và (nhờ vào #define) Nếu mong muốn in giá trị là true/false, ta cần sử dụng định dạng liệu cho đối tượng cout Chi tiết, hãy tham khảo mục 8, chương 17 gi|o trình n{y Chú ý: Hãy sử dụng kiểu liệu bool thay vì dùng kiểu liệu int để biểu diễn tính đúng sai Khi sử dụng kiểu bool, hãy nhớ giá trị đúng tương ứng với true; giá trị sai tương ứng với false (chú ý chúng viết thường) Hãy cẩn thận sử dụng toán tử so sánh Hãy chú ý toán tử so sánh là ==, khác với toán tử gán = Toán tử hội && Phép toán Phép toán ngôi !a Phép toán hai ngôi a&&b Toán tử tuyển || Phép toán hai ngôi a||b Toán tử phủ định ! a true false true true false false true true false false b true false true false true false true false T r a n g | 42 CuuDuongThanCong.com https://fb.com/tailieudientucntt Kết false true true false false false true true true false C++ Toán tử logic (44) Chương To|n tử Ví dụ Kết #include <iostream> Kết 1: using namespace std; Kết 2: int main() Kết 3: { int a = true; int b =false; cout<<”Kết 1:”<<(a&&a); cout<<”Kết 2:”<< (!a&&b); cout<<”Kết 3:”<< !(a||b); } Giải thích: Kết – tương ứng với biểu thức a&&a=a, nghĩa l{ true – Kết – tương ứng với !a&&b !a=false, false&&false=false – Kết – tương ứng với !(a||b), a||b=true||false=true, !(a||b)=!true=false – Bài tập Hãy lập trình kiểm tra tính đúng đắn định luật De Morgan: a !(a||b)=!a&&!b b !(a&&b)=!a||!b Toán tử điều kiện (bt_điều_kiện)?(kết_quả_1):(kết_quả_2); Giải thích: trả giá trị kết_quả_1 bt_điều_kiện l{ đúng, ngược lại, bt_điều_kiện là sai, thì trả giá trị kết_quả_2 Chương trình Kết T r a n g | 43 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Toán tử điều kiện có dạng cú ph|p sau: (45) Chương To|n tử #include <iostream> Max là: using namespace std; int main() { int a = 1; int b = 2; int max = (a>b)?a:b; cout<<”Max l{: “<<max; return 0; } Giải thích: chương trình trên tính gi| trị lớn hai số a và b Toán tử điều kiện kiểm tra điều kiện biểu thức a>b, vì a=1, b=2, nên giá trị nó là false Chính vì vậy, biểu thức điều kiện nhận kết tương ứng với kết 2, tức là b Toán tử điều kiện luôn trả giá trị cụ thể Như ví dụ trên, ta thấy biểu thức a>b đúng, thì gi| trị max nhận là số a; ngược lại là số b Tuy nhiên, không thiết cần phải có giá trị x|c định cho toán tử điều kiện Ví dụ sau đ}y minh họa điều này Nếu muốn sử dụng tập các câu lệnh cặp dấu ngoặc này, ta có thể sử dụng toán tử phân tách đề cập mục Ví dụ sau đ}y cho thấy việc sử dụng tập các câu lệnh bên cặp dấu ngoặc toán tử điều kiện T r a n g | 44 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương trình Kết #include <iostream> lon hon using namespace std; int main() { int a = 1; int b = 2; (a>b)?(cout<<a<<” lon hon”):(cout<<b<<” lon hon”); return 0; } Giải thích: ví dụ minh họa này, toán tử điều kiện không trả giá trị cụ thể nào Nó đơn kiểm tra điều kiện, a>b đúng, thì in câu a lớn hơn, ngược lại in câu b lớn Ta cần lưu ý rằng, các câu lệnh nằm cặp dấu ngoặc toán tử điều kiện, thì kết thúc câu lệnh không có dấu chấm phẩy (;) (46) Chương To|n tử Chương trình Kết #include <iostream> |a-b|=1 using namespace std; int main() { int a = 1; int b = 2; int c; (a>b)?(c = a-b,cout<<”|a-b|=”<<c):( c = b-a,cout<<”|a-b|=”<<c); return 0; } Giải thích: Trong ví dụ n{y, chương trình in giá trị tuyệt đối a-b Nếu a>b, thì giá trị tuyệt đối |a-b| = a-b; ngược lại a<b, thì giá trị tuyệt đối |a-b| = b-a Trong cặp dấu ngoặc đơn toán tử điều kiện, câu lệnh gán c=a-b (hoặc c=b-a) v{ cout phân tách dấu phẩy (,) Một điều cần lưu ý, chúng ta không phép khai báo biến cặp dấu ngoặc đơn này Việc khai báo biến dấu ngoặc đơn, áp dụng cho câu lệnh lặp for Toán tử phân tách Toán tử này kí hiệu là dấu phẩy Nó dùng để phân tách hai hay nhiều biểu thức chứa biểu thức phức hợp tương ứng Toán tử dịch bit Các toán tử n{y sử dụng để tính toán trên các số nguyên cách tính toán trên các bit T r a n g | 45 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ví dụ Kết … int a; int b; int c; c = (a=1, b=2, a+b); cout<<c; … Giải thích: biểu thức phức hợp, bên dấu ngoặc đơn l{ c|c biểu thức đơn phân tách toán tử phân tách Trong dãy các toán tử phân tách, nó ưu tiên thực từ trái sang phải (xem thêm phần độ ưu tiên toán tử trình bày mục sau chương n{y), nghĩa l{ a=1, sau đó b=2 v{ cuối cùng là c=a+b=1+2=3 (47) Chương To|n tử Toán tử ~ Kết Toán tử phủ định bit Các bit chuyển th{nh v{ ngược lại Ví dụ ~101=010 Toán tử hội bit Hội hai bit Trong trường hợp còn lại, ta nhận Ví dụ 1 (tương ứng 11 hệ thập phân) & 1 (tương ứng với hệ thập phân) 0 (tương ứng với hệ thập phân) & Nghĩa l{ 11&5=1 Toán tử tuyển bit Tuyển hai bit Trong trường hợp còn lại, ta nhận Ví dụ | | 1 (tương ứng 11 hệ thập phân) 0 (tương ứng với hệ thập phân) 1 (tương ứng với 11 hệ thập phân) Nghĩa l{ 11|1=11 Toán tử tuyển loại bit Tuyển loại hai bit khác Trong trường hợp còn lại, ta nhận Ví dụ ^ ^ 1 (tương ứng 11 hệ thập phân) 0 (tương ứng với hệ thập phân) 1 (tương ứng với 10 hệ thập phân) Nghĩa l{ 11^1=10 Toán tử dịch bit sang phải Dịch chuyển toàn dãy bit sang phải theo số bit định Nếu là số dương, ta bổ sung các bit vào đầu Nếu là số âm, ta bổ sung các số v{o đầu T r a n g | 46 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ >> (48) Chương To|n tử Ví dụ Đối với số dương … 1 >> … 0 1 (tương ứng 11 hệ thập phân) dịch sang phải bit (tương ứng với hệ thập phân) Nghĩa l{ 11>>1=5 Đối với số âm … 1 1 >> … 1 1 1 (tương ứng -11 hệ thập phân) dịch sang phải bit (tương ứng -3 hệ thập p ân) Nghĩa l{ -11>>2=-3 Các bạn cần lưu ý rằng, các biểu diễn trên, hệ thống chọn là 32 bit, thì chúng ta cần lấp đầy số bit này: - Nếu số dương thì c|c bit còn lại bổ sung v{o phía trước - Nếu số âm thì các bit còn lại bổ sung v{o phía trước Trong các ví dụ trên, phần d~y bit để trắng tương ứng với bit dấu – tương ứng với – v{ tương ứng với + Toán tử dịch bit sang trái Dịch chuyển toàn dãy bit sang trái theo số bit định Ví dụ Đối với số dương + 0 1 (tương ứng 11 hệ thập phân) << 1 dịch sang trái bit (tương ứng 44 hệ thập C++ << phân) Nghĩa l{ 11<<2=44 T r a n g | 47 CuuDuongThanCong.com https://fb.com/tailieudientucntt (49) Chương To|n tử &= Các phép toán gán hợp trên bit |= ^= >>= <<= Các toán tử hội bit, tuyển bit, tuyển loại bit và phủ định bit tính sau: chúng ta chuyển đổi các số thập phân sang nhị ph}n tương ứng, sau đó sử dụng c|c phép to|n tương ứng cho bit theo vị trí nó Ví dụ trên 210=102, 310=112 và ta thực c|c phép to|n tương ứng với bit Bit thứ (từ phải sang trái) là 0&1=1, bit thứ hai 1&1=1, kết phép toán 2&3 là 112 hay 310 Tương tự cho các phép toán còn lại Nếu hai số có độ dài bit khác nhau, thì ta việc bổ sung thêm số có độ dài bit ngắn hơn, sau đó thực c|c phép to|n đ~ nêu Trong trường hợp này, ta cần lưu ý phép toán tuyển loại có chân trị là hai bit tương ứng là khác nhau, giống thì tương ứng là 0(1^1=0^0=0, 1^0=0^1=1) Các phép toán hội, tuyển và phủ định còn đúng phép toán hội, tuyển và phủ định trên kiểu liệu logic Các toán tử dịch trái bit << và dịch phải bit >> thực trực tiếp trên số nguyên hệ thập phân, tính sau: a<<b=a*2b và a>>b=a/2b Toán tử chuyển đổi kiểu liệu Cách biểu diễn chuyển đổi biến thuộc kiểu liệu này, sang kiểu khác có thể thực kiểu chúng tương đương Ta có thể chuyển số thành số (sau này học hướng đối tượng, ta có thể chuyển T r a n g | 48 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Toán tử n{y dùng để chuyển đổi biến hay thuộc kiểu liệu này sang kiểu liệu khác Giả sử ta có biến int a = 3, int b = Khi thực phép chia để nhận kết thực, ta cần viết sau: (float)3/2 Hãy lưu ý số đ}y đ~ bị chuyển thành kiểu thực, và việc thực phép chia số thực cho số nguyên trả kiểu thực 1.5 Nếu ta viết 3/(float)2, kết tương tự Ngược lại, viết (float)(3/2) thì kết lại khác Sở dĩ là vì, nó thực phép chia nguyên 3/2 (kết l{ 1), sau đó nó chuyển giá trị nguyên này sang thực Do đó, giá trị thu l{ 1, thuộc kiểu số thực (tức là 1.0f) (50) Chương To|n tử c|c đối tượng cùng cây phả hệ) Ta không thể chuyển đổi từ số th{nh x}u, hay ngược lại (bằng cách thực phép toán chuyển đổi kiểu) Ta có thể chuyển đổi xâu số thành số và số thành xâu số nhiều c|ch kh|c nhau, việc sử dụng toán tử chuyển đổi kiểu là không phép Khi chuyển đổi, ta sử dụng các cú pháp sau: (kiểu_dữ_liệu)biến (kiểu_dữ_liệu)(biến) kiểu_dữ_liệu(biến) Chúng ta nên sử dụng kiểu thứ để tránh các nhầm lẫn đ|ng tiếc biểu thức phức tạp Các toán tử khác Trong phần lập trình hướng đối tượng, chúng ta làm quen thêm nhiều toán tử khác Theo trình tự trình bày giáo trình này, chúng ta chưa thảo luận thêm chúng Ta tìm hiểu chi tiết phần hướng đối tượng giáo trình này Thứ tự ưu tiên các toán tử Trong toán học, chúng ta biết tính giá trị biểu thức, thì luôn có ưu tiên các toán tử như: phép nh}n thực trước phép cộng, phép chia và nhân thực đồng thời, ưu tiên từ trái sang phải… Trong các ngôn ngữ lập trình nói chung C++ nói riêng, các toán tử có độ ưu tiên định Trong biểu thức phức tạp, ta cần chú ý đến độ ưu tiên các toán tử, điều này dễ gây sai sót Trong bảng sau đ}y, tôi xin đưa thứ tự ưu tiên các toán tử lập trình C++ Độ ưu tiên cùng loại :: Trái-sang-phải () [] -> ++ (hậu tố) dynamic_cast Trái-sang-phải static_cast reinterpret_cast const_cast typeid ++ (tiền tố) ~ ! sizeof new delete Phải-sang-trái *& + - (dấu dương âm) (type) (chuyển đổi kiểu) Phải-sang-trái * ->* Trái-sang-phải */% Trái-sang-phải + - (phép toán công, trừ) Trái-sang-phải << >> Trái-sang-phải < > <= >= Trái-sang-phải T r a n g | 49 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Mức ưu tiên Toán tử (51) Chương To|n tử 10 == != Trái-sang-phải 11 & Trái-sang-phải 12 ^ Trái-sang-phải 13 | Trái-sang-phải 14 && Trái-sang-phải 15 || Trái-sang-phải 16 ?: Phải-sang-trái 17 = *= /= %= += -= >>= <<= &= ^= |= Phải-sang-trái 18 , Trái-sang-phải Các toán tử thực theo mức ưu tiên từ trên xuống Nếu các toán tử cùng mức, nó thực theo độ ưu tiên cùng loại Ví dụ: Lưu ý Trong ví dụ thứ hai, việc sử dụng phép toán 2++ là không hợp lệ Ở đ}y, có tác dụng minh họa trực quan Còn là số, ta không thể thực phép toán 2++ để l{m thay đổi giá trị Trong C++, chúng ta cần thực phép g|n b = 2; sau đó l{ b++ Nghĩa l{ ta cần biểu diễn biểu thức sau để thu kết chính xác có thể bảo đảm thực thi C++ T r a n g | 50 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ a = (b=0, c=0, b+c) Toán tử g|n = có độ ưu tiên 17, c|c to|n tử cộng + có độ ưu tiên 7, to|n tử () có độ ưu tiên v{ to|n tử , có độ ưu tiên 18 Do đó, to|n tử () thực trước Bây ta xét các toán tử dấu (), chú ý các biểu thức b=0, c=0, b+c là các biểu thức riêng biệt, chúng phân tách toán tử phân tách (,) Theo thứ tự ưu tiên toán tử phẩy, nó thực từ trái-sang-phải Nghĩa l{ b=0, c=0 sau đó l{ b+c Cuối cùng nó thực toán tử gán giá trị biểu thức phức hợp bên phải cho bên trái Kết là a = (1+2)*3/2++ Toán tử g|n (độ ưu tiên 17), to|n tử + (độ ưu tiên 7), toán tử * (độ ưu tiên 6), to|n tử / (độ ưu tiên 6), to|n tử ++ hậu tố (độ ưu tiên 2) v{ to|n tử () (độ ưu tiên 2) Toán tử hậu tố ++ và toán tử () thực trước Theo độ ưu tiên cùng loại, nó thực thi từ trái-sangphải Như vậy, toán tử () thực đầu tiên Nghĩa l{ ta nhận biểu thức a = 3*3/2++ Tiếp theo, nó thực toán tử hậu tố ++, nhiên toán tử này tăng gi| trị lên sau thực xong các phép toán biểu thức Đến thời điểm này, ta nhận biểu thức a=3*3/2 Toán tử * v{ / có cùng độ ưu tiên, nó thực theo thứ trự từ trái sang phải, nghĩa l{ a=9/2=4 Kết (52) Chương To|n tử a = (b=2, (1+2)*3/b++) C++ Bài tập Tính toán các biểu thức sau, dựa v{o độ ưu tiên toán tử, sau đó, viết chương trình trên C++ để kiểm tra kết a 2+2*4%3+ ++2; b 2++ + ++2*(3 - 2) c 5++ - 3== 2-(4+2%3) d 5>>2^3*(1+2) T r a n g | 51 CuuDuongThanCong.com https://fb.com/tailieudientucntt (53) Chương Xuất – Nhập CHƯƠNG XUẤT NHẬP CƠ BẢN Đến thời điểm n{y, chúng ta đ~ biết hai cách thức để xuất liệu màn hình nhờ vào việc sử dụng đối tượng cout và hàm printf Trong chương n{y, chúng ta tìm hiểu cụ thể cách xuất-nhập liệu nhờ vào thiết bị nhập liệu là bàn phím, và thiết bị hiển thị liệu xuất là màn hình máy tính Trong thư viện chuẩn C++, các hàm xuất nhập nằm tệp header là iostream Đối với thư viện này ta cần lưu ý số điểm Có hai lớp thư viện có chức hỗ trợ các hàm xuất nhập đó l{ iostream v{ iostream.h Về chất, cách thức sử dụng chúng không có nhiều khác biệt Tuy nhiên việc sử dụng thư viện iostream có nhiều ưu điểm hẳn so với thư viện iostream.h Thư viện iostream.h đ~ đời c|ch đ}y qu| l}u (trên 15 năm) thư viện iostream nhiều Việc sử dụng thư viện mới, chuẩn mực tốt Thư viện iostream hỗ trợ kiểu char lẫn kiểu w_char Thư viện iostream đặc tả namespace std, thư viện iostream.h khai báo toàn cục Việc khai báo toàn cục chiếm dụng không gian nhớ lớn Vì vậy, muốn thực việc nhập xuất liệu C++, ta nên sử dụng thư viện iostream thay vì sử dụng iostream.h Tổng quát hóa, các lớp thư viện có hai dạng tồn song song là h và không có h (string và string.h, new v{ new.h…), thì chúng ta nên sử dụng thư viện không có h Trong trường hợp không có dạng tương ứng, ta bắt buộc phải sử dụng thư viện h (ví dụ math.h) Xuất liệu chuẩn cout Đầu tiên, ta khảo sát việc xuất liệu màn hình nhờ đối tượng cout Nó sử dụng kết hợp với toán tử chèn liệu >> (kí hiệu giống toán tử dịch bit phải) cout<<biến_1<<…<<biến_n; Trong đó, biến_1,…,biến_n: là các biến số Chúng đ~ khởi tạo giá trị Nếu biến chưa khởi tạo giá trị, ta nhận lỗi thực thi chương trình Chương trình dịch thông báo việc sử dụng biến mà T r a n g | 52 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Cú pháp: (54) Chương Xuất – Nhập không khởi tạo giá trị cho nó Các biến này có thể là biến thuộc kiểu liệu nguyên thủy tham chiếu Đối với các biến l{ c|c đối tượng thể các lớp, ta thảo luận mục “chồng chất toán tử nhập xuất” chương lập trình hướng đối tượng Sau đ}y l{ vài ví dụ việc sử dụng đối tượng cout: cout<<”Hello, world !”;//In c}u Hello, world ! m{n hình cout<<120;//In số 120 màn hình cout<<x;//In giá trị biến x màn hình Đối tượng cout kết hợp với toán tử << có thể ghép nhiều lần cout<<”Chao ban”<<” ban may tuoi”; cout<<”Chuc mung”<<endl; cout<<x<<”+”<<y<<”=”<<(x+y); Như ví dụ trên, muốn xuống dòng, ta sử dụng kí hiệu endl, ta có thể sử dụng kí hiệu \n m{ ta đ~ l{m quen chương trước Về mặt ngữ nghĩa, thì không có khác nào hai cách viết này Khi làm việc với đối tượng cout, ta có số cách thức định dạng liệu cung cấp mục chương 17 cuối giáo trình này Nhập liệu chuẩn cin Để nhập liệu ta có thể sử dụng h{m scanf C Nhưng theo xu hướng lập trình C++ đại, hãy sử dụng đối tượng cin Nó sử dụng kết hợp với toán tử trích tách liệu << (giống toán tử dịch bit phải) Sau toán tử này, bắt buộc là biến để lưu liệu tách Cú pháp: int age; cin>>age; float f; cin>>f; string s; cin>>s; Chú ý kiểu liệu biến sử dụng đối tượng cin này Nếu có vi phạm nào kiểu liệu (ví dụ biến l{ int, T r a n g | 53 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ cin>>biến_1>>…>>biến_n; Các biến số biến_1,…, biến_n cần khai b|o Thông thường, biến n{y chưa khởi tạo giá trị Sau đ}y l{ vài ví dụ việc sử dụng đối tượng cout: (55) Chương Xuất – Nhập nhập ta lại nhập vào kí tự không phải là số) thì chương trình dịch bỏ qua việc khởi tạo giá trị cho biến đó Chương trình ho{n to{n không phát sinh lỗi (process returned 0) Khi sử dụng đối tượng cout và cin, ta cần khai báo không gian sử dụng namespace là std Hoặc, có thể viết ngắn gọn std:: Chương trình Chương trình #include <iostream> #include <iostream> using namespace std; int main(){ int main(){ std::cout<<”Hello”; cout<<”Hello”; } } Đối tượng cin và xâu kí tự: ví dụ trên, tôi đ~ sử dụng đối tượng cin để tách xâu kí tự và gán cho biến xâu kí tự s Khi sử dụng đối tượng cin với xâu kí tự, cần lưu ý điểm: đối tượng cin dừng việc trích tách nó đọc thấy kí tự trắng xâu kí tự đó (có nghĩa, xâu nhập vào l{ “Toi di hoc” – thì nó t|ch x}u “Toi” v{ g|n cho biến s) Để khắc phục nhược điểm này đối tượng cin, C++ cung cấp cho chúng ta hàm khác là hàm getline, có chức tương tự Cú pháp: getline(chuẩn_nhập_dữ_liệu, tên_biến_xâu) Khi nhập xuất liệu từ bàn phím và màn hình, tham số chuẩn_nhập_xuất_dữ_liệu luôn sử dụng là cin Nếu làm việc với tập tin file, thì tham số này tương ứng với tên file Chúng ta tìm hiểu trường hợp này chương 16 giáo trình Chương trình #include <iostream> #include <string> using namespace std; C++ int main() { T r a n g | 54 CuuDuongThanCong.com https://fb.com/tailieudientucntt (56) Chương Xuất – Nhập string s; cout<<”Nhap ten: “; getline(cin, s); cout<<”Chao ban “<<s; return 0; } Nhập liệu nhờ lớp stringstream Để sử dụng lớp stringstream, chúng ta cần khai báo tệp header là <sstream> Lớp này cho phép đối tượng dựa trên sở xâu có thể chuyển đổi luồng stream Trong thư viện sstream, ta có ba lớp đối tượng luồng x}u bản: stringstream, istringstream và ostringstream Các luồng này có thể sử dụng để tách chèn xâu, nó đặc biệt hữu dụng chuyển xâu thành số v{ ngược lại Ví dụ, muốn tách số integer từ x}u “1201”, ta có thể viết sau: string mystr = “1201”; int mynum; stringstream(mystr)>>mynum; Đ}y không phải là cách thức giúp ta chuyển đổi xâu thành số Trong thư viện string, cung cấp cho chúng ta c|c h{m để thực thi công việc đó h{m atof (x}u số thực thành số thực), atoll (xâu thành số nguyên dài thành số nguyên dài), Tuy nhiên, các hàm này chủ yếu làm việc với xâu kí tự C, tức là char* Ví dụ sau đ}y cho thấy cách sử dụng lớp stringstream để nhập liệu từ bàn phím Chương trình #include<iostream> #include<sstream> C++ using namespace std; int main() T r a n g | 55 CuuDuongThanCong.com https://fb.com/tailieudientucntt (57) Chương Xuất – Nhập { string mystr; int mynum; getline(cin,mystr); stringstream(mystr)>>mynum; cout<<mynum; return 0; } Thay vì trực tiếp trích lọc số nguyên nhập vào, ta sử dụng hàm getline để trích lọc liệu nhập v{o dạng xâu kí tự Sau đó, chúng ta sử dụng lớp stringstream để chuyển đổi x}u đó th{nh số Ví dụ sau đ}y cho phép chúng ta nhập vào dãy giá trị, sau đó in tổng các số vừa nhập Số nhập v{o phân tách với dấu hai chấm : Chương trình #include <iostream> #include <sstream> Kết 1:2:3:4:5 Sum = 15 using namespace std; C++ int main() { string temp, s; getline(cin, s); istringstream ss(s); double sum = 0; while (getline(ss, temp, ':') ) { float a; stringstream(temp)>>a; sum+=a; } cout<<”Sum = “<<sum; return 0; } T r a n g | 56 CuuDuongThanCong.com https://fb.com/tailieudientucntt (58) Chương Xuất – Nhập Giải thích: ví dụ trên, hàm getline có thêm biến thể (sau này, chúng ta gọi chúng là các chồng chất hàm) Nó có tất dạng biến thể Phương ph|p n{y, thường sử dụng liệu nhập vào quá nhiều Chúng ta có thể cho người dùng nhập vào thành x}u, đó ta tiến hành xử lý nhờ lớp istringstream này C++ Để có cái nhìn cụ thể các lớp stringstream, istringstream, ostringstream; hãy tham khảo thêm thông tin phần trợ giúp MSDN4 (Microsoft Developer Network) http://www.msdn.microsoft.com/en-us/visualc/default.aspx T r a n g | 57 CuuDuongThanCong.com https://fb.com/tailieudientucntt (59) Chương C|c cấu trúc lệnh điều khiển CHƯƠNG CÁC CẤU TRÚC LỆNH ĐIỀU KHIỂN Một chương trình thực thi, nó không đơn là dãy các câu lệnh Trong quá trình xử lý, nó có thể kiểm tra điều kiện thực thi đoạn mã, lặp lặp lại đoạn m~ n{o đó… Với mục đích đó, C++ cung cấp cho chúng ta các cấu trúc điều khiển Với các cấu trúc điều khiển mà chúng ta tìm hiểu chương này, ta đề cập đến hai khái niệm: câu lệnh (mệnh đề - statement) và khối lệnh (block) Câu lệnh: là lệnh kết thúc dấu chấm phẩy (;) Nó có thể là lệnh đơn giản lệnh có cấu trúc Các lệnh đơn giản nhập xuất liệu; các khai báo biến, hằng; lệnh g|n… Các lệnh gán, xuất nhập…l{ c|c lệnh đơn C|c lệnh điều kiện, lựa chọn, lặp mà chúng ta tìm hiểu chương này là các lệnh có cấu trúc Khối lệnh: là dãy các câu lệnh Trong C++, khối lệnh đặt cặp dấu {} Cấu trúc lệnh có điều kiện: if và else Từ khóa if thường sử dụng muốn thực thi đoạn chương trình với điều kiện n{o đó Cấu trúc câu lệnh if trường hợp này Cú pháp: if (biểu_thức_điều_kiện_đúng) { Các_lệnh; } Giải thích: kiểm tra giá trị biểu thức điều kiện, đúng thì c|c lệnh bên thực hiện; ngược lại, lệnh không thực Ví dụ Trang | 58 CuuDuongThanCong.com https://fb.com/tailieudientucntt (60) Chương C|c cấu trúc lệnh điều khiển if (x>0) cout<<x<<” la so duong”; if((x>0)&&(y>0)) { cout<<x<<” la so duong”<<endl; cout<<y<<” la so duong”; } Nếu có nhiều câu lệnh chịu chi phối câu lệnh if, thì chúng đặt khối lệnh Trong trường hợp biểu_thức_điều_kiện sai, ta cần thực thi mệnh đề kh|c Khi đó, ta sử dụng thêm từ khóa else Cú pháp: if(biểu_thức_điều_kiện_đúng) { … }else { … } Ví dụ if (x%2==0) cout<<x<<” la so chan”; else cout<<x<<” la so le”; Cấu trúc if (lẫn else) có thể lồng v{o Khi đó, chúng ta có cấu trúc lệnh phức hợp C++ Ví dụ if(x>0) T r a n g | 59 CuuDuongThanCong.com https://fb.com/tailieudientucntt (61) Chương C|c cấu trúc lệnh điều khiển cout<<x<<” la so duong”; else if(x<0) cout<<x<<” la so am”; else cout<<x<<” la so 0”; Bài tập Viết chương trình giải phương trình bậc 2, với các hệ số nhập vào từ bàn phím Viết chương trình tính gi| trị hàm số sau: ( )|) ( ) ( ) { Viết chương trình giải hệ phương trình ba ẩn đầy đủ công thức định thức Cramer ( ) (| Cấu trúc lặp Trong thực tế, chúng ta gặp nhiều tình lặp lặp lại Với mục đích này, ngôn ngữ C++ cung cấp cho chúng ta các cấu trúc lặp tương ứng Vòng lặp while Cú pháp: Ví dụ Kết #include<iostream> Nhap n:5 using namespace std; T r a n g | 60 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ while (biểu_thức_điều_kiện_đúng) { … } Giải thích: Nếu biểu_thức_điều_kiện đúng, c|c lệnh bên vòng lặp thực nó nhận giá trị sai (62) Chương C|c cấu trúc lệnh điều khiển int main() { int n; cout<<”Nhap n:”; cin>>n; while (n>0){ cout<<n<<endl; n ; } return 0; } Giải thích: đầu tiên nhập vào giá trị cho biến n N nhập vào đ}y l{ Vòng lặp while kiểm tra điều kiện n>0 Điều kiện n{y đúng, nên c|c lệnh vòng lặp thực Nó in giá trị n l{ Sau đó, gi| trị n giảm 1, tức là n = Vòng lặp lại tiếp tục thực hiện, vì n>0 còn đúng Qu| trình này tiếp tục, n=0 Khi đó, điều kiện n>0 l{ sai Do đó, vòng lặp dừng lại Giá trị in màn hình là các số từ giảm đến Lưu ý: sử dụng vòng lặp while cần lưu ý c|c điểm sau đ}y: Vòng lặp phải có tính dừng Nghĩa l{ biểu_thức_điều_kiện phải có trường hợp sai Trong số tình huống, người ta sử dụng vòng lặp vô hạn, cần có chế để thoát khỏi vòng lặp cần thiết Nếu có nhiều lệnh chịu chi phối while, thì chúng cần đặt dấu khối lệnh Vòng lặp do…while { … }while (biểu_thức_điều_kiện_đúng); T r a n g | 61 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Cú pháp: (63) Chương C|c cấu trúc lệnh điều khiển Giải thích: Thực các lệnh vòng lặp, sau đó kiểm tra biểu_thức_điều_kiện Nếu biểu_thức_điều_kiện còn đúng, thì tiếp tục lặp Ví dụ Kết #include<iostream> Nhap n:5 using namespace std; int main(){ int n; cout<<”Nhap n:”; cin>>n; { cout<<n<<endl; n ; }while(n>0); return 0; } Giải thích: đầu tiên nhập vào giá trị cho biến n Giá trị n nhập vào đ}y l{ Vòng lặp do…while thực thi các lệnh bên nó Nó in giá trị n l{ Sau đó, gi| trị n giảm 1, tức là n = Vòng lặp kiểm tra giá trị biểu thức n>0 Vì biểu thức n{y đúng, nên nó tiếp tục lặp Quá trình này tiếp tục n=0 Giá trị n=0 in ra, sau kiểm tra n>0 không còn đúng nữa, vòng lặp kết thúc Khác với vòng lặp while trên, nó in giá trị từ giảm đến Vòng lặp phải có tính dừng Nghĩa l{ biểu_thức_điều_kiện phải có trường hợp sai Trong số tình huống, người ta sử dụng vòng lặp vô hạn, cần có chế để thoát khỏi vòng lặp cần thiết Nếu có nhiều lệnh chịu chi phối do…while, thì chúng cần đặt dấu khối lệnh T r a n g | 62 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lưu ý: sử dụng vòng lặp do…while cần lưu ý c|c điểm sau đ}y: (64) Chương C|c cấu trúc lệnh điều khiển Vòng lặp do…while luôn thực các lệnh bên nó, ít lần Vòng lặp for Cú pháp: for (biểu_thức_khởi_tạo; biểu_thức_giới_hạn; biểu_thức_tăng_giảm) { … } Giải thích: Thực vòng lặp, với số vòng lặp từ biểu_thức_khởi_tạo biểu_thức_giới_hạn theo mức tăng là biểu_thức_tăng_giảm Ví dụ Kết #include<iostream> Nhap n:5 using namespace std; int main() { int n, i; cout<<”Nhap n:”; cin>>n; for (i=0; i<=n;i++) { cout<<i<<endl; }; return 0; } Lưu ý: sử dụng vòng lặp for cần lưu ý c|c điểm sau đ}y: T r a n g | 63 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Giải thích: đầu tiên nhập vào giá trị cho biến n Giá trị n nhập vào đ}y l{ Vòng lặp for thực thi các lệnh bên với số vòng lặp là từ đến 5, theo bước nhảy là – tương ứng với i++ (65) Chương C|c cấu trúc lệnh điều khiển Các tham số vòng lặp for có thể khuyết vài (thậm chí là tất cả) tham số Tuy nhiên, dấu chấm phẩy là luôn bắt buộc Số bước lặp vòng lặp for tính sau: Nếu có nhiều lệnh chịu chi phối for, thì chúng cần đặt dấu khối lệnh Ta có thể thực việc khai báo biến trực tiếp bên dấu ngoặc đơn vòng lặp for Ví dụ Kết #include<iostream> Nhap n:5 using namespace std; int main() { int n; cout<<”Nhap n:”; cin>>n; for (int i=0; i<=n;i++) { cout<<i<<endl; }; return 0; } Lệnh break dùng để thoát khỏi vòng lặp Thông thường, ta sử dụng các vòng lặp không x|c định số lần lặp, để tránh lặp vô hạn, người ta thường sử dụng câu lệnh break để thoát khỏi vòng lặp T r a n g | 64 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Các câu lệnh nhảy a Câu lệnh break (66) Chương C|c cấu trúc lệnh điều khiển Ví dụ Kết #include<iostream> using namespace std; int main() { int n = 2; for(;;){ cout<<n<<endl; n ; if (n<0) break; } return 0; } Giải thích: Giá trị khởi tạo n = Vòng lặp for tiến hành in giá trị n, sau đó, giảm giá trị n C}u lệnh điều kiện bên for kiểm tra điều kiện n Nếu n<0, thì lệnh break thực và vòng lặp bị hủy b Câu lệnh continue Ví dụ Kết #include<iostream> using namespace std; int main() { for(int i=0;i<10;i++){ if (i%2!=0) continue; cout<<i<<endl; } return 0; } C++ Câu lệnh continue thường dùng các vòng lặp Khi lệnh continue gọi, bước lặp bỏ qua, và tiến hành bước lặp T r a n g | 65 CuuDuongThanCong.com https://fb.com/tailieudientucntt (67) Chương C|c cấu trúc lệnh điều khiển Giải thích: Vòng lặp for thực thi các lệnh bên nó Biến i chạy từ đến 9, kiểm tra điều kiện i có phải là số chẵn hay không Nếu i là số lẻ, thì câu lệnh continue thực v{ bước lặp bị bỏ qua Nếu i là số chẵn, thì lệnh continue không gọi v{ bước lặp thực Do đó, lệnh cout thực trường hợp biến i là chẵn Như vậy, giá trị i là chẵn, nó in kết Nếu giá trị i là lẻ, thì kết không in c Lệnh goto Lệnh goto cho phép tạo bước nhảy đến nh~n ấn định sẵn Tên nhãn đặt sau tên_nhãn: và lệnh goto nhảy đến tên nhãn Một lời khuyên cho chúng ta là nên hạn chế tối đa việc sử dụng lệnh goto Bởi vì lệnh goto thường làm phá vỡ cấu trúc lập trình đại Nhiều ngôn ngữ lập trình họ nhà C đời sau C++ Java đ~ tuyệt giao hoàn toàn với câu lệnh goto này Kết #include <iostream> using namespace std; int main() { int n = 5; loop://Tên nhãn cout<<n<<endl; n ; if(n>0) goto loop; return 0; } Giải thích: Giá trị khởi tạo biến n l{ Nh~n đặt tên là loop Nhãn có thể hiểu vị trí đ|nh dấu (bookmark) Chương trình tiến hành in giá trị biến n Sau đó, giảm giá trị n C}u lệnh điều kiện, kiểm tra giá trị biểu thức n>0, đúng thi lệnh goto gọi và nó chuyển đến vị trí đ~ đ|nh dấu là loop Chương trình lại thực thi thêm lần kể từ vị trí loop đó Nếu n<=0, lệnh goto không gọi Chương trình kết thúc Việc sử dụng các câu lệnh lặp, hoàn toàn có thể thay T r a n g | 66 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương trình (68) Chương C|c cấu trúc lệnh điều khiển cho lệnh goto Hãy luôn ghi nhớ: CHỈ NÊN sử dụng goto thực cần thiết d Lệnh exit Lệnh exit dùng để thoát khỏi chương trình v{ trả m~ định Mã định này tùy thuộc vào hệ điều hành, nó có thể sử dụng chương trình theo quy ước sau: chương trình kết thúc bình thường, thì m~ chương trình l{ 0; có cố không mong muốn xảy ra, thì m~ chương trình l{ giá trị khác void exit(int mã_chỉ_định); Nếu tham số mã_chỉ_định không cấp vào, tức là exit (không có dấu ngoặc đơn), thì nó tiến hành theo mặc định – tức giá trị Hàm exit nằm thư viện stdlib.h Đ}y l{ h{m tương đối cũ nằm thư viện h Ta có thể sử dụng lệnh exit khai b|o thư viện stdlib.h mà không có thư viện tương ứng là stdlib Cấu trúc lựa chọn: switch Cú pháp: switch(biểu_thức){ case hằng_1: nhóm_các_lệnh; break; case hằng_2: nhóm_các_lệnh; break; … default: C++ nhóm_các_lệnh; } T r a n g | 67 CuuDuongThanCong.com https://fb.com/tailieudientucntt (69) Chương C|c cấu trúc lệnh điều khiển Giải thích: kiểm tra giá trị biểu thức, giá trị biểu thức rơi v{o danh sách hằng, thì nó thực các lệnh trường hợp case tương ứng (nếu là hằng_1 – các lệnh trường hợp case hằng_1, ….) Nếu biểu thức không thuộc vào danh sách hằng, thì nó thực lệnh trường hợp default Kết #include <iostream> Ban la nam hay nu b/g:b using namespace std; Nam int main() { char n; cout<<”Ban la nam hay nu b/g:”<<endl; cin>>n; switch(n) { case ‘b’: cout<<”Nam”; break; case ‘g’: cout<<”Nu”; break; default: cout<<”Khong xac dinh”; } return 0; } Giải thích: chương trình buộc người dùng nhập vào kí tự (b – boy) hay (g – girl) Nếu người dùng nhập vào kí tự kh|c, chương trình tính đến trường hợp này Kí tự m{ người dùng nhập v{o lưu biến n Câu lệnh switch kiểm tra biến nhập v{o đó có nằm danh sách hay không (danh sách đ}y l{ ‘b’ v{ ‘g’) Nếu có, thì tương ứng với ‘b’ nó thực trường hợp case ’b’, l{ ‘g’ nó thực trường hợp case ‘g’ Lệnh break trường hợp có tác dụng là thoát khỏi câu lệnh lựa chọn (cũng mang tính lặp), không có lệnh break, tất các trường hợp xét duyệt và rơi v{o trường hợp nào thì các lệnh tương ứng thực thi, đồng thời lệnh trường hợp bên nó T r a n g | 68 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương trình (70) Chương C|c cấu trúc lệnh điều khiển thực thi Trong trường hợp, kí tự nhập v{o không tương ứng danh sách hằng, nó thực thi trường hợp default Vì default l{ trường hợp cuối cùng, nên nó không cần lệnh break Chú ý: Lệnh switch lựa chọn để sử dụng cần kiểm tra giá trị biểu thức có tương ứng với tập các số n{o đó hay không (sự tương ứng đ}y có thể là thuộc không thuộc tương ứng với khái niệm tập hợp) Các hằng_1, hằng_2,… có thể là vùng liên tục, gián đoạn (như c|c số từ 1, ‘a’ ’d’,…) Nhưng thiết các giá trị tương ứng với c|c trường hợp case phải là số (hoặc khoảng hằng) Liên tục Rời rạc C++ int a = 1; switch(a>0) { case true: cout<<"Duong"; break; case false: cout<<"Am"; break; default: cout<<"Khong"; } int a = 1; switch(a) { case 1: case 2: case 3: cout<<"Xuan"; break; case 4: case 5: case 6: cout<<"Ha"; break; case 7: case 8: case 9: cout<<"Thu"; break; case 10: case 11: case 12: cout<<"Dong"; break; default: cout<<"Khong phai thang cua nam"; T r a n g | 69 CuuDuongThanCong.com https://fb.com/tailieudientucntt (71) Chương C|c cấu trúc lệnh điều khiển } Biểu thức lệnh switch thiết không phải là kiểu có cấu trúc (mảng, xâu,…) Ví dụ sau đ}y phát sinh lỗi biên dịch, biểu thức tương ứng với xâu string s = “abc”; Error switch(s) { case “a”: case “ab”: case “abc”: default: } Trong hầu hết các ngôn ngữ lập trình, đại đa số không cho phép tham số switch là x}u (cũng lệnh case of họ Pascal – Delphi) Tuy nhiên, ngôn ngữ C# hỗ trợ xâu kí tự tham số switch dù nó là dẫn xuất C++ Tên lệnh if…else switch for while do…while break Cách dùng Khi cần kiểm tra một v{i điều kiện mang tính chất logic Khi cần kiểm tra điều kiện tính thuộc vào biến số/biểu thức danh sách tương ứng Lặp có số vòng lặp x|c định Cần kiểm tra điều kiện lặp trước thực lệnh, lặp không x|c định số vòng lặp Kiểm tra điều kiện lặp sau thực lệnh, lặp không x|c định số vòng lặp Cần thoát khỏi vòng lặp T r a n g | 70 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Một điều cần lưu ý, chất thì câu lệnh switch tương ứng với dãy các câu lệnh if Chúng hoàn toàn có thể thay cho Cũng tương tự, các câu lệnh lặp while, while v{ for có thể thay cho cách hoàn toàn Có nghĩa l{ chúng ta cần nắm cú pháp ba câu lệnh lặp này là có thể vận dụng trường hợp Tuy nhiên, chúng sử dụng c|c trường hợp mang tính đặc trưng Điều này hữu ích cho người đ~ làm quen với ngôn ngữ lập trình họ Pascal – Delphi Bảng sau đ}y tổng hợp các cách sử dụng các lệnh có cấu trúc thường dùng (72) Chương C|c cấu trúc lệnh điều khiển continue goto Bỏ qua vòng lặp tại, thực thi bước lặp Nhảy đến nh~n đinh Nên tr|nh sử dụng, sử dụng trường hợp thực cần thiết Bài tập Sử dụng các cấu trúc lặp for, while, do…while, goto để xây dựng các chương trình tính tích ph}n sau (với cấu trúc xây dựng chương trình) phương ph|p hình chữ nhật (trái, phải trung tọa) ∫ ( ) Lựa chọn cấu trúc lệnh phù hợp, để tính giá trị chuỗi hữu hạn sau đ}y ∑ Tính các tổng sau đ}y ( ( ) ) C++ ( ) với n, x nhập vào từ bàn phím Yêu cầu xây dựng hàm T r a n g | 71 CuuDuongThanCong.com https://fb.com/tailieudientucntt (73) Chương Hàm CHƯƠNG HÀM Hàm là tập hợp các câu lệnh, nó thực thi gọi từ vị trí kh|c chương trình Hãy quan s|t lược đồ bên đ}y: Thư viện Thư viện Hàm Hàm Hàm Hàm Hàm Hàm … … Chương trình chính Thư viện n Hàm Hàm Hàm main{ Gọi H{m (Thư viện 1); Hàm Gọi H{m (Thư viện 2); Hàm Gọi H{m (Thư viện n); … Gọi H{m (chương trình chính) Hình 16 – Sơ đồ minh họa việc sử dụng hàm Trong lược đồ n{y, c|c h{m viết c|c thư viện khác Trong chương trình chính, chúng ta có thể gọi c|c h{m c|c thư viện, lẫn c|c h{m khai báo chương trình chính Nhờ vào việc sử dụng hàm, chúng ta có thể ph}n chia chương trình thành các modul nhỏ Đôi lúc, người ta gọi c|ch ph}n chia chương trình Trang | 72 CuuDuongThanCong.com https://fb.com/tailieudientucntt (74) Chương H{m th{nh c|c h{m này là cách giải b{i to|n theo phương ph|p “chia để trị” Cách phân chia này có nhiều ưu điểm: Làm cho chương trình trở nên gọn gàng dễ đọc Dễ cải biên chương trình Dễ kiểm tra theo các modul Đó l{ ý tưởng để xây dựng hàm Vậy h{m khai báo và sử dụng nào Chúng ta tìm hiểu chương n{y Khai báo và sử dụng hàm Cú pháp: kiểu_dữ_liệu tên_hàm(danh_sách_tham_số) { Thân hàm; } Trong đó, o kiểu_dữ_liệu: là kiểu liệu mà hàm trả o tên_hàm: là tên h{m, người lập trình đặt Tên h{m không chứa kí tự đặc biệt, không bắt đầu số, không chứa kí tự trắng, không trùng với từ khóa o danh_sách_tham_số: là danh sách các tham số dùng c|c biến cục Nếu có nhiều tham số, thì chúng phân tách theo các dấu phẩy o Thân hàm: là nội dung m{ người lập trình xây dựng nên Nếu hàm trả kiểu liệu khác void, ta cần sử dụng lệnh return để trả biến chứa kết và có cùng kiểu liệu với kiểu liệu hàm Nếu hàm trả kiểu liệu void thì điều này là không cần thiết Ví dụ Kết #include <iostream> using namespace std; C++ int add(int a, int b) { T r a n g | 73 CuuDuongThanCong.com https://fb.com/tailieudientucntt (75) Chương H{m return a+b; } int main() { cout<<add(1, 2); return 0; } Giải thích: Mọi chương trình C++ luôn bắt đầu h{m main Điều đó có nghĩa l{ c|c lệnh hàm main thực thi cách Đối tượng cout in giá trị hàm add(1,2) Khi gọi đến hàm add, nó ánh xạ đến h{m add đ~ khai báo trên, nó tạo lời gọi h{m đến h{m đ~ xây dựng có tên tương ứng (chúng ta cần lưu ý đến điều này để phân biệt với khái niệm hàm nội tuyến tìm hiểu các mục tiếp theo) Với a, b là các tham số hình thức, chúng thay các giá trị cụ thể là và Hàm add này trả giá trị là tổng a và b nhờ từ khóa return Cách khai báo hàm ví dụ trên gọi là khai báo trực tiếp Chúng ta còn cách khai báo hàm gián tiếp nữa, m{ ta thường gọi là khai báo h{m prototype sau: Ví dụ Kết #include <iostream> using namespace std; int add(int a, int b); int main() { C++ cout<<add(1, 2); return 0; T r a n g | 74 CuuDuongThanCong.com https://fb.com/tailieudientucntt (76) Chương H{m } int add(int a, int b) { return a+b; } Trong khai báo hàm dạng này, cấu trúc khai báo hàm khuyết phần thân hàm Chỉ có khai báo phần tên hàm theo cú pháp chuẩn Ta có thể đặt hàm xây dựng hoàn chỉnh bất kì vị trí nào Cách khai báo hàm prototype có nhiều ưu điểm: - Không cần quan t}m đến thứ tự khai báo hàm Nếu không sử dụng khai báo prototype, thì hàm khai báo sau phép gọi h{m khai b|o trước nó Điều ngược lại l{ không phép Nhưng khai báo prototype thì ta hoàn toàn không cần quan t}m đến điều này - Ta có thể tách phần khai b|o prototype v{ đặt nó vào tập tin mới, thường là tập tin tiêu đề h (với tên gọi tùy v{o người lập trình quy định), phần thân hàm lại chứa tệp kh|c, thường là cpp chính tệp chứa chương trình chính C|ch l{m n{y giúp chương trình sáng sủa nhiều Trong các dự án lập trình lớn, người ta thường phân tách theo dạng này Chúng ta xét ví dụ minh họa sau Trong ví dụ minh họa này, dự án tôi gồm có hai tệp: tieude.h để chứa khai b|o prototype v{ main.cpp để chứa thân hàm và hàm main Đối với Codeblocks, hãy thực theo c|c bước sau: - Tạo dự |n C++ v{ lưu lại Trong dự án này, mặc định Codeblocks tạo tệp main.cpp Đối với Eclipse, thực sau: - Kích chuột phải v{o thư mục cần đặt tệp h, chọn New > Header File T r a n g | 75 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Vào New > File > chọn C/C++ header Sau đó, h~y chọn vị trí để lưu trữ tệp tiêu đề (thông thường, ta nên tạo c|c thư mục kh|c để lưu tệp h tệp cpp tôi đ~ trình b{y trên) (77) Chương H{m - Kích chuột phải v{o thư mục cần đặt tệp cpp, chọn New > Source File Đối với Visual Studio 2010, kích chuột phải vào tên dự án, chọn Add New Item Sau đó chọn header file h Tệp tieude.h #ifndef TIEUDE_H_INCLUDED #define TIEUDE_H_INCLUDED Tệp main.cpp #include <iostream> #include "tieude.h" using namespace std; int sum(int, int); int main() void showmsg(void); { showmsg(); #endif // TIEUDE_H_INCLUDED return 0; } void showmsg(){ cout<<sum(1, 3); } int sum(int a, int b){ return a+b; } Trong tệp tieude.h, ta việc nhập các khai báo prototype vào #define và #endif Trong tệp main.cpp, ta cần bổ sung khai b|o thư viện #include “tieude.h” Chú ý rằng, tên tệp tiêu đề nằm dấu nh|y kép “”, mà không phải là dấu <> C|c h{m chương trình chính có thể sử dụng mà không cần quan t}m đến thứ tự khai b|o Như chúng ta thấy, các hàm khai báo sau hàm main (điều này có thể phép khai báo prototype) Hàm showmsg khai b|o trước h{m sum có thể gọi hàm sum Thứ tự tiêu đề hàm tệp tiêu đề hoàn toàn không quan trọng và nó không ảnh hưởng việc sử dụng các hàm theo thứ tự trước sau Khi sử dụng khai báo prototype trên các tập tin h, ta cần lưu ý, dự án có sử dụng namespace, ví dụ std, ta có thể sử dụng cú pháp truy cập std:: m{ không sử dụng using namespace std tệp h này Nếu tệp cpp và tệp h nằm cùng thư mục, thì phần #include tệp cpp có thể viết tên tệp tiêu đề dấu “” Nếu chúng không nằm cùng thư mục, ta cần đường dẫn tương đối cho nó Ví dụ tệp headers.h nằm thư mục headers và tệp main.cpp nằm thư mục cpps Nếu tệp headers.h là tệp tiêu đề tệp main.cpp, ta cần T r a n g | 76 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lưu ý: (78) Chương H{m include nó main.cpp Giả sử headers và cpps nằm cùng thư mục src Khi đó, tệp main.cpp, hãy khai b|o sau: #include“ /headers/headers.h” Trong đó, dấu / để dịch lùi mức cấu trúc c}y thư mục (dịch lùi từ thư mục headers mức chính l{ thư mục src), sau đó l{ headers/headers.h Phạm vi tác dụng biến Như tôi đ~ giới thiệu trên, biến toàn cục là biến khai báo ngoài tất các hàm (hay không bao bất kì dấu {} nào) Các biến này có tác dụng toàn chương trình Ta có thể gọi nó hàm main, hay các hàm khác Ngược lại, biến còn lại gọi là các biến cục Những biến cục khai báo phạm vi n{o (được x|c định nhờ dấu {}) thì có tác dụng phạm vi đó m{ thôi Ví dụ Giải thích #include <iostream> - Biến global là biến toàn cục, nó có tác dụng toàn chương trình Ta có thể sử dụng nó h{m main, h{m add… using namespace std; int global; int add(int a, int b) { local result = a + b; return result; } int main() { int local1; if(local1>0) - Biến local, local1, local2 là các biến cục Biến local khai báo hàm add, nó có phạm vi tác dụng phạm vi hàm này Biến cục local1 khai báo hàm main Nó có tác dụng hàm main Biến local2 khai báo phạm vi tác dụng câu lệnh if, nó có tác dụng khối lệnh này Nếu ta gọi biến này ngoài khối lệnh if, chương trình dịch báo lỗi C++ { int local2; T r a n g | 77 CuuDuongThanCong.com https://fb.com/tailieudientucntt (79) Chương H{m } return 0; } Hàm không trả giá trị - Hàm void Như chúng ta đ~ thấy các ví dụ trên, các hàm mà chúng ta sử dụng là các hàm có giá trị trả Đối với các loại h{m n{y, để hàm trả giá trị n{o đó, ta sử dụng từ khóa return Giá trị hàm trả phải có kiểu liệu cùng loại với kiểu liệu m{ ta quy định khai báo hàm Chúng ta bắt gặp tình huống: nhiều lúc hàm mà ta xây dựng không trả giá trị nào, trả nhiều giá trị Khi đó, chúng ta sử dụng khai báo hàm void Đối với hàm không trả giá trị, ta có thể tham khảo ví dụ sau Còn hàm trả nhiều giá trị, chúng ta thảo luận kĩ phần tham biến Ví dụ Kết #include<iostream> Hello, world ! using namespace std; void showMsg() { cout<<”Hello, world !”; } int main() { showMsg(); return 0; C++ } Chú ý: T r a n g | 78 CuuDuongThanCong.com https://fb.com/tailieudientucntt (80) Chương H{m Vì hàm có kiểu liệu trả luôn trả giá trị cụ thể, nên chúng ta có thể sử dụng trực tiếp loại hàm này các biểu thức tính toán (ví dụ a=b+sin(x)) Điều này là không thể hàm void Khi sử dụng các hàm không có tham số hình thức, ta gọi hàm theo cách sau: tên_hàm(); là cách gọi h{m đúng Nếu gọi hàm theo cách: tên_hàm;, thì dù chương trình dịch không báo lỗi, kết nhiều không chính xác Vì vậy, chúng ta nên gọi hàm theo cách đầu tiên Tham biến và tham trị Cho đến thời điểm n{y, c|c h{m m{ chúng ta đ~ nghiên cứu truyền tham số theo tham trị Điều n{y có nghĩa l{ gọi hàm, các giá trị từ c|c đối số truyền vào hàm chép sang các tham số này, các tham số đó đóng vai trò l{ c|c tham số hình thức, chúng không lưu lại giá trị cho c|c đối số truyền vào, mà các giá trị đó đ~ bị các lệnh h{m l{m thay đổi Ví dụ Giải thích #include <iostream> Nếu tham số a hàm setNum sử dụng trên (đơn l{ int a) thì nó quy định là truyền theo tham trị using namespace std; void setNum(int a) { a = 0; } int main() { Khi truyền theo tham trị, giá trị biến xuất lời gọi hàm này, không thay đổi sau thoát khỏi h{m Điều n{y có nghĩa l{ gi| trị biến b trước gọi hàm là 1, sau gọi hàm, nó nhận giá trị là int b = 1; setNum(b); C++ cout<<b; return 0; T r a n g | 79 CuuDuongThanCong.com https://fb.com/tailieudientucntt (81) Chương H{m } Nếu muốn thay đổi giá trị biến truyền tham số hàm, ta sử dụng khai báo tham biến Với việc quy định các tham số truyền theo tham biến, thì khai báo ta bổ sung vào dấu & trước tên tham số đó Bằng cách này, các biến l{ đối số lời gọi hàm bị l{m thay đổi giá trị sau kết thúc lời gọi hàm Ví dụ Giải thích #include <iostream> Nếu tham số a hàm setNum sử dụng trên (int &a) thì nó quy định là truyền theo tham biến using namespace std; void setNum(int &a) { a = 0; } int main() Khi truyền theo tham biến, giá trị biến xuất lời gọi hàm này, thay đổi sau thoát khỏi h{m Điều n{y có nghĩa l{ gi| trị biến b trước gọi hàm là 1, sau gọi hàm, nó nhận giá trị là { int b = 1; setNum(b); cout<<b; return 0; } Ví dụ C++ Việc sử dụng khai báo hàm theo tham biến có ưu điểm: ta biết rằng, hàm có thể trả giá trị Nếu trường hợp, ta mong muốn hàm trả nhiều giá trị, ta có thể sử dụng hàm void kết hợp với việc truyền tham số cho hàm theo tham biến Ví dụ sau đ}y cho ta thấy rõ điều này Kết T r a n g | 80 CuuDuongThanCong.com https://fb.com/tailieudientucntt (82) Chương H{m #include <iostream> m= 2, n=1 using namespace std; void swap(int &a, int &b) { int c = a; a = b; b = c; } int main() { int m = 1; int n = 2; swap(m, n); cout<<”m=”<<m<<”, n=”<<n; return 0; } Lưu ý: Cách truyền tham biến trên áp dụng cho C++, C ta có thể truyền tham biến nhờ trỏ Cách này, còn hoạt động tốt trên C++ T r a n g | 81 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Giải thích: khai báo hàm swap, tham số a v{ b quy định truyền theo tham biến Hàm này thực việc ho|n đổi giá trị hai tham số hình thức a và b Trong hàm main, hai biến m, n có giá trị tương ứng là và Khi gọi hàm swap, thì hai biến này bị ho|n đổi giá trị cho Do đó, kết in là m=2, n=1 (vì tham số truyền theo tham biến) Như vậy, h{m trường hợp này có thể xem trả giá trị mong muốn (83) Chương H{m Ví dụ Kết #include <iostream> m= 2, n=1 using namespace std; void swap(int *a, int *b) { int *c;//hoặc đơn là c *c = *a; *a = *b; *b = *c; } int main() { int m = 1; int n = 2; swap(&m, &n); cout<<”m=”<<m<<”, n=”<<n; return 0; } Giá trị mặc định tham số hình thức Chương trình Kết #include<iostream> C++ Khi khai báo các tham số hình thức bên hàm Nếu các tham số đó gán giá trị mặc định, thì gọi hàm, chúng ta có vài cách gọi tương ứng với số lượng khác các tham số T r a n g | 82 CuuDuongThanCong.com https://fb.com/tailieudientucntt (84) Chương H{m using namespace std; int add(int a, int b=0, int c=0) { return a+b+c; } int main() { cout<<add(1)<<endl; cout<<add(1,2)<<endl; cout<<add(1,2,3)<<endl; return 0; } Giải thích: H{m add khai báo với ba tham số hình thức Tham số thứ là không thể thiếu, vì nó không quy định giá trị mặc định Với hai tham số b, c còn lại, ta có thể để khuyết Trong trường hợp để khuyết, nó nhận giá trị mặc định m{ ta đ~ g|n cho nó (cụ thể đ}y l{ 0) Do đó, gọi hàm add(1), nó tương ứng với lời gọi hàm add(1,0,0), tức giá trị là tổng 1+0+0 Tương tự, gọi hàm add(1,2) thì tương ứng với add(1,2,0) và cho kết là Khi gọi h{m đầy đủ ba tham số add(1,2,3) cho kết là Chồng chất hàm Ví dụ Kết #include<iostream> C++ Trong C++, hai hàm khác có thể có cùng tên, danh sách tham số chúng phải khác Chúng biết đến với tên gọi là chồng chất hàm Khái niệm chồng chất hàm khác hoàn toàn với khái niệm quá tải hàm mà chúng ta tìm hiểu phần lập trình hướng đối tượng T r a n g | 83 CuuDuongThanCong.com https://fb.com/tailieudientucntt (85) Chương H{m #include<string> abcd using namespace std; int add(int a, int b) { return a+b; } string add(string a, string b) { return a+b; } int main() { cout<<add(1,2)<<endl; cout<<add(“ab”,”cd”)<<endl; return 0; } Giải thích: Hai hàm mà ta xây dựng có cùng tên l{ add H{m add đầu tiên có chức cộng hai số Hàm add thứ hai có chức cộng hai xâu kí tự Kiểu liệu trả và tham số hình thức chúng kh|c Chỉ định inline trước khai báo hàm giúp trình biên dịch nhận biết h{m n{y khai báo là nội tuyến Không có thay đổi nào đ|ng kể h{m bình thường với hàm nội tuyến Chỉ có điểm khác biệt đó l{: với hàm nội tuyến, trình biên dịch khởi tạo thân hàm và chèn nó vào vị trí gọi thời điểm m{ h{m đó gọi, thay vì nó chèn lời gọi hàm Việc làm này cải thiện đ|ng kể tốc độ biên dịch chương trình T r a n g | 84 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Hàm nội tuyến (86) Chương H{m inline type tên_hàm(danh_sách_tham_số) { Thân hàm; } Trong hầu hết các trình biên dịch đại, việc quy định hàm là inline là không cần thiết Nếu hàm có thể sử dụng khai báo inline, thì chương trình dịch tự động tối ưu m~ nguồn để có thể sử dụng nó Ngược lại, hàm không thể sử dụng khai b|o inline, thì chương trình dịch bỏ qua khai báo này Chỉ định inline có tác dụng định hướng cho chương trình dịch Hàm đệ quy H{m đệ quy là hàm gọi lại chính nó Nó hữu dụng các tác vụ xếp (Quicksort) tính toán các biểu thức truy hồi, giải các bài toán giải thuật cùng tên … H{m đệ quy tương ứng với khái niệm quy nạp toán học Ta có thể sử dụng định nghĩa giai thừa theo quy nạp toán học sau { ( ) Và chúng ta có thể xây dựng h{m đệ quy tương ứng với phép tính giai thừa n{y sau Ví dụ Kết #include <iostream> using namespace std; long Fac(long a) { if(a>0) return a*Fac(a-1); else C++ return 1; } T r a n g | 85 CuuDuongThanCong.com https://fb.com/tailieudientucntt (87) Chương H{m int main() { long num = 3; cout<<Fac(num); return 0; } Giải thích: hàm Fac tính giá trị giai thừa a theo công thức tính trên Nếu a>0, thì hàm trả giá trị là a*Fac(a-1), hàm Fac(a-1) lại gọi đến chính nó, và quá trình này tiếp tục giá trị a = Việc sử dụng lời gọi hàm Fac(0) cho kết tương ứng là Lưu ý: h{m đệ quy vòng lặp, nó có thể lặp vô hạn, điều kiện dừng không đảm bảo Ta xét thêm ví dụ sử dụng đệ quy Tính tổng: Với công thức này, ta có thể ph}n tích sau ( ⏟ ) Từ phân tích này, ta dễ dàng thấy công thức dạng truy hồi: { Ví dụ Kết #include <iostream> using namespace std; long S(int n) C++ { if(n == 1) T r a n g | 86 CuuDuongThanCong.com https://fb.com/tailieudientucntt (88) Chương H{m return 1; else return n+S(n-1); } int main() { int n = 3; cout<<S(n); return 0; } Bài tập Xây dựng hàm giải phương trình ( ) √ Xây dựng hàm tính tổng n số nguyên tố đầu tiên, với n là số nhập vào từ bàn phím Xây dựng hàm kiểm tra số có phải là số chính phương hay không Số chính phương l{ số nguyên có thể biểu diễn dạng lũy thừa số nguyên Xây dựng h{m đệ quy để tính tổng n số nguyên dương từ đến n Xây dựng h{m đệ quy để tính x|c định số hạng thứ n d~y sau đ}y { C++ Với, n nhập vào từ bàn phím Cho dãy số nguyên Hãy xây dựng các hàm sau: a Hàm nhập vào số nguyên đó b Hàm xếp số nguyên đó theo thứ tự tăng dần c Hàm tính tổng số nguyên đó d Xuất các kết quả: các số nguyên sau xếp và tổng chúng T r a n g | 87 CuuDuongThanCong.com https://fb.com/tailieudientucntt (89) Chương C|c kiểu liệu có cấu trúc CHƯƠNG CÁC KIỂU DỮ LIỆU CÓ CẤU TRÚC Mảng Một mảng là dãy các phần tử có cùng loại liệu xếp liên tục nhớ máy tính Chúng có thể truy cập theo số nó Điều n{y có ưu điểm là chúng ta có thể khai báo biến giá trị kiểu nguyên nhờ vào khai báo mảng, mà không cần phải khai báo riêng biệt Mảng int Khai báo mảng kiểu_dữ_liệu tên_mảng[số_phần_tử]; Ví dụ string humans[5]; //Khai báo mảng humans có phần tử xâu kí tự int numbers[10]; //Khai báo mảng numbers có phần tử số nguyên Các số mảng đ|nh thứ tự từ vị trí Để truy cập đến phần tử mảng, chúng ta truy cập theo số sau humans[0], humans[1],… tương ứng với các phần tử thứ nhất, thứa hai… mảng humans Khởi tạo mảng Việc khởi tạo giá trị cho mảng có thể sử dụng cặp dấu {} string humans[5] = {“Lan”, “Nam”, “Binh”, “Hoa”, “Hieu”}; Khi truy cập vào mảng theo số, thì humans[0]=”Lan”, humans[1]=”Nam”,… Trang | 88 CuuDuongThanCong.com https://fb.com/tailieudientucntt (90) Chương C|c kiểu liệu có cấu trúc Một cách thức để khởi tạo giá trị cho mảng, là ta sử dụng toán tử gán cho phần tử Để vét toàn mảng (để nhập liệu cho mảng xuất liệu từ mảng làm việc với các phần tử mảng), ta có thể sử dụng vòng lặp for int numbers[10]; for (int i=0; i<10; i++) numbers[i]=i; Mảng nhiều chiều Mảng nhiều chiều có thể xem là mảng mảng Mảng hai chiều là mảng mảng chiều, mảng ba chiều là mảng mảng hai chiều,… //Khai báo ma trận int matrix[4][4];//Ma trận có 16 phần tử (4 dòng, cột) Chúng ta có thể minh họa khai báo ma trận trực quan sau: matrix[1][3] Tương tự, ta có thể tạo các khai báo mảng nhiều chiều khác Việc truy cập vào mảng để xét duyệt các phần tử nó, có thể thực với số câu lệnh lặp lồng chính là số chiều mảng: mảng hai chiều – hai vòng lặp lồng nhau, mảng ba chiều – ba vòng lặp lồng nhau,… C++ //In giá trị toàn mảng for(int i=0; i<4; i++){ for(int j=0;j<4;j++) cout<<matrix[i][j]<<” “; cout<<endl; T r a n g | 89 CuuDuongThanCong.com https://fb.com/tailieudientucntt (91) Chương C|c kiểu liệu có cấu trúc } Mảng giả nhiều chiều Nếu ta khai báo mảng sau //Khai báo ma trận int matrix[4*4];//Ma trận có 16 phần tử thì mảng này gọi là mảng giả nhiều chiều (giả hai chiều) Số phần tử mảng giả hai chiều này số phần tử mảng hai chiều Thực chất mảng giả nhiều chiều là mảng chiều Các thao tác xử lý với mảng giả nhiều chiều thực thi mảng chiều Ta cần lưu ý rằng, việc chuyển đổi số qua lại mảng nhiều chiều và mảng giả nhiều chiều là hoàn toàn có thể thực Ví dụ sau đ}y in giá trị các phần tử theo dạng ma trận cách sử dụng khai báo mảng hai chiều và mảng giả hai chiều Mảng hai chiều int matrix[4][4]; //Nhập mảng for(int i=0; i<4; i++) for(int j=0;j<4;j++) matrix[i][j]=i+j; //In giá trị mảng for(int i=0; i<4; i++){ for(int j=0;j<4;j++) cout<<matrix[i][j]<<” “; cout<<endl; } Mảng giả hai chiều int matrix[4*4]; //Nhập mảng for(int i=0; i<4; i++) for(int j=0;j<4;j++) matrix[i*4+j]=i+j; //In giá trị mảng for(int i=0;i<4;i++){ for(int j=0; j<4;j++) cout<<matrix[i*4+j]<<” “; cout<<endl; } Trong trường hợp mảng chiều, ta có thể không cần khai báo kích thước mảng (ví dụ type tên_hàm(int args[])) Trong trường hợp mảng nhiều chiều, thì có số phần tử chiều thứ là có thể không cần khai báo, còn các chiều còn lại, thiết phải khai báo (ví dụ type tên_hàm(int args[][10][10])) Mảng truyền theo tham biến T r a n g | 90 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Mảng là tham số hình thức: sử dụng mảng làm tham số hình thức, cần lưu ý c|c điểm sau đ}y: (92) Chương C|c kiểu liệu có cấu trúc Cách tốt sử dụng mảng làm tham số hình thức là hãy sử dụng nó dạng trỏ Chi tiết trỏ trình b{y chương sau Bài tập Xây dựng c|c h{m sau đ}y a Nhập vào mảng hai chiều các số nguyên b Thực các phép cộng, nhân hai mảng này c X|c định phần tử lớn nhất, nhỏ dòng d Xây dựng ma trận từ ma trận cũ, đó c|c phần tử ma trận cũ n{y có gi| trị chẵn chính nó cộng với phần tử lớn dòng đó C|c phần tử lẻ chính nó trừ cho phần tử nhỏ dòng đó Tương ứng với số đó, ta xây dựng nên ma trận từ ma trận cũ đ~ cho Ví dụ: Phần tử lớn dòng 1: Phần tử lớn dòng 2: Phần tử nhỏ dòng 1: Phần tử nhỏ dòng 2: Phần tử 1x1 là lẻ, nên nó 1-1=0, v{ đ}y chính là phần tử 1x1 ma trận Tương tự cho các phần tử còn lại Ma trận thu là Cho mảng chiều, viết chương trình ho|n đổi vòng các giá trị mảng đó (phần tử đầu trở thành phần tử cuối, …) Ví dụ {1, 2, 3}, sau ho|n đổi, ta thu {2, 3, 1} Cho ma trận, hãy sử dụng phương ph|p khử Gauss để đưa ma trận này dạng tam giác trên Như tôi đ~ giới thiệu, thư viện chuẩn C++ chứa lớp string mạnh mẽ, mà nó có thể hữu dụng việc thực thi các tác vụ xử lý xâu Tuy nhiên, vì xâu là mảng các kí tự, đó, chúng ta có thể xử lý xâu xử lý trên mảng T r a n g | 91 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Xâu kí tự (93) Chương C|c kiểu liệu có cấu trúc Ví dụ, ta có khai b|o x}u sau char strings [20]; Xâu strings này chứa 20 kí tự Việc khởi tạo giá trị cho x}u ho{n to{n tương tự khởi tạo giá trị cho mảng Tuy nhiên, chúng ta có thêm cách khởi tạo thuận lợi sau strings = “Chao ban”; Khi phân bố vào nhớ, xâu này biểu diễn mảng Tuy nhiên, phần tử cuối cùng mảng kí tự này là phần tử kết thúc xâu, kí hiệu là \0 C h a o b a n \0 10 11 12 13 14 15 16 17 18 19 C++ Việc khai báo xâu theo kiểu mảng ký tự hay theo kiểu string là hoàn to{n tương đương Vì vậy, chúng ta có thể tùy ý lựa chọn cách xử lý chúng Ngoài ra, mảng có thể khai b|o trỏ Vì vậy, với xâu kí tự, chúng ta có ba cách khai báo: sử dụng mảng kí tự, sử dụng trỏ và khai báo xâu string Chi tiết trỏ, chúng ta học chương sau T r a n g | 92 CuuDuongThanCong.com https://fb.com/tailieudientucntt (94) Chương Con trỏ CHƯƠNG CON TRỎ Khi biến lưu v{o c|c ô nhớ, thông thường ta không quan t}m đến cách bố trí theo vị trí vật lý nó, chúng ta đơn truy cập đến các biến theo định danh nó Bộ nhớ m|y tính tổ chức theo các ô nhớ, ô nhớ là kích thước nhỏ mà máy tính có thể quản lý – còn gọi là bytes Mỗi ô nhớ đ|nh dấu theo cách liên tục Các ô nhớ cùng khối ô nhớ đ|nh dấu theo cùng số khối ô nhớ trước đó cộng Theo cách thức tổ chức này, ô nhớ có địa định danh và tất các ô nhớ thuộc vào mẫu liên tiếp Ví dụ, chúng ta tìm kiếm ô nhớ 1776, chúng ta biết nó là ô nhớ nằm vị trí ô nhớ 1775 và 1777, hay chính x|c l{ ô nhớ sau 776 ô nhớ so với ô nhớ 1000 (hay trước ô nhớ 2776 là 1000 ô nhớ) Toán tử tham chiếu & Khi mô tả biến, hệ điều hành cung cấp số lượng ô nhớ cần thiết để lưu trữ giá trị biến Chúng ta không định cách trực tiếp vị trí chính x|c để lưu trữ biến bên mảng các ô nhớ đó May mắn thay, tác vụ này hoàn toàn tự động suốt quá trình Runtime hệ điều hành Tuy nhiên, v{i trường hợp, chúng ta có thể quan tâm đến địa mà các biến lưu trữ để điều khiển chúng Địa mà các biến lưu bên nhớ gọi là tham chiếu đến biến đó Sự tham chiếu đến biến có thể nhận cách bổ sung dấu & trước định danh biến – nó gọi l{ địa biến đó Ví dụ: int a = 10; int *adr = &a; Khi khởi gán biến *adr cho địa biến a, thì từ thời điểm này, việc truy cập tên biến a với tham chiếu & hoàn toàn không liên quan đến giá trị nó, ta nhận giá trị biến a nhờ vào biến *adr Trang | 93 CuuDuongThanCong.com https://fb.com/tailieudientucntt (95) Chương Con trỏ Giả sử biến andy lưu v{o nhớ ô nhớ 1776 Chúng ta có thể minh họa đoạn chương trình sau lược đồ bên int andy = 25; int fred = andy; int* ted = &andy; andy 25 1775 1776 1777 fred & -ted Hình 17 – Tham chiếu trỏ Đầu tiên giá trị 25 gán cho biến andy, biến fred khởi tạo từ biến andy (sao chép giá trị) Biến ted tham chiếu đến địa biến andy, mà không chép giá trị biến này vào ô nhớ nó Toán tử tham chiếu ngược * Một biến tham chiếu đến biến khác gọi là trỏ Con trỏ trỏ đến biến tham chiếu Bằng việc sử dụng trỏ, chúng ta có thể truy cập trực tiếp đến giá trị biến tham chiếu đến Để thực thi điều này, chúng ta đặt trước định danh biến trỏ dấu *, đó, nó đóng vai trò l{ toán tử tham chiếu ngược và nó có thể gọi là “gi| trị trỏ bởi” Bởi vậy, chúng ta có thể viết sau Chúng ta có thể gọi: beth tương ứng với giá trị trỏ ted Để minh họa điều này, chúng ta có thể tham khảo lược đồ sau: T r a n g | 94 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ beth = *ted; (96) Chương Con trỏ ted 1776 1775 1776 1777 25 nhớ 25 beth Hình 18 – Tham chiếu ngược trỏ Lược đồ n{y tương ứng với đoạn chương trình sau beth = ted;//beth tương ứng với ted beth = *ted;//beth tương ứng với giá trị trỏ ted Cần phân biệt chính xác biến ted trỏ đến giá trị 1776, *ted trỏ đến giá trị lưu ô 1776, tức l{ 25 Như vậy, chúng ta cần phải phân biệt cách chính xác hai toán tử: toán tử tham chiếu & và toán tử tham chiếu ngược* Toán tử tham chiếu &: đọc là địa Toán tử tham chiếu ngược *: đọc là giá trị trỏ Như vậy, biến có thể tham chiếu nhờ toán tử & và có thể tham chiếu ngược toán tử * Giả sử chúng ta có int andy = 25; int *ted = &andy; Khi đó, c|c biểu thức sau đ}y cho giá trị đúng (giả sử địa biến andy lưu ô nhớ 1776) C++ andy == 25; &andy == ted;//=1776 *ted == 25; T r a n g | 95 CuuDuongThanCong.com https://fb.com/tailieudientucntt (97) Chương Con trỏ Ta có thể phát biểu tổng quát biểu thức *ted = &andy sau: trỏ *ted trỏ v{o địa andy, tương ứng với địa này ô nhớ, ta có thể nhận giá trị tương ứng là giá trị lưu ô nhớ này Khai báo biến trỏ Ta có thể lấy giá trị mà trỏ trỏ đến cách trực tiếp, nó cần thiết chúng ta muốn khai báo kiểu liệu tương ứng với nó Cú pháp khai báo trỏ sau: type *tên_con_trỏ; Ví dụ Ví dụ Kết #include<iostream> using namespace std; int main() { int *pint; long long *pll; cout<<sizeof(pint)<<endl; cout<<sizeof(pll)<<endl; cout<<sizeof(*pint)<<endl; cout<<sizeof(*pll)<<endl; return 0; } Giải thích: ví dụ này, biến pint v{ pll dùng để lưu địa trỏ, chúng luôn có kích thước mặc định là bytes Các biến *pint và *pll là các T r a n g | 96 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ int *pint; char *pchar; float *pfloat; Trong ví dụ trên, chúng ta khai báo ba trỏ có kiểu liệu khác nhau, chất, chúng – pint, pchar, pfloat là trỏ và chúng có cùng số ô nhớ không gian nhớ (trên hệ thống windows 32bit, chúng chiếm 4byte – ta có thể sử dụng hàm sizeof để kiểm tra kích thước thực trên hệ thống m|y tính sử dụng) Tuy nhiên, liệu mà các trỏ trỏ đến lại có kích thước kh|c tương ứng với int, char và float mà chúng ta đ~ tìm hiểu (tương ứng trên hệ windows 32 bit là 4, 1, 4) (98) Chương Con trỏ biến trỏ vào các kiểu liệu int v{ long long tương ứng Biến int có kích thước bytes và biến long long có kích thước bytes Lưu ý, khai b|o n{y, dấu * không phải là toán tử tham chiếu ngược, nó đơn là trỏ Chúng có cùng kí hiệu, l{ hai thứ hoàn toàn khác Ví dụ Kết #include<iostream> 10 using namespace std; 20 int main() { int fval, sval; int *p; p = &fval; *p = 10; p = &sval; *p=20; cout<<fval<<endl; cout<<sval<<endl; return 0; } Giải thích: Bằng cách sử dụng biến trỏ *p, chúng ta đ~ l{m thay đổi giá trị biến fval và sval Biến trỏ này lần đầu tiên, nó trỏ đến địa biến fval, từ ô nhớ địa này, nó ánh xạ đến giá trị mà ta khởi gán l{ 10 Do đó, gi| trị biến fval |nh xạ tương ứng đến 10 Tương tự cho biến sval Để minh họa trỏ có thể tạo sai khác giá trị cùng chương trình, chúng ta tham khảo ví dụ sau Kết 10 20 C++ Ví dụ #include<iostream> using namespace std; int main() { int fval=5, sval=15; int *p1, *p2; p1 = &fval; p2 = &sval; *p1 = 10; T r a n g | 97 CuuDuongThanCong.com https://fb.com/tailieudientucntt (99) Chương Con trỏ *p2 = *p1; p1 = p2; *p1 = 20; cout<<fval<<endl; cout<<sval<<endl; return 0; } Giải thích: Các biến *p1 và *p2 trỏ đến địa fval v{ sval Như vậy, *p1=10, tạo cho vùng địa mà nó trỏ đến, ánh xạ đến giá trị 10 (có nghĩa l{ thời điểm này fval = 10, sval = 15) Dòng lệnh *p2=*p1 làm cho biến trỏ *p2 trỏ đến giá trị mà *p1 trỏ đến (tức *p2 = 10) Và thời điểm này, biến sval có giá trị tương ứng là 10 (do ánh xạ theo vùng địa mà *p2 trỏ đến) Dòng lệnh p1=p2 g|n địa mà p2 trỏ đến (địa biến sval) cho địa mà p1 trỏ đến (địa biến fval), vậy, thời điểm này sval=fval=10 Dòng lệnh *p1=20 tạo cho vùng địa mà *p1 trỏ đến (cũng l{ địa biến *p2 và fval) ánh xạ đến giá trị 20, nghĩa l{ fval = 20 Vì vậy, kết thúc chương trình, fval = 10, sval = 20 Cả hai biến *p1 v{ *p2 trỏ đến địa biến fval Một trỏ khai báo liệu nó là loại liệu n{o đó Do đó, có biến không phải là trỏ có cùng kiểu liệu, chúng ta có thể sử dụng khai báo thu gọn int *p1, *p2, num; Con trỏ, mảng và xâu kí tự Như tôi đ~ nói trên, mảng có thể khai báo theo kiểu truyền thống theo cấu trúc trỏ Ví dụ, tôi có mảng số nguyên, chúng có 20 phần tử char xau[20]; //Khai báo truyền thống char *xau; //Khai báo trỏ string xau;//Khai báo xâu Khởi tạo giá trị cho trỏ nó đóng vai trò là mảng Thứ nhất: ta sử dụng phép gán để chép toàn mảng lên trỏ T r a n g | 98 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ int numbers[20]; //Khai báo truyền thống int *p = new int[20]; //Khai báo trỏ Hoặc ta có khai báo biến xâu kí tự theo cách sau (100) Chương Con trỏ p = numbers;//p là trỏ, numbers là mảng Thứ hai: khởi tạo trực tiếp cho phần tử Chương trình Kết #include<iostream> Nhap dai: using namespace std; void Input(int *p, int length) { for (int i=0; i<length; i++){ cin>>*(p+i); ======== } 6890 } void Output(int *p, int length) { for (int i=0; i<length; i++){ cout<<*(p+i)<<” “; } } int main() { int *a; int lengtha; cout<<”Nhap dai: “; cin>>lengtha; Input(a, lengtha); cout<<”========”<<endl; Output(a, lengtha); return 0; } Giải thích: biến trỏ luôn truyền theo kiểu tham biến, đó, chúng ta không cần bổ sung dấu & v{o bên trước tên biến H{m Input dùng để nhập mảng số nguyên có độ d{i length, h{m Output dùng để xuất liệu từ mảng nguyên đó Khi truy xuất từ biến trỏ, ngoài cách xuất trên, ta T r a n g | 99 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ int *p; for (int i=0; i<20; i++){ *(p+i)=numbers[i]; } Chương trình nhập – xuất liệu việc sử dụng trỏ (101) Chương Con trỏ còn có thể viết p[i] tương ứng mảng – p[i] v{ *(p+i) là phần tử thứ i Nghĩa l{ ta hoàn toàn có thể viết cin>>p[i]; cout<<p[i]; cin>>*(p+i); cout<<*(p+i); int c = p[i]+q[j]; int c = *(p+i)+*(q+j); Việc sử dụng trỏ để thay cho x}u ho{n to{n tương tự Nhưng hãy lưu ý, phần tử cuối cùng nó là phần tử \0 Các phép toán số học trên trỏ Các phép toán số học trên trỏ tương đối khác với phép toán số học trên số nguyên Chúng ta tham khảo c|c phép to|n tương ứng với nó Phép cộng và phép trừ Giả sử, chúng ta có ba trỏ khai b|o sau char *pchar; short *pshort; int *pint; và các biến trỏ này trỏ vào các ô nhớ 1000, 2000 và 3000 Khi ta viết pchar++; pshort++; pint++; nó nhảy sang địa ô nhớ (ô nhớ dịch sang phải đơn vị, tức l{ tương ứng với 1001, 2002 và 3004) Điều n{y đơn giản Kiểu liệu char chiếm byte, nên ô l{ địa ô +1 Kiểu liệu short chiếm byte, nên địa ô +2 Kiểu liệu long chiếm byte, nên địa ô là +4 Chương trình Kết int *p; for(int i=0; i<4; i++){ C++ *(p+i)=i; cout<<*(p+i)<<" "<<(p+i)<<endl; T r a n g | 100 CuuDuongThanCong.com https://fb.com/tailieudientucntt (102) Chương Con trỏ } Ta có thể thấy giá trị v{ vùng địa biến trỏ nguyên kết trên Dễ nhận thấy địa tăng theo byte (0x7ffd5000 0x7ffd5004,…) Để hiểu rõ hơn, ta có thể quan sát lược đồ sau đ}y 1000 1001 pchar pchar++ 2000 pshort 3000 3001 3002 plong 2001 2002 2003 pshort++ 3003 3004 3005 3006 3007 plong++ Hình 19 – Tăng/Giảm địa trỏ Kết n{y tương đương với pchar+1, pshort+1 và plong+1 Ho{n to{n tương tự với toán tử ++p, p và p Toán tử tham chiếu ngược và toán tử tăng-giảm Cả hai toán tử tăng v{ giảm có độ ưu tiên cao to|n tử tham chiếu ngược Nếu ta đặt Ta cần chú ý rằng, biểu thức này hoàn toàn khác với (*p)++; T r a n g | 101 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ *p++; Do toán tử ++ có độ ưu tiên cao to|n tử *, nên biểu thức n{y tương ứng với *(p++) Vì vậy, biểu thức n{y có nghĩa l{ lấy giá trị ánh xạ ô nhớ (103) Chương Con trỏ Trong biểu thức này, dấu () có độ ưu tiên cao nhất, nên biểu thức *p thực trước, đó, gi| trị biểu thức này là giá trị ánh xạ ô nhớ cộng thêm Biểu thức *p++ = *q++; Bởi vì toán tử ++ có độ ưu tiên cao to|n tử *, hai biến p v{ q tăng, vì chúng là toán tử hậu tố, nên toán tử * thực trước, sau đó tăng địa biến tương ứng Như vậy, biểu thức trên tương ứng với *p = *q; ++p; // hay p=p+1; ++q; // hay q=q+1 Trong trường hợp này, chúng ta nên sử dụng dấu () để có cách nhìn nhận rõ r{ng Con trỏ trỏ vào trỏ C++ cho phép sử dụng trỏ đa tầng, nghĩa l{ trỏ trỏ vào trỏ Để khai báo trỏ loại này, chúng ta cần bổ sung thêm vào biến trỏ toán tử * a b c ‘z’ 7230 8092 7230 8092 10502 Giá trị biến viết ô Dưới ô l{ địa tương ứng ô đó nhớ Địa biến a là 7230, giá trị ô nhớ tương ứng l{ ‘z’ Biến *b trỏ vào địa biến a và giá trị tương ứng T r a n g | 102 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ char a; char *b; char **c; a = ‘z’; b = &a; c = &b; Để minh họa điều này, chúng ta có thể tạo địa ngẫu nhiên nhớ cho biến, ví dụ là 7230, 8092 và 10502 (104) Chương Con trỏ biến b ánh xạ đến giá trị ô nhớ biến a – tức giá trị l{ ‘z’ Biến c trỏ v{o địa b, nó ánh xạ gián tiếp đến giá trị a – tức giá trị ‘z’ Sự tương ứng trỏ trỏ vào trỏ khác và mảng hai chiều Chương trình Kết #include <iostream> 012 using namespace std; 123 int main() 234 { //Khai báo ma trận int **matrix; //Khởi tạo ma trận matrix = new int*[3];//dòng for(int i=0; i<3; i++)//phần tử matrix[i] = new int[3]; //Nhập ma trận for (int i=0; i<3; i++) for (int j=0; j<3; j++) { *(*(matrix+i)+j)=i+j; } //Xuất ma trận for (int i=0; i<3; i++){ for (int j=0; j<3; j++) cout<<*(*(matrix+i)+j)<<” “; cout<<endl; } //Xóa ma trận delete[] matrix; return 0; } Từ trở đi, xử lý bài toán trên mảng, ta hoàn toàn có thể sử dụng trỏ để xử lý Chúng ho{n to{n tương đương Chỉ có khác biệt khai báo: khai báo theo kiểu truyền thống, ta cần kích thước khai báo, còn khai báo theo kiểu trỏ, ta có thể định kích thước sau nhờ vào toán tử new Chúng ta tìm hiểu chi tiết toán tử new chương T r a n g | 103 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Việc khai báo biến **c trên, có thể xem l{ mảng hai chiều Chúng ta lại xét bài toán ma trận (105) Chương Con trỏ Con trỏ void Con trỏ void là loại trỏ đặc biệt Trong C++, void dùng để quy định không tồn kiểu liệu (hay kiểu liệu rỗng) Vì vậy, trỏ void là trỏ trỏ vào giá trị có kiểu liệu void (cũng vì lẽ đó, m{ nó không x|c định độ dài và thuộc tính tham chiếu ngược) Chương trình Kết #include<iostream> y, 1603 using namespace std; void increase(void* data, int psize) { if(psize==sizeof(char)) { char* pchar; pchar=(char*)data; ++(*pchar); }else if(psize==sizeof(int)) { int* pint; pint = (int*)data; ++(*pint); } } int main() { char a = ‘x’; int b = 1602; increase(&a, sizeof(a)); increase(&b, sizeof(b)); cout<<a<<”, “<<b<<endl; return 0; } Giải thích: hàm increase có hai tham số: tham số data là trỏ void, tham số psize l{ kích thước trỏ data Câu lệnh if kiểm tra điều kiện xem biến trỏ data thuộc kiểu liệu nào – psize==sizeof(char) thì T r a n g | 104 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Con trỏ void cho phép trỏ sang kiểu liệu bất kì Nhưng chuyển đổi, chúng có giới hạn lớn: liệu trỏ chúng không thể trực tiếp tham chiếu ngược, và vì nguyên nhân này, chúng ta cần ép kiểu địa trỏ void sang trỏ khác mà nó có kiểu liệu cụ thể trước tham chiếu ngược trở lại (106) Chương Con trỏ nó là trỏ kiểu char, tương tự psize==sizeof(int) thì nó là trỏ kiểu int Vì đ}y, ta chưa x|c định nó là trỏ kiểu gì, nên ta sử dụng tham số là trỏ void Nếu là trỏ char, ta sử dụng biến trỏ pchar để khởi tạo giá trị cho nó cách ép kiểu từ trỏ void Hoàn to{n tương tự cho biến trỏ pint Mục đích hàm increase là tìm giá trị tham số data Trong hàm main, ta sử dụng hai biến char và int Giá trị kí tự ‘x’ l{ kí tự ‘y’, số 1602 là 1603 Con trỏ null Một trỏ null là trỏ dùng để khởi gán cho biến trỏ có kiểu liệu bất kì Trong C++, nó quy định là int *p; p = 0; char *c; c = 0; Ta không nên nhầm lẫn trỏ null và trỏ void Một trỏ null là giá trị cụ thể mà trỏ có thể trỏ đến, và nó có thể trỏ đến “mọi vị trí” Trong đó, trỏ void là trỏ đặc biệt, nó có thể trỏ đến “v{i vị trí” Con trỏ hàm C++ cho phép thực thi tính toán với trỏ trỏ vào hàm Khi thực thi điều này, nó xem h{m l{ tham số trỏ vào hàm khác, chúng không tồn tham chiếu ngược Để khai báo trỏ hàm, chúng ta cần khai báo nó khai b|o prototype cho hàm, tên hàm bao dấu () đồng thời bổ sung * phía trước tên nó Kết 12 C++ Chương trình #include<iostream> using namespace std; int addition(int a, int b) { return a+b; } int substraction(int a, int b) { return a-b; T r a n g | 105 CuuDuongThanCong.com https://fb.com/tailieudientucntt (107) Chương Con trỏ } int operation (int x, int y, int (*functocall) (int, int)) { int g; g=(*functocall)(x,y); return (g); } int main() { int m, n; int (*minus)(int, int) = substraction; m = operation(7, 5, addition); cout<<m<<endl; n = operation(20, m, minus); cout<<n; return 0; } Giải thích: Hai hàm addition và substraction thực các chức cộng và trừ hai số nguyên Hàm operation gọi h{m tương ứng với trỏ hàm định Trong hàm main, trỏ hàm minus tương ứng với hàm substraction Khi gọi hàm operation, tùy theo trỏ h{m quy định tham số thứ ba, nó trỏ đến h{m tương ứng định: operation(7, 5, addition) – trỏ đến hàm addition, tức cộng hai số v{ Tương tự, operation(20, m, minus) tương ứng với operation(20, m, substraction) và cho kết là phép trừ 20 cho m ∫ ( ) ∫ ( ) C++ Bài tập Hãy xây dựng ba hàm cộng hai số nguyên addi, cộng hai số thực addf và cộng hai xâu adds cách sử dụng trỏ hàm Hàm tổng quát đặt tên là add Sử dụng trỏ h{m để xây dựng hàm giải phương trình bậc và phương trình bậc Sử dụng trỏ h{m để tính các tích phân sau phương ph|p hình thang hình chữ nhật (phải, trái trung vị) T r a n g | 106 CuuDuongThanCong.com https://fb.com/tailieudientucntt (108) Chương 10 Bộ nhớ động CHƯƠNG 10 BỘ NHỚ ĐỘNG Mục đích tạo biến trỏ là không xử lý các tác vụ tính toán, mà còn có thể quản lý nhớ m|y tính Để thực điều này, chúng ta cần khai báo số ô nhớ cung cấp cho biến trỏ, không còn cần dùng đến chúng nữa, chúng ta có thể giải phóng các ô nhớ n{y Để thực tác vụ này, C++ cung cấp cho ta hai toán tử là new và delete Toán tử new và new[] Để yêu cầu nhớ động, chúng ta sử dụng toán tử new, theo sau nó là kiểu liệu Nếu nó là mảng nhiều phần tử, thì số phần tử ấn định bên dấu [] sau kiểu liệu Khi đó, nó trả trỏ trỏ vào khối ô nhớ đầu tiên int*num = new int;//Khai báo biến trỏ int byte int*nums = new int[10];//Khai báo biến trỏ nums với 4*10 bytes Tôi xin nhắc lại lần nữa, kiểu liệu tương ứng với số lượng các ô nhớ quy định sẵn Như trường hợp này, trên hệ điều hành windows 32bit, biến num chiếm bytes nhớ giá trị Còn biến nums thì chiếm 40 byte nhớ, tương ứng với giá trị Nếu sử dụng h{m sizeof để kiểm tra kích thước biến *nums trường hợp này, ta nhận Sở dĩ là vì, sau khởi tạo 10 phần tử kiểu int, trỏ đặt vào phần tử đầu tiên, cho nên, sử dụng sizeof trường hợp này là sizeof phần tử đầu tiên (tức kiểu int) Do đó, kết thu là bytes Khi khởi tạo cho biến trỏ trỏ vào biến trỏ khác (tạm gọi là biến trỏ nhiều tầng) Ta cần khởi tạo cho biến trỏ chung Tương ứng với biến trỏ chung, có mảng các biến trỏ khác tương ứng Do đó, ta cần khởi tạo cho dãy biến trỏ này, nhờ vào vòng lặp for Tương tự vậy, chúng ta có thể khởi tạo cho biến trỏ đa tầng (có thể là hai, ba, bốn…) int***num; int length = 10; //khởi tạo biến trỏ chung num = new int**[length]; Trang | 107 CuuDuongThanCong.com https://fb.com/tailieudientucntt (109) Chương 10 Bộ nhớ động //khởi tạo biến trỏ tầng for (int i=0; i<length; i++) num[i]=new int*[length]; //khởi tạo biến trỏ tầng for (int i=0; i<length; i++) for(int j=0; j<length; j++) num[i][j]=new int[length]; Để truy cập đến ô nhớ, ta có thể truy cập theo num[i][j][k] *(*(*(num+i)+j)+k) Như vậy, ta có thể thấy rằng, thực chất trỏ l{ mảng Chỉ có điểm khác biệt là mảng có kích thước cố định khai báo, con trỏ có kích thước định ta cần sử dụng toán tử new Bộ nhớ động yêu cầu chương trình, v{ hệ điều hành cung cấp cho nó từ nhớ heap Tuy nhiên, nhớ m|y tính hữu hạn, và nó có thể bị cạn kiệt Chính vì lẽ đó, chúng ta cần đến kĩ thuật để kiểm tra tình trạng này nhớ C++ (và chí C) cung cấp cho ta hai phương thức chuẩn để kiểm tra: vượt qua ngoại lệ (throw bad_alloc) và không vượt qua ngoại lệ (nothrow bad_alloc) Bằng việc sử dụng ngoại lệ là bad_alloc, hệ thống bỏ qua quá trình cung ứng ô nhớ bị thất bại (theo mặc định) Ngoại lệ là tính mạnh mẽ C++, chúng ta tìm hiểu chi tiết chương sau Nếu gặp ngoại lệ, chúng ta cần cho biết có bỏ qua để tiếp tục chạy chương trình hay không Toán tử new cung cấp theo phương thức mặc định là vượt qua (throw bad_alloc) bobby = new (nothrow) int[5]; //Không vượt qua ngoại lệ, chương trình cố gắng tiếp tục Nếu sử dụng vượt qua ngoại lệ throw bad_alloc, trường hợp cung ứng ô nhớ bị thất bại, trỏ trả trỏ null, v{ chương trình tiếp tục Chúng ta cần kiểm tra biến trỏ bobby Nếu nó là null thì quá trình cung cấp nhớ động thất bại v{ ngược lại thì thành công Đ}y l{ c|ch làm thủ công, chúng ta có thể sử dụng cấu trúc try… catch để xử lý tình T r a n g | 108 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ bobby = new int[5]; //Nếu thất bại, ngoại lệ bị vượt qua – throw bad_alloc Nếu không vượt qua ngoại lệ, mà cố gắng tiếp tục chạy chương trình, thì (110) Chương 10 Bộ nhớ động này Chi tiết, chúng ta thảo luận kĩ phần sau Lưu ý rằng, hai ngoại lệ này nằm thư viện <new> Toán tử delete và delete[] Nếu biến trỏ không cần dùng đến nữa, chúng ta có thể xóa bỏ các ô nhớ nó để giải phóng nhớ động Qu| trình n{y thực thi toán tử delete delete num; delete[] nums; Cách viết thứ dùng để giải phóng trỏ đơn tầng Cách thứ hai dùng cho trỏ đa tầng (hai tầng trở lên) Toán tử delete có tác dụng với trỏ khởi tạo toán tử new trỏ null C++ Chương trình Kết #include <iostream> Allocation is ok #include <new> Allocation is ok using namespace std; Allocation is ok int main() Allocation is ok { Allocation fail double *ptr[ 5]; for ( int i = 0; i < 5; i++ ) { ptr[ i ] = new (nothrow) double[ 50000000 ]; if (ptr[i]!=0) cout << "Allocation is ok"; else cout<<”Allocation fail”; } delete[] ptr; return 0; } Trong ANSI-C, hàm malloc, calloc, realloc v{ free dùng thay cho new và delete và hiển nhiên chúng hoạt động trên C++ Chúng ta không thảo luận chi tiết h{m n{y, vì theo xu hướng lập trình C++ đại, người ta sử dụng hai toán tử new và delete mà ta thảo luận trên T r a n g | 109 CuuDuongThanCong.com https://fb.com/tailieudientucntt (111) Chương 11 Kiểu liệu struct và Con trỏ struct CHƯƠNG 11 KIỂU DỮ LIỆU STRUCT VÀ CON TRỎ STRUCT Kiểu liệu mảng m{ chúng ta đ~ thảo luận trên giúp chúng ta lưu tập liệu cùng loại Nếu chúng ta không đơn lưu trữ cùng loại liệu, mà có thể là nhiều kiểu liệu khác nhau, thì mảng không thể giải vấn đề Trong C++ (và C) cung cấp cho chúng ta kiểu liệu giúp ta giải vấn đề n{y, đó l{ struct Struct Một liệu struct là nhóm các phần tử có thể có kiểu liệu kh|c đặt cùng tên Các phần tử liệu n{y gọi là các thành viên struct Cấu trúc khai b|o struct C++ sau struct tên_struct{ type thành_viên_1; type thành_viên_2; … } [tên_đối_tượng_struct]; Trong đó: struct là từ khóa tên_struct là tên kiểu liệu struct m{ ta định nghĩa thành_viên_1, … l{ c|c phần tử thành viên struct tên_đối_tượng_struct: là tên biến thuộc kiểu tên_struct Hai c|ch khai b|o sau đ}y l{ tương đương Ví dụ Ví dụ struct product{ struct product{ int weight; int weight; float price; float price; } }apple, banana, melon; product apple; product banana, melon; Giải thích: hai ví dụ trên product là kiểu liệu struct mà ta tạo Nó gồm có hai th{nh viên l{ weight v{ price Tương ứng với kiểu liệu Trang | 110 CuuDuongThanCong.com https://fb.com/tailieudientucntt (112) Chương 11 Kiểu liệu struct và Con trỏ struct này, ta có các biến apple, banana, melon Để khai báo nó, ta có thể sử dụng hai c|ch theo hai ví dụ này Cần lưu ý rằng, các biến apple, banana, melon là các biến thuộc kiểu liệu product Để truy cập đến các thành viên biến struct, ta sử dụng toán tử chấm (.) apple.weight = 200; apple.price = 2; banana.weight = 150; banana.price = 1; … Chúng ta có thể hiểu apple.weight là khối lượng táo, apple.price là giá tiền táo Có thể xem thành viên biến struct là biến độc lập mà ta có thể truy cập cách tên_biến.phần_tử_thành_viên Ta hoàn toàn có thể thực các phép to|n tương ứng với liệu th{nh viên n{y (nghĩa l{ c|c phép to|n đó tương ứng với các phép toán trên kiểu liệu các phần tử thành viên đó: phần tử thành viên là kiểu nguyên thì ta có thể thực các phép toán số nguyên với thành viên này, phần tử thành viên là kiểu xâu thì có thể thực thi các phép toán với xâu cho biến thành viên này,…) Ví dụ, tôi mua 400gam táo, và 300g chuối Giá 100g táo là 1$, và 100g chuối là 0.7$ Cần tính toán số tiền mà tôi cần trả Ví dụ apple.weight = 400; apple.price = (float)apple.weight*1/100; banana.weight = 300; banana.price = banana.weight*0.7/100; float money = apple.price+banana.price; Như ví dụ trên, tôi có thể thực các phép toán số học với các phần tử thành viên là kiểu số c|c biến số bình thường C++ Struct là kiểu liệu người dùng định nghĩa (nhờ vào từ khóa struct – từ khóa struct viết thường) Ta có thể khai báo mảng các phần tử thuộc kiểu struct Ví dụ sau đ}y minh họa cho việc mua bán siêu thị T r a n g | 111 CuuDuongThanCong.com https://fb.com/tailieudientucntt (113) Chương 11 Kiểu liệu struct và Con trỏ struct Bài toán: Trong siêu thị, giá táo và chuối ấn định trên sản phẩm tùy thuộc chất lượng sản phẩm, không phụ thuộc vào khối lượng nó a Tính khối lượng hàng hóa (kg) mà người tiêu dùng mua b Nếu khách hàng mua hàng trị giá trên 10$, thì người tiêu dùng khuyến mãi Hãy kiểm tra xem người tiêu dùng có khuyến mãi hay không Biết số lượng táo và chuối người tiêu dùng lựa chọn Kết Buy apples weight and price 100 Continue to buy apples (y/n) ?y weight and price 200 Continue to buy apples (y/n) ?n Buy bananas weight and price 150 Continue to buy bananas (y/n) ?y weight and price 250 Continue to buy bananas (y/n) ?n Total weight: 0.7 No Promotion C++ Chương trình #include<iostream> using namespace std; #define MAX 10 struct product{ int weight; float price; }apples[MAX], bananas[MAX]; /* Khai báo hàm */ void buyProducts(product pd, string name, int &weight, float &price) { int i = 0; cout<<”Buy “<<name<<endl; while (true){ cout<<”weight and price: <<endl; cin>>pd[i].weight; cin>>pd[i].price; weight += pd[i].weight; price += pd[i].price; cout<<”Continue to buy “<<name<<” (y/n) ?”; char yn; cin>>yn; if((yn==’n’)||(yn==’N’)) break; else i++; } } int main() { T r a n g | 112 CuuDuongThanCong.com https://fb.com/tailieudientucntt (114) Chương 11 Kiểu liệu struct và Con trỏ struct int weight = 0; float price = 0; buyProducts(apples, “apples”, weight, price); buyProducts(bananas, “bananas”, weight, price); cout<<”Total weight: “<<(float)weight/1000<<endl; if(price>10) cout<<”Promotion”; else cout<<”No Promotion”; return 0; } Giải thích: Trong phần thực thi chương trình, người tiêu dùng đ~ mua hai táo và hai chuối Quả táo thứ có khối lượng 100g và giá 2$, táo thứ hai – 200g và 3$ Quả chuối thứ có khối lượng 150g và giá 1$, chuối thứ hai – 250g và 3$ T r a n g | 113 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Mảng apples và bananas thuộc kiểu liệu product Hàm buyProducts dùng để tính toán khối lượng và tổng giá hàng hóa loại mua Nếu tham số products hàm là apples, thì nó tính tương ứng với khối lượng tổng cộng và tổng đơn giá apples Tương tự cho bananas Ta sử dụng vòng lặp while vì ta không biết chính xác số lượng h{ng hóa m{ người dùng lựa chọn Tuy nhiên, số tổng không thể vượt số MAX m{ ta đ~ định nghĩa Biến weight v{ price truyền theo tham biến, đó, trước phép cộng dồn (toán tử cộng đồng +=), ta không cần khởi tạo giá trị cho chúng, ta có thể khởi tạo cho chúng đầu hàm main Nếu khởi tạo đầu hàm buyProducts, thì ta có thể tính khối lượng và giá loại mà thôi (khi đó, muốn tính toán cho hai sản phẩm, ta bổ sung thêm biến để lưu lại các giá trị này) Sau gọi hàm này hai lần, tương ứng với apples và bananas, thì biến weight lưu lại tổng khối lượng hai sản phẩm, biến price lưu lại giá hai sản phẩm Khối lượng quy đổi sang kg, nên ta chia cho 1000 Nếu quy định đơn vị là kg, thì điều này là không cần thiết (115) Chương 11 Kiểu liệu struct và Con trỏ struct Con trỏ struct Tương ứng với mảng struct, ta có trỏ trỏ vào struct Với trỏ trỏ vào struct, ta có thể tạo mảng struct động Khai báo trỏ struct: có hai cách khai báo struct product{ int weight; float price; }*apples; struct product{ int weight; float price; }; product* apples; Tham chiếu: ho{n to{n tương tự trỏ trỏ vào kiểu liệu nguyên thủy struct product{ int weight; float price; }apples1; … products* apples2; apples2 = &apples1; Truy cập giá trị phần tử thành viên: có hai cách truy cập apples2->weight *(apples2.weight) apples2->price *(apples2.price) struct product{ int weight; float price; }*apples2; … apples2->weight struct product{ int *weight; float price; }apples2; … *apples2.weight T r a n g | 114 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ta cần lưu ý khác apples2->weight và *apples2.weight Trường hợp đầu nó tương đương với *(apples2.weight) v{ theo độ ưu tiên toán tử, dấu () thực trước tiên, nghĩa l{ th{nh viên weight apples2 Tiếp đó l{ phép to|n * Điều này có thể hiểu là trỏ apples2 trỏ v{o địa biến thành viên weight apples Trường hợp thứ hai, là giá trị trỏ biến thành viên weight apples2 (toán tử có độ ưu tiên cao to|n tử *) (116) Chương 11 Kiểu liệu struct và Con trỏ struct Struct lồng Struct có thể khai báo lồng Chúng ta có thể khai báo lồng bên trong, khai báo Khai báo lồng struct family{ string father; string mother; struct children{ string son; string daughter; }first, second; }myfamily; Khai báo struct children{ string son; string daughter; }; struct family{ string father; string mother; children first, second; }myfamily; Khi đó, việc truy cập tương tự trên myfamily.first.son … Nếu là trỏ, ta có c|ch truy cập tương tự struct family{ string father; string mother; struct children{ string son; string daughter; }*first, second; }*myfamily; myfamily->first->son myfamily->second.son Kích thước struct theo lý thuyết nó l{ kích thước tổng cộng các liệu thành viên Tuy nhiên, theo cách thức tổ chức nhớ, các liệu thành viên struct xếp liền kề Việc tổ chức nhớ hệ thống máy tính 32bit theo xu hướng là nhóm bytes Điều n{y có nghĩa l{, liệu thành viên thứ đ~ lấp vào số byte nhớ, và còn thừa, thì hệ điều hành xem xét để đưa liệu thành viên vào Nếu liệu thành viên chiếm số lượng các byte nhớ còn thừa, thì nó lấp vào phần nhớ còn thừa đó Nhưng vượt quá, thì hệ thống định hướng bổ sung thêm nhóm byte nhớ để chứa liệu thành viên (nếu cần nhiều T r a n g | 115 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Kích thước nhớ struct (117) Chương 11 Kiểu liệu struct và Con trỏ struct thì cung cấp tiếp nhóm byte nữa) Còn số byte còn thừa, nó để trống Ví dụ struct number{ char a; int b; char c; }mynum; … cout<<sizeof(mynum); //12 Giải thích: Ví dụ struct number{ char a; char c; int b; }mynum; … cout<<sizeof(mynum); //8 char char int T r a n g | 116 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong ví dụ 1: Trong ví dụ 2: char – 1byte Cũng tương tự ví dụ Dữ liệu thành int – 4byte viên char thứ chiếm byte, hệ Đầu tiên, liệu thành viên char thống cung cấp byte, thừa lấp đầy nhóm 4byte, char chiếm byte Kiểu liệu thành viên tiếp 1byte nên còn thừa byte Dữ liệu theo kiểu char, chiếm 1byte, nó thành viên là int Vì int có thể lấp vào số byte còn thừa Tại chiếm 4byte, thừa có thời điểm này, còn thừa byte Tiếp byte, nó không đủ chỗ cho int Do đó, theo, ta xét đến liệu thành viên hệ thống tự động cung cấp thêm int Dữ liệu này chiếm byte, byte để lấp đầy phần còn thừa Tiếp còn thừa byte, đó hệ thống theo, cung cấp nhóm byte cho lấp đầy byte này và cung cấp int Không có byte thừa trường thêm byte cho int Như vậy, số hợp này Kiểu liệu thành viên byte trường hợp này là char còn lại chiếm 1byte, hệ (1+1+2)+4=8 byte thống cung cấp cho nó byte, còn thừa byte Số byte thừa này lấp đầy Như vậy, số byte trường hợp này là (1+3)+4+(1+3)=12 byte Như vậy, tùy vào kiểu liệu thành viên, ta cần chọn cách xếp hợp lý để có kết tối ưu nhớ Hãy quan sát lược đồ xếp nhớ hướng tối ưu bên (trường hợp 2) (118) Chương 12 C|c kiểu liệu khác CHƯƠNG 12 CÁC KIỂU DỮ LIỆU KHÁC Kiểu liệu tự định nghĩa Ta có thể định nghĩa kiểu liệu thông qua kiểu liệu đ~ tồn typedef kiểu_dữ_liệu_tồn_tại tên_kiểu_dữ_liệu_mới; Sau khai báo kiểu liệu mới, ta có thể sử dụng nó kiểu liệu thông thường typedef char C; typedef char* pchar; typedef char string50[50]; … C mychar; pchar p; string50 str; Kiểu liệu union thường Với từ khóa union, ta có thể khai báo kiểu liệu từ các kiểu liệu đ~ tồn union Tên_kiểu_union{ kiểu_dữ_liệu_thành_viên_1 tên_thành_viên_1; kiểu_dữ_liệu_thành_viên_2 tên_thành_viên_2; … }biến_kiểu_dữ_liệu_union; Khi khái báo kiểu union, các liệu thành viên khai báo trên cùng không gian nhớ vật lý Kích thước nó l{ kích thước kiểu liệu thành viên lớn Ví dụ union myType{ char C; double Db; } newType; //8 byte, vì double = 8byte, char = 1byte Trang | 117 CuuDuongThanCong.com https://fb.com/tailieudientucntt (119) Chương 12 C|c kiểu liệu khác … //Sử dụng biến_kiểu_dữ_liệu_union.tên_thành_viên… Kiểu liệu union ẩn danh Ho{n to{n tương tự với kiểu liệu union thường, ta không cần định tên biến union tạo union Tên_kiểu_union{ kiểu_dữ_liệu_thành_viên_1 tên_thành_viên_1; kiểu_dữ_liệu_thành_viên_2 tên_thành_viên_2; … }; Khi khai báo biến thuộc kiểu union ẩn danh, ta có thể khai b|o sau Ví dụ … union Tên_kiểu_union tên_biến; tên_biến.tên_thành_viên … Kiểu liệu enum Kiểu liệu enum tạo có thể chứa nhiều liệu khác mà không có giới hạn giá trị enum colors{black=0, blue, green, cyan, red} mycolor; enum colors{black=0, blue, green, cyan, red}; colors mycolor; Enum là loại liệu chứa các biến đ|nh số Do đó, các giá trị nó luôn là số nguyên (để làm số) T r a n g | 118 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ enum Tên_kiểu_enum{ giá_trị_1; giá_trị_2; … }Tên_thể_hiện_enum; Chúng ta có thể tham khảo thêm các ví dụ sau đ}y (120) Chương 12 C|c kiểu liệu khác Ví dụ #include<iostream> using namespace std; enum colors{red, green, blue}; C++ int main() { for(int i=0; i<3; i++) if (colors(i)==green) cout<<"green"<<endl; return 0; } T r a n g | 119 CuuDuongThanCong.com https://fb.com/tailieudientucntt (121) Chương 13 Lập trình hướng đối tượng CHƯƠNG 13 LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG Lịch sử hình thành Trước kĩ thuật lập trình hướng đối tượng đời, người đ~ trải qua các thời kì lập trình tuyến tính, lập trình hướng thủ tục Lập trình tuyến tính M|y tính đầu tiên lập trình mã nhị phân, sử dụng các công tắc khí để nạp chương trình Cùng với xuất các thiết bị lưu trữ lớn và nhớ m|y tính có dung lượng lớn, nên các ngôn ngữ lập trình cấp cao bắt đầu xuất Các ngôn ngữ lập trình n{y thiết kế làm cho công việc lập trình trở nên đơn giản C|c chương trình ban đầu chủ yếu liên quan đến tính toán, chúng tương đối ngắn Chúng chủ yếu chạy theo các dòng lệnh cách tuần tự, dòng trước chạy trước, dòng sau chạy sau Nhược điểm: o Nếu ta cần sử dụng đoạn lệnh n{o đó nhiều lần, thì ta phải chép nó nhiều lần o Không có khả kiểm soát phạm vi nhìn thấy biến o Chương trình dài dòng, khó hiểu, khó nâng cấp Lập trình hướng thủ tục Với nhược điểm trên, đòi hỏi có ngôn ngữ lập trình thay Đó chính l{ nguyên nh}n đời ngôn ngữ lập trình hướng thủ tục Về chất, chương trình chia nhỏ thành c|c modul (đơn vị chương trình) Mỗi đơn vị chương trình chứa các hàm hay thủ tục (nên gọi là hướng thủ tục) Tuy tách rời thành các modul riêng biệt, ngôn ngữ lập trình hướng thủ tục đảm bảo thông tin thông suốt các modul nhờ v{o chế hoạt động h{m, chế truyền theo tham biến và tham trị Với lập trình hướng thủ tục, chương trình lớn có thể chia nhỏ Trang | 120 CuuDuongThanCong.com https://fb.com/tailieudientucntt (122) Chương 13 Lập trình hướng đối tượng th{nh c|c modul, để lập trình viên có thể đảm nhận Tiêu biểu số này là C, Pascal Nhược điểm: o Các hàm và thủ tục thường gắn kết với nhau, muốn nâng cấp chương trình, thường phải chỉnh sửa tất các hàm và thủ tục liên quan o Không phù hợp với xu đại vì không mô tả thực thể sống thực Lập trình hướng đối tượng a Giới thiệu Với xu đại, ngôn ngữ lập trình hướng đối tượng đ~ đời Cơ sở lập trình hướng đối tượng l{ đối tượng Đối tượng là thể thực thể giới thực Một thực thể giới thực thường có: c|c đặc trưng v{ c|c h{nh động Ví dụ: người giới thực có c|c đặc trưng - tên gọi, tuổi, màu tóc, màu mắt, m{u da… v{ c|c h{nh động – ăn, nói, chạy, nhảy… Cách thức lập trình này mô tả cách chính xác các vật, người giới thực Bây giờ, ta xét vài ví dụ thấy cần thiết lập trình hướng đối tượng Ví dụ Chúng ta điểm qua số tính chương trình soạn thảo văn Word Microsoft Chúng ta thảo luận c|c đối tượng Drawing Word Mỗi đối tượng có các thuộc tính: màu viền, dạng đường viền, kích thước viền, màu sắc viền, màu nền, có văn hay không đối tượng drawing…Khi chúng ta biến đổi hình dạng đối T r a n g | 121 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ví dụ Chúng ta muốn xây dựng chương trình quản lý sinh viên Khi đó, ta cần lưu trữ c|c thông tin liên quan đến đối tượng sinh viên này: họ tên sinh viên, mã số sinh viên, ng{y th|ng năm sinh, quê qu|n, điểm các môn, điểm tổng kết,… v{ nhiều thông tin khác liên quan Sau kết thúc năm học, sinh viên nhận đ|nh gi| kết học tập mình Chúng ta cần có phương thức tiếp nhận kết để sinh viên đó có thể phản ứng lại với gì mà mình nhận được, họ phải thực c|c h{nh động học tập, tham gia vào các hoạt động trường, khoa… đó l{ h{nh động mà sinh viên cần thực (123) Chương 13 Lập trình hướng đối tượng tượng: kéo giãn, làm lệch xiêng, quay vòng… chúng ta cần đưa thông điệp để c|c đối tượng hình thể n{y thay đổi theo C|c h{nh động này thuộc quyền sở hữu đối tượng Tiêu biểu số này là C++, Java, C#, Delphi, Python… b Phương pháp phân tích và thiết kế hướng đối tượng Trước bắt đầu viết chương trình theo hướng đối tượng, thì ta cần phân tích và thiết kế c|c đối tượng Từ sơ đồ cấu trúc nhận được, chúng ta có thể xây dựng nên chương trình Chi tiết cách thức phân tích và thiết kế đối tượng, chúng ta tìm hiểu kĩ học phần phân tích thiết kế hệ thống thông tin Trong nội dung giáo trình này, chúng ta thảo luận phần nhỏ, để giúp các bạn có thể xây dựng nên cấu trúc chương trình theo hướng đối tượng Sơ đồ cấu trúc lập trình hướng đối tượng sử dụng phổ biến l{ sơ đồ mô tả trên ngôn ngữ UML (Unified Modeling Languages) UML là ngôn ngữ chuyên dùng để mô hình T r a n g | 122 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trong hai ví dụ minh họa trên, chúng ta thấy hướng tiếp cận theo lập trình hướng đối tượng là gần gũi với sống thực Chúng ta không quan t}m đến khía cạnh không cần thiết đối tượng, chúng ta tập trung v{o c|c đặc trưng v{ c|c h{nh động đối tượng Kể từ thời điểm này trở đi, chúng ta gọi các đặc trưng đối tượng là các thuộc tính thành viên đối tượng đó (hoặc liệu thành viên, biến thành viên đối tượng) v{ c|c h{nh động đối tượng l{ c|c phương thức thành viên (hay hàm thành viên) đối tượng Các cách gọi liệu thành viên, thuộc tính thành viên, biến thành viên hay thuộc tính (tương ứng phương thức th{nh viên, h{m th{nh viên, phương thức) là không có phân biệt Tôi đưa nhiều cách gọi kh|c để chúng ta có thể quen tham khảo các giáo trình khác Bởi lẽ, nhiều giáo trình chọn lựa các cách gọi khác Các cách gọi n{y tùy thuộc vào ngôn ngữ lập trình (trong C++ thông thường người ta sử dụng khái niệm liệu thành viên – member data biến thành viên – member variable và hàm thành viên – member function, đó, c|c ngôn ngữ Java, Delphi hay C# lại sử dụng khái niệm phương thức – method và thuộc tính – property) Khái niệm thành viên áp dụng cho liệu thành viên lẫn hàm thành viên Phương ch}m lập trình hướng thủ tục theo gi|o sư Niklaus Wirth Chương trình = Cấu trúc liệu + Giải thuật Còn phương ch}m lập trình hướng đối tượng là Chương trình = Đối tượng + Dữ liệu (124) Chương 13 Lập trình hướng đối tượng hóa c|c đối tượng Nó không áp dụng lập trình, m{ còn sử dụng rộng r~i c|c lĩnh vực khác sống Trong UML có nhiều dạng sơ đồ hoạch định Nhưng phạm trù lập trình hướng đối tượng, sơ đồ lớp là mô tả gần gũi Do đó, tôi trình bày cách xây dựng chương trình mô tả sơ đồ lớp Một số kí hiệu cần lưu ý UML Trước tìm hiểu cách mô hình hóa bài toán UML, chúng ta cần tìm hiểu số kí hiệu sử dụng UML Các kí hiệu này có thể khác nhiều chương trình mô Những kí hiệu mà tôi sử dụng đ}y l{ kí hiệu dùng trên Visual Studio 2010 Kí hiệu Ý nghĩa Lớp Thuộc tính phương thức private Thuộc tính phương thức protected Thuộc tính phương thức public Biểu diễn tính kế thừa Mũi tên luôn hướng lớp sở Chiều còn lại luôn vào lớp Thuộc tính Phân tích và thiết kế mô hình Việc phân tích và thiết kế mô hình là công việc đòi hỏi các nhà thiết kế phải có tư tốt Đối với bài toán phân tích và thiết kế, không phải có mô hình kết quả, mà có thể có vài, chí là nhiều mô hình khác Tuy nhiên, công việc chọn lựa mô hình tối ưu l{ điều cần thiết Trong nội dung giáo trình này, tôi giới thiệu sơ qua cách hoạch định mô hình Chúng ta không sâu nghiên cứu vấn đề này Trong học phần phân tích và thiết kế hệ thống thông tin trình bày chi tiết và cụ thể Các bước phân tích và thiết kế Để phân tích và thiết kế mô hình hướng đối tượng, cần thực c|c giai đoạn sau đ}y: T r a n g | 123 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Phương thức (125) - Bước Mô tả bài toán Một bài toán miêu tả dạng ngôn ngữ tự nhiên Nó chủ yếu dựa vào yêu cầu khác hàng và trợ giúp khách hàng - Bước Đặc tả các yêu cầu Sau phân tích các nhân tố tham gia vào mô hình, ta cần tiến h{nh xem xét c|c t|c nh}n t|c động vào nhân tố Mối quan hệ các nhân tố… - Bước Trích chọn đối tượng Sau tổng hợp các tác nhân và nhân tố mô hình Chúng ta cần tiến hành lựa chọn chúng Việc loại bỏ các nhân tố và tác nhân không cần thiết là quan trọng Nó giúp cho mô hình tập trung vào các nhân tố quan trọng và cần thiết, tránh phân tích và thiết kế tràn lan - Bước Mô hình hóa các lớp đối tượng Sau chọn lựa c|c đối tượng cần thiết Chúng ta phân tích đối tượng Khi ph}n tích đối tượng, ta cần lưu ý tập trung vào thứ cốt lõi đối tượng, tr|nh đưa v{o phương thức và thuộc tính không cần thiết, không quan trọng đối tượng – đó chính l{ tính trừu tượng hóa liệu Khi phân tích, chúng ta cần lưu ý đến các tính chất chung đối tượng Nếu c|c đối tượng có nhiều tính chất chung, chúng ta nên xây dựng đối tượng mới, chứa các tính chất chung đó, đối tượng còn lại thừa kế từ đối tượng n{y, để nhận các tính chất chung - Bước Thiết kế đối tượng Chúng ta cần đảm bảo rằng, đối tượng phải có c|c phương thức và thuộc tính riêng lẫn c|c phương thức và thuộc tính chia sẻ C|c phương thức riêng có th}n đối tượng có quyền thay đổi C|c phương thức chia sẻ có thể truy cập đối tượng khác theo các mức khác - Bước Xây dựng và kiểm thử mô hình Bắt tay vào xây dựng mô hình Ở đ}y, chúng ta sử dụng ngôn ngữ UML để mô tả Sau xây dựng xong mô hình, cần tiến hành kiểm thử mô hình Kiểm thử các mô hình các tình thực tế là cần thiết để đảm bảo mô hình nhận là phù hợp, trước bắt tay vào viết chương trình Trên đ}y, là bước đề nghị để chúng ta có cái nhìn tổng quát phân tích và thiết kế Có thể có nhiều c|ch để phần tích và thiết kế mô hình Nhưng hãy luôn đảm bảo rằng, mô hình thu không đạt hiệu cao, m{ còn đảm bảo nó phải dễ dàng bảo trì và nâng cấp Mỗi có lỗi xuất hiện, chúng ta cần biết khoanh vùng để thu nhỏ phạm vi phát lỗi Chúng ta lấy ví dụ nhỏ Ph}n tích hướng đối tượng mô hình quản lý cửa hàng bán xe đạp Trong mô hình này, ta cần quản lý c|c nhóm đối tượng sau: đối tượng xe đạp, đối tượng chi nhánh bán hàng, đối tượng kh|ch h{ng v{ đối tượng nhân viên bán hàng - Đối tượng xe đạp: chúng ta cần quản lý mã số xe (mã số gồm hai phần: phần id chi nhánh bán hàng + mã số vạch), loại xe, màu sắc, gi| b|n, nước T r a n g | 124 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương 13 Lập trình hướng đối tượng (126) Chương 13 Lập trình hướng đối tượng Hình 20 – Minh họa sơ đồ lớp T r a n g | 125 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ sản xuất (các thuộc tính chung) Đối tượng xe đạp địa hình: số b|nh răng, c|ch lên (bằng tay/tự động), chống sooc Đối tượng xe đạp du lịch: xe đơn/đôi, tự động (hỗ trợ tự chạy điện hay không), chiếu sáng (có/không) Xe đua thể thao: điều chỉnh độ cao (có/không), các chế độ đạp (đạp thư giản, đạp tăng tốc, đạp chậm…) - Đối tượng khách hàng và nhân viên bán hàng: họ và tên, số CMND Đối tượng khách hàng: cách thức toán (tiền mặt/chuyển khoản), cách thức giao hàng (nhận chỗ/ đưa đến tận nh{) Đối tượng nhân viên bán h{ng: id chi nh|nh b|n h{ng, ng{y th|ng năm sinh, quê qu|n, địa chỉ, mã số thuế, lương… - Đối tượng chi nh|nh b|n h{ng: id chi nh|nh b|n h{ng, địa Nếu yêu cầu quản lý nhiều c|c thông tin đối tượng, đó ta cần bổ sung thêm các thuộc tính tương ứng này Đối với c|c phương thức thực thi h{nh động, tương ứng với thuộc tính, ta có hai phương thức để định và tiếp nhận Ví dụ, đối tượng nhân viên, có họ v{ tên Tương ứng với thuộc tính n{y, ta có phương thức định để đặt tên cho nh}n viên (đặt tên là thiết lập tên gọi phần mềm quản lý) và tiếp nhận tên có yêu cầu Đối tượng kh|ch h{ng có phương thức định (quyết định thực giao dịch) Đối tượng nh}n viên b|n h{ng có phương thức tiếp nhận (nhận giao dịch) Đối tượng địa điểm b|n h{ng có phương thức nhận hàng (nếu h{ng còn đầy thì không tiếp nhận thêm) Theo ph}n tích trên, đối tượng xe đạp l{ đối tượng chung C|c đối tượng xe đạp thể thao, xe đạp du lịch, xe đạp địa hình kế thừa từ lớp xe đạp Đối tượng người để quản lý thông tin chung V{ c|c đối tượng nhân viên và khách hàng thừa kế từ lớp người Cuối cùng l{ đối tượng chi nh|nh b|n h{ng Theo c|ch ph}n tích n{y, ta có sơ đồ lớp sau: (127) Chương 13 Lập trình hướng đối tượng Trong sơ đồ n{y, c|c phương thức và thuộc tính lớp đối tượng đ~ ph}n tích trên Để tr|nh rườm r{, c|c phương thức biểu diễn sơ lược Lớp và đối tượng Lớp là biểu diễn đối tượng lập trình v{ ngược lại đối tượng là thể lớp Một đối tượng gồm có: thuộc tính v{ phương thức Chúng ta có thể xem lớp l{ kiểu liệu, còn đối tượng là biến Lớp khai báo nhờ từ khóa class class tên_lớp{ các_thuộc_tính các_phương_thức } [tên_đối_tượng]; tên_lớp: là tên lớp tên_đối_tượng: là tên đối tượng Khai báo lớp tương đối giống khai báo struct Các thuộc tính khai b|o khai b|o biến C|c phương thức khai b|o khai b|o h{m Chúng có thể định ba từ khóa: private, protected và public o private: các thành viên (biến thành viên hàm thành viên) cùng lớp từ hàm bạn nó có thể truy cập o protected: các thành viên cùng lớp từ hàm bạn nó từ lớp dẫn xuất nó bạn lớp dẫn xuất nó có thể truy cập Mức truy cập lớn trường hợp này là bạn lớp dẫn xuất Chúng ta thảo luận thêm phần sau o public: các thành viên có thể truy cập lẫn từ lớp Theo mặc định, không định từ khóa, thì private ấn định C++ class Humans{ string name; int age; public: void setName(string); void setAge(string); string getName(void); int getAge(void); T r a n g | 126 CuuDuongThanCong.com https://fb.com/tailieudientucntt (128) Chương 13 Lập trình hướng đối tượng }man; Humans là tên lớp, chứa các thuộc tính l{ name v{ age, chúng không định từ khóa, nên private sử dụng C|c phương thức setName, setAge, getName và getAge định là public Trong trường hợp này, man là đối tượng thể lớp Humans Chương trình Kết #include<iostream> The man: Jack, age 21 using namespace std; class Humans{ string name; int age; public: void setName(string); void setAge(int); string getName(void); int getAge(void); }; void Humans::setName(string s){ name = s; } void Humans::setAge (int a){ age = a; } string Humans::getName(void){ return name; } int Humans::getAge(void){ return age; } int main(){ Humans man; man.setName(“Jack”); man.setAge(21); cout<<”The man: “<<man.getName()<<”, age T r a n g | 127 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Sau khai báo lớp, ta cần bổ sung phần thân lớp – tương ứng với các hàm thành viên Hoặc ta có thể bổ sung trực tiếp vào lớp – tương tự khai báo hàm trực tiếp, sử dụng khai báo prototype Đối với khai báo prototype, để x|c định phương thức là lớp n{o đó, ta sử dụng toán tử phạm vi :: theo sau tên lớp (129) Chương 13 Lập trình hướng đối tượng “<<man.getAge(); return 0; } Giải thích: Hàm setName gán biến s cho th{nh viên name, tương tự cho hàm setAge gán biến a cho thành viên age Hàm getName trả liệu nhận từ thành viên name và hàm getAge – nhận liệu từ thành viên age Hai phương thức setName và setAge gọi l{ phương thức setter Phương thức getName và getAge gọi l{ phương thức getter Các phương thức setter dùng để nhập liệu cho các thuộc tính thành viên, các phương thức getter dùng để nhận giá trị từ các thuộc tính th{nh viên đ~ nhập setter (hoặc phương thức khởi tạo) Chúng ta cần lưu ý rằng, hàm main không thuộc lớp Humans, đó, nó không thể truy xuất đến các thuộc tính th{nh viên trường hợp n{y, vì chúng khai báo mặc định là private Toán tử phạm vi :: giới hạn truy cập bất hợp lệ hàm không thuộc lớp Humans hay là bạn Humans Đ}y l{ cách thức để quy định phương thức có phải là thành viên lớp hay không Cách thức thứ hai, có thể khai báo hàm trực tiếp bên lớp Về chất, hai cách này không có khác biệt nào Lớp có t|c dụng l{ kiểu liệu, đó, ta có thể khai báo nhiều đối tượng cùng lớp hay mảng c|c đối tượng class Humans{ string name; int age; public: void setName(string); void setAge(int); string getName(void); int getAge(void); }; void Humans::setName(string s){ name = s; Kết Name and Nam 21 Name and Binh 22 Name and Xuan 22 Name and Tuan T r a n g | 128 CuuDuongThanCong.com https://fb.com/tailieudientucntt age of Student age of Student age of Student age of Student C++ Chương trình #include<iostream> using namespace std; (130) Chương 13 Lập trình hướng đối tượng } void Humans::setAge (int a){ age = a; } string Humans::getName(void){ return name; } int Humans::getAge(void){ return age; } #define MAX 21 Name and age of Student Lan 22 =====Students===== The man: Nam, age 21 The man: Binh, age 22 The man: Xuan, age 22 The man: Tuan, age 21 The man: Lan, age 22 Cơ sở lập trình hướng đối tượng là liệu thành viên và các hàm thành viên đối tượng Chúng ta hoàn toàn không sử dụng tập các biến toàn cục để truyền qua hàm (hay tập các biến cục truyền theo tham biến), m{ thay v{o đó, chúng ta sử dụng c|c đối tượng cùng với T r a n g | 129 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ int main(){ Humans man[MAX]; for(int i=0; i<MAX; i++){ string s; int a; cout<<"Name and age of Student " <<(i+1)<<endl; cin>>s; cin>>a; man[i].setName(s); man[i].setAge(a); } cout<<"=====Students====="<<endl; for(int i=0; i<MAX; i++) cout<<"The man: "<<man[i].getName()<<", age "<<man[i].getAge()<<endl; return 0; } Trong trường hợp này, biến man là mảng c|c đối tượng Humans Chương trình minh họa cho việc nhập tên sinh viên, tuổi họ v{ lưu v{o mảng Sau đó, xuất kết màn hình Dù là cùng là thể lớp Humans, c|c đối tượng man[1], man[2],… có c|c thuộc tính hoàn toàn khác (131) Chương 13 Lập trình hướng đối tượng liệu thành viên và hàm thành viên nó C|c h{m th{nh viên t|c động trực tiếp lên các liệu thành viên Hàm tạo và hàm hủy Chương trình Ví dụ #include<iostream> The man: Jack, age 21 using namespace std; class Humans{ string name; int age; public: Humans(string, int); string getName(void); int getAge(void); }; Humans::Humans (string s, int a){ name = s; age = a; } string Humans::getName(void){ return name; } int Humans::getAge(void){ return age; } int main(){ Humans man(“Jack”, 21); cout<<”The man: “<<man.getName()<<”, age “<<man.getAge(); return 0; } Hàm tạo không có kiểu liệu trả - tương ứng với kiểu void Tuy nhiên chúng ta không sử dụng từ khóa void trước khai báo hàm tạo T r a n g | 130 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Trước sử dụng đối tượng, chúng ta cần khởi tạo giá trị cho nó để tránh gặp phải giá trị không mong muốn thực thi chương trình Một cách thức m{ chúng ta đ~ sử dụng trên là sử dụng phương thức setter Một phương thức đơn giản hơn, chúng ta có thể sử dụng hàm khởi tạo (hay gọi tắt là hàm tạo) Việc khai báo hàm tạo tương tự khai báo hàm thành viên khác, nhiên thiết tên hàm tạo phải trùng với tên lớp (132) Chương 13 Lập trình hướng đối tượng Chương trình Ví dụ #include<iostream> The man: Jack, age 21 using namespace std; class Humans{ string name; int age; public: Humans(string, int); ~Human(); string getName(void); int getAge(void); }; Humans::Humans (string s, int a){ name = s; age = a; } Humans::~Humans() { //do something //delete pointer; } string Humans::getName(void){ return name; } int Humans::getAge(void){ return age; } int main(){ Humans man(“Jack”, 21); cout<<”The man: “<<man.getName()<<”, age “<<man.getAge(); T r a n g | 131 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Nếu đối tượng đ~ tạo ra, ta không muốn sử dụng đến nó nữa, để thu hồi nhớ, ta cần sử dụng phương thức để hủy bỏ các liệu thành viên nó – đó l{ h{m hủy Hàm hủy l{ hàm thành viên lớp Nó không có kiểu liệu trả về, ta không sử dụng từ khóa void trước khai báo hàm hủy Hàm hủy có tên trùng với tên lớp v{ phía trước tên hàm hủy là dấu ~ Hàm hủy tự động gọi phạm vi hoạt động đối tượng kết thúc Phạm vi hoạt động đối tượng giống phạm vi hoạt động biến cục - khai báo phạm vi nào, thì có tác dụng phạm vi đó (133) Chương 13 Lập trình hướng đối tượng } return 0; Chồng chất hàm tạo Cũng c|c h{m kh|c C++ cho phép chồng chất hàm, hàm tạo có thể bị chồng chất Khi chồng chất hàm tạo thì danh sách tham số phải khác (kiểu liệu số lượng tham số) Chúng ta cần nhớ rằng, chồng chất hàm thì trình biên dịch gọi h{m tương ứng với danh sách tham số nó Trong trường hợp chồng chất hàm tạo, thì quá trình gọi là tự động đối tượng tạo ra, đó, hàm tạo gọi tự động tương ứng với danh sách tham số nó Ví dụ The man: Jack, age 21 The default: Default, age 21 C++ Chương trình #include<iostream> using namespace std; class Humans{ string name; int age; public: Humans(void); Humans(string, int); string getName(void); int getAge(void); }; Humans::Humans (void){ name = “Default”; age = 21; } Humans::Humans (string s, int a){ name = s; age = a; } string Humans::getName(void){ return name; } int Humans::getAge(void){ return age; } int main(){ Humans man(“Jack”, 21); Humans default; T r a n g | 132 CuuDuongThanCong.com https://fb.com/tailieudientucntt (134) Chương 13 Lập trình hướng đối tượng cout<<”The man: “<<man.getName()<<”, age “<<man.getAge()<<endl; cout<<”The default: “<<default.getName()<<”, age “<<default.getAge(); return 0; } Giải thích: trường hợp ví dụ trên, để tạo đối tượng thuộc lớp Humans, ta có thể sử dụng hai hàm tạo tương ứng: Humans(void) Humans(string, int) Nếu gọi theo phương thức hàm tạo không đối số, thì tên gọi và tuổi tạo mặc định Còn gọi theo phương thức có đối số, thì tên gọi và tuổi tạo theo tham số truyền vào Ta cần lưu ý c|ch gọi hàm tạo, hàm tạo có đối số, thì sau tên đối tượng, chúng ta cần cung cấp tham số tương ứng với tham số hàm tạo bên dấu () Còn hàm tạo không đối số, thì hãy khai b|o nó khai b|o biến mà không có bất kì dấu () nào Humans man(); //Sai Humans man; //Đúng Humans man = Humans(); //Đúng Chú ý: Khi ta không khai báo hàm tạo mặc định không tham số và khai báo hàm tạo mặc định có tham số, thì việc khai báo đối tượng theo c|ch Humans man; l{ không phép Nếu ta không tạo hàm tạo n{o, thì điều này là hợp lệ Sao chép hàm tạo Một đối tượng có thể tạo từ hàm tạo theo cách khởi gán các liệu thành viên nó cho giá trị n{o đó Chúng ta hoàn toàn có thể khởi tạo đối tượng từ đối tượng khác cách sử dụng toán tử gán Tuy nhiên, thực tế, liệu đối tượng lớn, phức tạp, thì việc sử dụng toán tử gán thực thi chậm và có thể gây số lỗi liên quan đến hủy đối tượng vùng nhớ Để khắc phục nhược điểm này, ta có thể sử dụng hàm tạo chép Khi sử dụng hàm tạo chép, trình biên dịch chép toàn liệu thành viên đối tượng đó sang đối tượng khởi tạo C++ Humans man(“Jack”, 21); //Sao chép trực tiếp Humans man2 = man; //Sao chép hàm tạo T r a n g | 133 CuuDuongThanCong.com https://fb.com/tailieudientucntt (135) Chương 13 Lập trình hướng đối tượng Tham chiếu Phương thức chép hàm tạo (hoặc tổng quát là các phương thức có sử dụng tham chiếu đến lớp đối tượng) có thể thực theo tham chiếu tham chiếu (tương ứng với không có từ khóa const), hãy luôn quy định là tham chiếu (có toán tử &) Khi quy định tham chiếu, đối tượng tham chiếu tham chiếu đến địa đối tượng gốc Dữ liệu đối tượng tham chiếu ánh xạ theo địa đối tượng tham chiếu (không thực việc chép trực tiếp mà là gián tiếp thông qua địa biến tham chiếu) Tuy nhiên, vì lí n{y m{ đối tượng tham chiếu có thể bị thay đổi giá trị (tương tự truyền theo tham biến) Điều này làm vi phạm tính đóng gói lập trình hướng đối tượng Cũng vì lí n{y, C++ cung cấp cho ta từ khóa const để quy định đối tượng tham chiếu không bị làm thay đổi liệu th{nh viên v{ nó gọi là tham chiếu Như vậy, ta cần phân biệt ba cách truyền tham số đối tượng phương thức: truyền theo tham trị – liệu đối tượng có thể thay đổi bên phương thức thay đổi này không lưu lại, việc chép liệu trường hợp này là thực thi trực tiếp nên thường áp dụng cho các kiểu liệu nguyên thủy đơn giản; truyền theo tham chiếu – liệu đối tượng có thể bị thay đổi phương thức và nó lưu lại, nó thực việc chép liệu cách gián tiếp nên liệu có cấu trúc phức tạp (như lớp đối tượng, trỏ) có thể chép nhanh nhiều so với truyền theo tham trị; tham chiếu – tương tự tham chiếu không cho phép thay đổi liệu đối tượng phương thức T r a n g | 134 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Humans::Humans(const Humans& m){ name = m.name; age = m.age; } Humans man2(man); Chúng ta lưu ý rằng, việc chép hàm tạo quy định theo tham chiếu const Humans& Nếu ta không viết hàm chép hàm tạo, thì trình biến dịch tự động làm giúp(nghĩa l{ ta luôn có thể sử dụng cách khởi tạo đối tượng theo kiểu Object newObj(oldObj); với oldObj l{ đối tượng thuộc lớp Object đ~ tạo, dù ta có tạo phương thức chép hàm tạo hay không) Hay nói cách khác, hàm tạo chép là mặc định đại đa số trình biên dịch ANSI C++ đại (GCC, Visual C++, Borland C++, Intel C++) (136) Chương 13 Lập trình hướng đối tượng Hợp lệ … class Humans{ string name; int age; public: Humans(string, int); Humans(const Humans&); string getName(void); int getAge(void); }; Humans::Humans(const Humans& m){ name = m.name; age = m.age; } … Không hợp lệ … class Humans{ string name; int age; public: Humans(string, int); Humans(const Humans&); ~Human(); string getName(void); int getAge(void); }; Humans::Humans(const Humans& m){ name = m.name; age = m.age = 22;//Error } … Ta có thể thấy trường hợp không hợp lệ, chúng ta quy định đối tượng tham chiếu m cho phép đối tượng khác tham chiếu đến nó theo tham chiếu Nhưng đối tượng tham chiếu đến nó, lại cố gắng thay đổi thuộc tính age nó Trong trường hợp n{y, chương trình phát sinh lỗi Nếu quy định là tham chiếu bình thường (bỏ từ khóa const) thì khai b|o xem là hợp lệ (tuy nhiên vi phạm tính đóng gói) Ví dụ Tham chiếu Hằng … class PhanSo Ví dụ Tham chiếu … class PhanSo T r a n g | 135 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Khi phương thức lớp đối tượng sử dụng tham số chính là đối tượng lớp đó, chúng ta có thể truy cập trực tiếp đến thuộc tính đối tượng tham chiếu kể nó quy định là private (cả tham chiếu lẫn không tham chiếu) Bên cạnh đó, ta quy định là tham chiếu bình thường, thì ta có thể sử dụng c|c phương thức getter v{ setter để truy cập đến các thuộc tính nó Nhưng ta sử dụng tham chiếu hằng, thì không phép truy cập đến c|c phương thức đối tượng tham chiếu Chúng ta có thể truy cập đến các phương thức đối tượng tham chiếu n{y Phương thức là phương thức bổ sung vào từ khóa const vào cuối khai b|o phương thức tiêu đề hàm prototype và tiêu đề khai b|o h{m đầy đủ Hãy quan sát các ví dụ sau đ}y (137) Chương 13 Lập trình hướng đối tượng { }; private: int Tu; int Mau; public: //Khai báo các hàm tạo PhanSo Nhan(const PhanSo&); int GetTu(void); int GetMau(void); PhanSo PhanSo::Nhan(const PhanSo& p) { return PhanSo(Tu*p.Tu, Mau*p.Mau); //không phép viết PhanSo(Tu*p.GetTu(), Mau*p.GetMau()); } int PhanSo::GetTu(void) { return Tu; } { }; private: int Tu; int Mau; public: //Khai báo các hàm tạo PhanSo Nhan(PhanSo&); int GetTu(void); int GetMau(void); PhanSo PhanSo::Nhan(PhanSo& p) { return PhanSo(Tu*p.GetTu(), Mau*p.GetMau()); //hoặc PhanSo(Tu*p.Tu, Mau*p.Mau); } int PhanSo::GetTu(void) { return Tu; } int PhanSo::GetMau(void) { return Mau; } … int PhanSo::GetMau(void) { return Mau; } … Trong trường hợp ta muốn sử dụng phương thức cho đối tượng tham chiếu hằng, thì cần khai b|o phương thức GetTu v{ GetMau l{ phương thức Khi đó, chúng ta sử dụng cú ph|p sau đ}y: int GetTu(void) const; C++ int GetMau(void) const; T r a n g | 136 CuuDuongThanCong.com https://fb.com/tailieudientucntt (138) Chương 13 Lập trình hướng đối tượng … class PhanSo { private: int Tu; int Mau; public: //Khai báo các hàm tạo PhanSo Nhan(const PhanSo&); int GetTu(void) const; int GetMau(void) const; }; PhanSo PhanSo::Nhan(const PhanSo& p) { return PhanSo(Tu*p.Tu, Mau*p.Mau); //hoặc PhanSo(Tu*p.GetTu(), Mau*p.GetMau()); } int PhanSo::GetTu(void) const { return Tu; } int PhanSo::GetMau(void) const { return Mau; } … Việc bổ sung từ khóa const v{o sau khai b|o phương thức giúp cho đối tượng tham chiếu có thể gọi phương thức này Thêm khái niệm C++ mà chúng ta cần biết là phương thức tham chiếu Một phương thức tham chiếu cho phép ta sử dụng nó biến – ta có thể gán trực tiếp giá trị biến cho phương thức đó m{ không gặp phải trở ngại nào C++ Chương trình #include <iostream> using namespace std; T r a n g | 137 CuuDuongThanCong.com https://fb.com/tailieudientucntt (139) Chương 13 Lập trình hướng đối tượng class complex{ private: float img; float real; public: complex(); complex(float, float); float &getimg(); float &getreal(); }; complex::complex( float img, float real ) { this->img = img; this->real = real; } complex::complex() { real = 0; img = 0; } float & complex::getreal() { return real; } int main () { complex c; c.getreal() = 2; c.getimg() = 1; cout<<c.getreal()<<" + I*"<<c.getimg(); } Trong ví dụ này, chúng ta thấy c|c phương thức getter khai báo là phương thức tham chiếu Ta có thể gán trực tiếp giá trị cho c|c phương thức n{y Trong trường hợp n{y, phương thức getter có hai tính năng: T r a n g | 138 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ float & complex::getimg() { return img; } (140) Chương 13 Lập trình hướng đối tượng vừa là getter vừa là setter Nhưng với tính phương thức setter đơn (tức thiết lập giá trị nhất) Tính đóng gói – Encapsulation Ví dụ trên đưa cho ta hai phương |n: nên hay không nên sử dụng từ khóa const Câu trả lời là h~y nên quy định việc chép hàm tạo là truyền theo tham chiếu hằng, lẽ c|c đối tượng khác nhau, không có quyền chỉnh sửa liệu thành viên nhau, nó có thể truyền thông điệp cho mà thôi, việc chỉnh sửa liệu thành viên là thân đối tượng đó Điều này là thể tính đóng gói lập trình hướng đối tượng Tính đóng gói lập trình hướng đối tượng còn thể các mức độ cho phép truy cập liệu và hàm thành viên – tương ứng với từ kho| private, protected v{ public m{ ta đ~ thảo luận trên Khái niệm: tính đóng gói l{ tính chất không cho phép người dùng hay đối tượng kh|c thay đổi liệu thành viên đối tượng nội Chỉ có các hàm thành viên đối tượng đó có quyền thay đổi trạng thái nội nó mà thôi C|c đối tượng khác muốn thay đổi thuộc tính thành viên đối tượng nội tại, thì chúng cần truyền thông điệp cho đối tượng, và việc định thay đổi hay không đối tượng nội định Trong vài giáo trình, tính chất này còn gọi l{ tính đóng gói và ẩn dấu thông tin (encapsulation and information hiding) T r a n g | 139 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chúng ta có thể khảo sát ví dụ sau: bệnh nhân cần phải thay nội tạng để có thể sống, thì việc thay nội tạng đó cần phải có đồng ý bệnh nhân Không có thể tự động thực điều này (chỉ bệnh nh}n đ~ rơi v{o tình trạng hôn mê, thì người nhà bệnh nhân định thay họ) Nội tạng là các thuộc tính cố hữu bệnh nhân C|c phương thức thay nội tạng đối tượng b|c sĩ không phải l{ phương thức thành viên đối tượng bệnh nhân (bệnh nhân không thể tự thay nội tạng cho mình v{ b|c sĩ không có quyền thay nội tạng cho bệnh nhân không có đồng ý họ) Do đó, họ muốn thực thì cần có phương thức đồng ý bệnh nhân (phương thức thành viên đối tượng bệnh nhân) Phương thức đồng ý bệnh nh}n n{y không thể nào áp dụng cho bệnh nhân (bệnh nhân A không thể định thay nội tạng cho bệnh nhân B) Như vậy, liệu thành viên đối tượng nào, thì có đối tượng đó có quyền thay đổi (141) Chương 13 Lập trình hướng đối tượng Con trỏ đối tượng Chúng ta đ~ l{m quen với mảng đối tượng v{ chúng ta đ~ biết có tương ứng 1-1 mảng và trỏ Trong phần này, chúng ta thảo luận trỏ đối tượng Chúng ta sử dụng lớp Humans trên cho các ví dụ minh họa phần này Việc khai báo trỏ đối tượng ho{n to{n tương tự khai b|o trỏ liệu Humans *man; Để truy cập đến c|c phương thức thành viên bên ngoài lớp (hàm thành viên), ta sử dụng dấu -> (vì có c|c phương thức th{nh viên định là public) Khi gọi phương thức khởi tạo, ta có thể gọi theo cách mà ta đ~ sử dụng cho trỏ liệu Hoặc có thể sử dụng toán tử new Chương trình Kết … Andy, 22 int main() Jack, 21 { Humans man(“Andy”, 22); Humans *man0 = &man; //Hoặc Humans *man1 = new Humans(“Jack”, 21); cout<<man0->getName()<<”, ”<<endl<<man0>getAge()<<endl; cout<<man1->getName()<<endl<<man1->getAge(); return 0; Ngay sau toán tử new, chúng ta gọi phương thức khởi tạo nó Trong ví dụ trên, ta khởi tạo đối tượng Nếu muốn tạo danh s|ch c|c đối tượng theo dạng trỏ, ta có thể sử dụng toán tử new[] mà ta đ~ thảo luận trên T r a n g | 140 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ } (142) Chương 13 Lập trình hướng đối tượng Khi liên đới đến trỏ, có nhiều vấn đề liên quan đến c|ch đọc Chúng ta có thể tổng kết theo bảng bên đ}y Biểu thức *x &x x.y x->y (*x).y x[i] Cách đọc trỏ x địa x thành viên y đối tượng x thành viên y đối tượng trỏ x thành viên y đối tượng trỏ x đối tượng thứ i trỏ x Lớp khai báo nhờ từ khóa struct và union Trong C++, lớp có thể khai báo nhờ vào từ khóa struct từ khóa union Chúng ta đ~ biết từ khóa struct dùng để khai báo kiểu liệu struct và nó chứa các liệu thành viên Từ khóa union dùng để khai báo kiểu liệu union và chứa các liệu thành viên Tuy nhiên, chúng có thể chứa các hàm thành viên Khi khai báo lớp từ khóa struct, không có khác biệt nào so với từ khóa class Chỉ có khác biệt, đó l{ theo mặc định, phương thức thành viên và liệu th{nh viên n{o không định từ khóa quy định mức truy cập (private, protected, public) thì lớp khai báo từ khóa class là private còn lớp khai báo struct là public Còn từ khóa union có vài khác biệt, không thể dùng để khai báo lớp hoàn hảo từ khóa struct hay class, nó có thể chứa các phương thức bên nó Nếu không định từ khóa quy định mức truy cập, thì nó mặc định là public Nếu viết lớp với đầy đủ hàm tạo, hàm hủy v{ c|c phương thức khác từ khóa class, thì thay từ khóa struct, không có nhiều thay đổi Nếu thay từ khóa union, thì trình dịch thông báo lỗi Sở dĩ là vì dù union cho phép chứa phương thức thành viên, nó không hỗ trợ khai báo prototype, không hỗ trợ liệu kiểu string Con trỏ this Con trỏ this trỏ vào liệu thành viên chính nó Điều này có nghĩa l{ trỏ this có phạm vi tác dụng lớp Một điều cực kì T r a n g | 141 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chú ý: Hãy luôn sử dụng từ khóa class để khai báo lớp (143) Chương 13 Lập trình hướng đối tượng quan trọng, là trỏ this hoạt động với các liệu thành viên và các h{m th{nh viên khai b|o l{ không tĩnh (non-static) Các liệu thành viên v{ h{m th{nh viên tĩnh (static) không hỗ trợ trỏ this Ví dụ Kết #include<iostream> using namespace std; class Complex{ float real; float img; public: Complex(float, float); bool isMe(const Complex&); }; Complex::Complex(float real, float img){ this->real = real; this->img = img; } bool Complex::isMe(const Complex& c){ if(&c==this) return true; else return false; } int main(){ Complex a(3, 2); Complex b(2, 2); Complex *c = &a; cout<<a.isMe(b)<<endl; cout<<a.isMe(*c); return 0; } Giải thích: với việc sử dụng trỏ this hàm tạo, ta có thể đặt tên các tham số hàm tạo trùng với tên các liệu lớp Để truy cập đến các thuộc tính lớp, ta sử dụng trỏ this Hàm thành viên isMe kiểm tra đối tượng có phải là chính nó hay không (có cùng địa trên T r a n g | 142 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ví dụ phương thức hàm tạo lớp Complex trên, chúng ta có thể sử dụng this->real để truy cập thuộc tính real, this->img – để truy cập thuộc tính img Ta có thể so sánh đối tượng khác với đối tượng nội nhờ vào trỏ this này (144) Chương 13 Lập trình hướng đối tượng nhớ) Dù là nó (có liệu thành viên giống nhau) thì kết nhận l{ sai (0) Trong hàm main, ta khởi tạo hai đối tượng a v{ b Đối tượng trỏ c trỏ v{o địa đối tượng a Điều này có nghĩa l{ c có cùng vùng địa với a, còn b thì không Khi gọi hàm a.isMe(b) cho kết là sai (0) và a.isMe(*c) cho kết l{ đúng (1) Thành viên tĩnh – Từ khóa static Một lớp có thể chứa c|c th{nh viên tĩnh không tĩnh Nếu không định từ khóa là static cho các thành viên, thì theo mặc định, nó là nonstatic Nếu muốn quy định cho th{nh viên n{o l{ tĩnh, thì ta bổ sung từ khóa static v{o trước nó Nếu l{ th{nh viên không tĩnh, ta không cần khai báo bất kì từ khóa nào Một liệu th{nh viên tĩnh lớp l{ biến toàn cục lớp đó Bởi thay đổi liệu thành viên tĩnh đối tượng n{y có tác dụng lên toàn các liệu thành viên tĩnh c|c đối tượng khác Một phương thức không tĩnh có quyền truy cập đến các liệu thành viên không tĩnh Một phương thức tĩnh có truy cập đến liệu thành viên không tĩnh Trong trường hợp này, ta cần tạo thể đối tượng và truy cập đến các thuộc tính không tĩnh từ đối tượng n{y.Để truy cập đến th{nh viên không tĩnh, ta sử dụng thể đối tượng, sau đó l{ dấu chấm (hoặc ->), tiếp đến l{ th{nh viên không tĩnh.Để truy cập đến đối tượng tĩnh, ta sử dụng toán tử phạm vi sau tên lớp, tiếp đến l{ th{nh viên tĩnh C|c phương thức tĩnh v{ không tĩnh có thể truy cập lẫn Kết -Call by NonStatic method Name: Ford Serial: 123 -Call by Static method Name: Ford Serial: 123 Count: C++ Ví dụ #include <iostream> using namespace std; class Car { public: string name; int serial; public: static int count; static void Show() { Car vehicle; T r a n g | 143 CuuDuongThanCong.com https://fb.com/tailieudientucntt (145) Chương 13 Lập trình hướng đối tượng vehicle.name = "Ford"; vehicle.serial = 123; cout << "\nName: " << vehicle.name; cout << "\nSerial: " << vehicle.serial; } static void CallShowStatic(){ Show(); } void CallShowNonStatic(){ Show(); } }; int Car::count = 2; int main() { Car a; cout<<" -Call by NonStatic method -"; a.CallShowNonStatic(); cout<<"\n -Call by Static method -"; Car::CallShowStatic(); cout << "\n\nCount: " <<Car::count; return 0; } Giải thích: Hàm thành viên Show là static, nên muốn truy cập đến các liệu th{nh viên không tĩnh thì nó cần tạo thể lớp đó l{ đối tượng vehicle Hàm CallShowStatic là static, hàm CallShowNonStatic là non-static có thể truy cập đến hàm Show là static cách trực tiếp Trong hàm main, các hàm non-static gọi thông qua thể lớp, còn h{m static gọi thông qua toán tử phạm vi Dữ liệu static là count truy cập thông qua toán tử phạm vi Mặc dù thành viên static có thể truy cập trực tiếp thông qua toán tử phạm vi, nó chịu chi phối các mức truy cập (private, protected, public) Chỉ có các thành viên không tĩnh có thể sử dụng trỏ this Hàm bạn: thành viên lớp quy định là private protected thì có các hàm thành viên lớp có quyền truy cập đến nó Nếu phương thức không phải là thành viên lớp muốn truy cập T r a n g | 144 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Hàm bạn và lớp bạn (146) Chương 13 Lập trình hướng đối tượng đến, thì nó phải là hàm bạn lớp đó Phương thức bạn có thể khai báo nhờ từ khóa friend Ví dụ Kết #include <iostream> 10 using namespace std; class Rectangle { private: int w; int h; public: Rectangle(int, int); friend int Area(Rectangle); }; Rectangle::Rectangle(int w, int h){ this->w = w; this->h = h; } int Area(Rectangle rec){ return (rec.w*rec.h); } int main() { Rectangle rec(2, 5); cout<<Area(rec); return 0; } Giải thích: hàm Area là hàm toàn cục, nó không phải là thành viên lớp (vì không sử dụng toán tử phạm vi khai báo) Nếu ta cố tình truy cập đến các liệu w v{ h thì chương trình dịch báo lỗi, chúng quy định là private Khi ta khai báo hàm Area là hàm bạn, nó giải vấn đề này Ví dụ #include <iostream> using namespace std; Kết Square: 5x5 T r a n g | 145 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lớp bạn: ta có hai lớp A và B, và khai báo B là bạn A, thì đó, c|c phương thức lớp A có thể truy cập đến các thuộc tính private và protected lớp B (147) Chương 13 Lập trình hướng đối tượng Ta lưu ý A là bạn B, thì không có nghĩa l{ B l{ bạn A Như vậy, tình bạn có thể là chiều hai chiều tùy thuộc vào quy định người lập trình T r a n g | 146 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ class Rectangle { private: int w; int h; public: Rectangle(int, int); friend class MakeSquare; }; class MakeSquare{ private: int w; int h; public: MakeSquare(Rectangle); void ShowSquare(void); }; MakeSquare::MakeSquare(Rectangle rec){ this->w = max(rec.w, rec.h); this->h = max(rec.w, rec.h); } void MakeSquare::ShowSquare(void){ cout<<"Square: "<<w<<"x"<<h; } int main() { Rectangle rec(2, 5); MakeSquare mk(rec); mk.ShowSquare(); return 0; } Giải thích: Lớp Rectangle quy định là lớp bạn lớp Square, đó, lớp Square có quyền truy cập đến các thuộc tính private và protected lớp Rectangle Hàm tạo lớp Square truy cập đến các liệu thành viên lớp Rectangle để lấy chiều dài và chiều rộng đối tượng rec (dù chúng l{ private), để tạo nên đối tượng mk Đối tượng Square tạo với cạnh nó là số đo lớn các cạnh đối tượng Rectangle (148) Chương 13 Lập trình hướng đối tượng Chồng chất toán tử Trong ngôn ngữ lập trình hướng đối tượng, có nhiều ngôn ngữ hỗ trợ chồng chất toán tử (các ngôn ngữ hỗ trợ bao gồm C++, Delphi 2009, C#,VB.net, … mức độ hỗ trợ khác nhau; các ngôn ngữ không hỗ trợ bao gồm Java, Python,…) Chồng chất toán tử (operator overloading) là cách thức xây dựng các hàm thành viên mà tên gọi chúng là các toán tử đ~ định nghĩa trước đó (+, -, *, v.v.) C++ là ngôn ngữ hỗ trợ chồng chất toán tử hoàn hảo Các toán tử sau đ}y có thể chồng chất C++ Các toán tử phép chồng chất + - * / = < > += -= *= /= << >> <<= >>= == != <= >= ++ % & ^ ! | ~ &= ^= |= || && %= [] () , ->* -> new delete new[] delete[] Cấu trúc khai báo chồng chất toán tử type operator toán_tử(tham số){…thân hàm…} Ví dụ sau đ}y minh họa cho việc chồng chất toán tử Chúng ta xây dựng lớp số phức, xây dựng các phép toán cộng hai số phức (phép toán hai ngôi) v{ phép tăng số phức lên đơn vị thực v{ đơn vị ảo(phép toán ngôi) cách sử dụng chồng chất toán tử Kết + 3*I + 3*I T r a n g | 147 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ví dụ #include<iostream> using namespace std; class Complex{ float real; float img; public: Complex(float, float); Complex operator +(const Complex&); Complex operator ++(void); void toString(void); }; Complex::Complex(float a, float b){ real = a; img = b; } Complex Complex::operator +(const Complex& b){ Complex c(0, 0); c.real = real + b.real; (149) Chương 13 Lập trình hướng đối tượng c.img = img + b.img; return c; } Complex Complex::operator ++(void){ Complex c(0, 0); c.real += ++real; c.img += ++img; return c; } void Complex::toString(void){ cout<<real<<" + "<<img<<"*I"<<endl; } int main(){ Complex a(3, 2); Complex b(-1, 1); (a+b).toString(); (++a).toString(); return 0; } Ta lưu ý rằng, phương thức toán tử, số tham số hình thức luôn hạng toán tử trừ Điều n{y có nghĩa l{ với phép toán ngôi không có tham số hình thức, với toán tử hai ngôi có tham số hình thức Điều này là dễ hiểu, đ~ có tham số mặc định – đó chính l{ th}n đối tượng nội (đối tượng tương ứng với trỏ this) Phép toán cộng, cộng đối tượng nội với đối tượng kh|c Phép to|n tăng đơn vị thực, đơn vị ảo l{m thay đổi giá trị đơn vị thực v{ đơn vị ảo đối tượng nội lên Vì các toán tử này trả kiểu số phức, nên ta hoàn toàn có thể thực phép toán phức hợp với chúng (tức là biểu thức có nhiều toán tử loại này thực trên các hạng tử là các số phức) Complex Complex::operator +(const Complex& b){ return Complex(real + b.real, img + b.img); } T r a n g | 148 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ (a+ ++b+(a+b)).toString(); Bằng việc sử dụng chồng chất toán tử, biểu thức tính toán trở nên đơn giản Ta có thể sử dụng cách gọi a.operator+(b) Hai cách này cho kết Đối với hàm toán tử + và ++ trên, ta có thể viết ngắn gọn m{ không cần khai báo thêm biến tạm: (150) Chương 13 Lập trình hướng đối tượng Complex Complex::operator ++(void){ return Complex(++real, ++img); } Việc thực các toán tử trên c|c đối tượng cần yêu cầu đối tượng trước đó phải khởi tạo giá trị Nghĩa l{ phải có hàm tạo cho đối tượng đó Mặc dù C++ hỗ trợ chồng chất nhiều toán tử, ta không nên lạm dụng nó Chúng ta nên sử dụng chồng chất toán tử với mục đích đúng đắn (cộng hai số phức thì sử dụng toán tử + mà không phải là toán tử kh|c, …) Việc sử dụng chồng chất toán tử l{ h{m th{nh viên |p dụng cho tất các toán tử mà C++ hỗ trợ Trừ các toán tử gán, hợp nhất, () và -> Các toán tử còn lại |p dụng cho các hàm toàn cục Hàm toàn cục h{m th{nh viên, nó không thuộc lớp nào Việc khai báo hàm toàn cục thực sau type operator@(A) Trong đó, @ l{ kí hiệu toán tử, A là tên lớp Ví dụ #include<iostream> using namespace std; class Complex{ public: float real; float img; Kết + 1*I }; Complex::Complex(float a, float b){ real = a; img = b; } void Complex::toString(void){ cout<<real<<" + "<<img<<"*I"<<endl; } Complex operator -(const Complex &a, const Complex &b){ return Complex(a.real - b.real, a.img - b.img); } int main(){ T r a n g | 149 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Complex(float, float); void toString(void); (151) Chương 13 Lập trình hướng đối tượng Complex a(3, 2); Complex b(-1, 1); (a-b).toString(); return 0; } Giải thích: ví dụ này, hàm toán tử - không phải là hàm thành viên lớp Complex Do đó, muốn truy cập đến các thuộc tính nó, ta phải quy định các thuộc tính này là public phải tạo thêm c|c phương thức getter để thu thập liệu quy định nó là hàm bạn Cũng vì nó không phải là hàm thành viên lớp Complex, nên số tham số phép toán ngôi là 1, phép toán hai ngôi là Đối với chồng chất toán tử nhập xuất - IO overloading, chúng ta có số chú ý: + Nếu khai báo hàm toán tử là thành viên lớp Ví dụ Kết (4, 5) #include <iostream> using namespace std; C++ class Vector2D{ private: int x, y; public: Vector2D(){ x = 0; y = 0; } Vector2D(int x1, int y1){ x = x1; y = y1; } ostream& operator<<(ostream& os){ os<<"("<<x<<", "<< y<<")"; return os; } }; int main() { T r a n g | 150 CuuDuongThanCong.com https://fb.com/tailieudientucntt (152) Chương 13 Lập trình hướng đối tượng Vector2D ab(4, 5); //ab.operator<<(cout); ab<<(cout); return 0; } Giải thích: hàm toán tử << thực thi việc in giá trị đối tượng nội (vì nó là thành viên lớp Vector2D) Nó là toán tử đơn hạng, đó, hàm toán tử không có mặt tham số Vector2D Đối với cách sử dụng này, ta có thể gọi nó hàm main hai cách sau: ab.operator<<(cout) ab<<(cout) Cả hai cách gọi n{y không trùng khớp với toán tử xuất (hay toán tử chèn liệu) Thông thường, ta xuất liệu theo chuẩn cout<<dữ_liệu Để thực điều này, ta cần sư dụng cách hai + Nếu khai báo hàm toán tử không phải là thành viên lớp Ví dụ Kết (4, 5) #include <iostream> using namespace std; ostream& operator<<(ostream& os, const Vector2D& v){ os<<"("<<v.x<<", "<<v.y<<")"; return os; } T r a n g | 151 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ class Vector2D{ private: int x, y; public: Vector2D(){ x = 0; y = 0; } Vector2D(int x1, int y1){ x = x1; y = y1; } friend ostream& operator<<(ostream&, const Vector2D&); }; (153) Chương 13 Lập trình hướng đối tượng int main() { Vector2D ab(4, 5); cout<<ab; return 0; } Giải thích: ví dụ này, hàm toán tử là hàm bạn lớp Vector2D Nó chứa tham số Vector2D nó không phải là thành viên lớp nội Khi in giá trị, nó in giá trị Vector2D này Khi sử dụng toán tử nhập liệu >> (hay toán tử trích tách liệu), ta khai b|o ho{n to{n tương tự Kiểu liệu trả lúc này là istream& thay vì sử dụng ostream& trên Chúng ta tiến hành nhập liệu cho nên tham số Vector2D h{m cần thay đổi – chúng ta cần bỏ từ khóa const lẽ ta tiến hành nhập liệu cho nó nên không thể quy định truyền giá trị theo tham chiếu (tức không cho phép thay đổi giá trị) Ví dụ Kết (4, 5) #include <iostream> using namespace std; istream& operator>>(istream& is, Vector2D& v){ is>>v.x>>v.y; return is; } T r a n g | 152 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ class Vector2D{ private: int x, y; public: Vector2D(){ this->x = 0; this->y = 0; } Vector2D(int x, int y){ this->x = x; this->y = y; } friend istream& operator>>(istream&, Vector2D&); }; (154) Chương 13 Lập trình hướng đối tượng int main() { Vector2D ab; cin>>ab; return 0; } Các kiểu liệu ostream& và istream& nằm thư viện iostream namespace std (dấu & để quy định là truyền theo tham chiếu phương thức tham chiếu) Tính kế thừa - Inheritance Một tính theng chốt lập trình hướng đối tượng đó l{ tính kế thừa Nhờ vào tính kế thừa, nó cho phép lớp có thể dẫn xuất từ lớp khác, chính vì chúng tự động tiếp nhận các thành viên bố mẹ và bổ sung thêm các thành viên riêng chúng Tính kế thừa cho phép lớp có thể nhận liệu thành viên (private, protected, public) và các hàm thành viên (trừ hàm tạo, hàm hủy, hàm bạn và hàm toán tử gán =) Ta có thể xét ví dụ lớp động vật Animal và minh họa tính kế thừa lược đồ bên (Hình 13) Hình 21 – Tính kế thừa T r a n g | 153 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lớp động vật Animal có các thuộc tính thành viên: tên gọi, cân nặng Các hàm thành viên: di chuyển, ăn Ta xét hai lớp dẫn xuất nó là lớp mèo Cat và lớp cá Fish Lớp Cat có các thuộc tính thành viên riêng: màu lông, màu mắt Các hàm thành viên riêng: Bắt chuột, Leo cây Lớp Fish có các thuộc tính thành viên riêng: kiểu vẩy, loại nước (nước ngọt, nước mặn, nước lợ) C|c h{m th{nh viên: bơi, sinh sản (cách thức sinh nào) (155) Chương 13 Lập trình hướng đối tượng Theo tính thừa kế, lớp Cat và Fish không có thuộc tính thành viên và hàm thành viên riêng lớp, mà nó còn có thuộc tính thành viên và hàm thành viên lớp Animal Từ nay, ta gọi lớp dẫn xuất Cat và Fish là các lớp và lớp dẫn xuất Animal là lớp sở (hay lớp cha) Ta cần lưu ý rằng, tên gọi mang tính tương đối, vì lớp có thể là lớp n{y, lại là lớp sở lớp kh|c Do đó, để tránh nhầm lẫn, trường hợp cần phân biệt, ta gọi cụ thể là lớp lớp nào, hay lớp sở lớp nào Để quy định lớp là dẫn xuất từ lớp khác, ta sử dụng toán tử : theo cấu trúc sau class Animal{ … }; class Cat: Từ_khóa_mức_truy _cập Animal{ … }; class Fish: Từ_khóa_mức_truy _cập Animal{ … }; Theo cấu trúc khai báo này, thì Cat và Fish là lớp lớp sở Animal Kết #include <iostream> using namespace std; class Animal{ protected: string name; int weight; public: Animal(void); Animal(string, int); void move(void); void eat(void); }; class Cat:public Animal{ private: -Animal Object I can eat I can move -Cat Object I can eat I can move I can catch mouse I can climb tree C++ Ví dụ T r a n g | 154 CuuDuongThanCong.com https://fb.com/tailieudientucntt (156) string colorf; string colore; public: Cat(string, int, string, string); void catchmouse(void); void climb(void); }; void Animal::move(void){ cout<<"I can move"<<endl; } void Animal::eat(void){ cout<<"I can eat"<<endl; } Animal::Animal(){ this->name = ""; this->weight = 0; } Animal::Animal(string name, int weight){ this->name = name; this->weight = weight; } void Cat::catchmouse(void){ cout<<"I can catch mouse"<<endl; } void Cat::climb(void){ cout<<"I can climb tree"<<endl; } Cat::Cat(string name, int weight, string colorfc, string colorec){ this->name = name; this->weight = weight; this->colorf = colorf; this->colore = colore; } int main() { T r a n g | 155 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương 13 Lập trình hướng đối tượng (157) Chương 13 Lập trình hướng đối tượng Animal an("Gau", 100); Cat ca("Cat1", 3, "black", "blue"); cout<<" -Animal Object -"<<endl; an.eat(); an.move(); cout<<" -Cat Object -"<<endl; ca.eat(); ca.move(); ca.catchmouse(); ca.climb(); return 0; } Giải thích: chương trình n{y lớp Cat thừa kế từ lớp Animal Nó kế thừa liệu thành viên và hàm thành viên lớp Animal Để hàm tạo lớp Cat có thể truy cập đến các liệu thành viên lớp Animal, thì các liệu thành viên này phải khai báo mức truy cập là protected public Đối tượng ca lớp Cat có thể truy cập đến c|c phương thức thành viên lớp sở là Animal lớp Animal này public (Cat:public Animal) Một điều cần lưu ý đó l{ h{m tạo Khi thừa kế, thì lớp không thừa kế hàm tạo từ lớp sở, lớp sở cần có hàm tạo mặc định không đối số (hàm tạo này luôn tồn tại; ta khai báo thêm vài hàm tạo, thì cần khai báo hàm tạo không có đối số) Các mức truy cập Phạm vi public protected private Thành viên cùng lớp phép phép phép Thành viên lớp dẫn xuất phép phép không phép Còn lại phép không phép không phép Chúng ta cần lưu ý cách viết tính kế thừa Cat:public Animal có số quy tắc chuyển đổi Nếu các thành viên lớp sở có mức truy cập là A, thừa kế ta quy định mức truy cập lớp lớp sở là B (A và B có thể là private < protected < public) và giả sử A<B, thì T r a n g | 156 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Mức độ cho phép truy cập đến các liệu thành viên từ lớp cho bảng sau (158) Chương 13 Lập trình hướng đối tượng các thành viên này lớp cha trở thành các thành viên lớp có mức truy cập là mức truy cập bé A Như tôi đ~ giới thiệu trên, biến th{nh viên định từ khóa mức truy cập là private thì có c|c phương thức cùng lớp c|c phương thức bạn có quyền truy cập (bao gồm hàm bạn và lớp bạn) Nếu mức truy cập là public, thì phương thức có quyền truy cập đến Chúng ta tìm hiểu kĩ từ khóa protected Tôi đ~ trình b{y các khả m{ phương thức có thể truy cập đến biến thành viên khai báo là protected: - Tương tự c|c mức truy cập private (chính nó và bạn nó) - Từ c|c phương thức lớp dẫn xuất - Từ c|c phương thức bạn lớp dẫn xuất (bao gồm hàm bạn và lớp bạn) Đối với trường hợp đầu tiên, chúng ta đ~ tìm hiểu nó phần hàm bạn và lớp bạn Chúng ta khảo sát hai khả sau cùng Đối với khả thứ hai, hãy quan sát ví dụ sau đ}y: Kết 20 C++ Ví dụ #include <iostream> using namespace std; class Polygon{ protected: int w, h; public: void SetValue(int w, int h){ this->w = w; this->h = h; } }; class Rectangle:public Polygon{ public: int GetArea(){ return w*h; } }; int main() { T r a n g | 157 CuuDuongThanCong.com https://fb.com/tailieudientucntt (159) Chương 13 Lập trình hướng đối tượng Rectangle rec; rec.SetValue(4, 5); cout<<rec.GetArea(); return 0; } Giải thích: bạn lưu ý phương thức GetArea đối tượng Rectangle Nó sử dụng các biến th{nh viên thừa kế từ lớp Polygon Những biến th{nh viên n{y khai b|o l{ protected, đó, nó có quyền truy cập đến Trong trường hợp, bạn lớp dẫn xuất, ta có thể quan sát ví dụ minh họa sau đ}y: Ví dụ #include <iostream> using namespace std; class Polygon{ protected: int w, h; public: void SetValue(int w, int h){ this->w = w; this->h = h; } }; class Rectangle:public Polygon{ public: int GetArea(){ return w*h; } friend void ShowWH(Rectangle); }; void ShowWH(Rectangle p){ cout<<p.w<<"x"<<p.h; } Kết 4x5 C++ int main() { Rectangle rec; rec.SetValue(4, 5); ShowWH(rec); return 0; } T r a n g | 158 CuuDuongThanCong.com https://fb.com/tailieudientucntt (160) Chương 13 Lập trình hướng đối tượng Giải thích: trường hợp n{y, phương thức ShowWH là bạn lớp dẫn xuất Rectangle, nó có quyền truy cập đến các biến th{nh viên định protected Tính đa kế thừa – Multiple Inheritance Trong ngôn ngữ lập trình hướng đối tượng, tính kế thừa chia làm hai loại: ngôn ngữ đơn thừa kế và ngôn ngữ đa thừa kế Tính đơn thừa kế: là tính chất cho phép lớp có thể kế thừa từ lớp sở Nếu muốn sử dụng tính đa thừa kế ngôn ngữ lập trình loại này, ta có thể cần phải sử dụng đến khái niệm giao diện interface Ngôn ngữ đơn thừa kế tiêu biểu gồm: Java, C#, Delphi Tính đa thừa kế: là tính chất cho phép lớp có thể kế thừa từ nhiều lớp sở Ngôn ngữ đa thừa kế tiêu biểu gồm: C++ Khai b|o tính đa kế thừa C++ tuân theo cú pháp sau class A: TKMTC1 B, TKMTC2 C, TKMTC3 D,…; Trong đó, + TKMTC1, TKMTC2, TKMTC3 là các từ khóa mức truy cập Chúng có thể là public, protected private + Lớp A gọi là lớp con; lớp B, C, D gọi là các lớp sở Kết #include <iostream> using namespace std; class A{ int a; public: void showA(void); }; class B{ int b; public: void showB(void); }; I’m A I’m B I’m C C++ Ví dụ T r a n g | 159 CuuDuongThanCong.com https://fb.com/tailieudientucntt (161) Chương 13 Lập trình hướng đối tượng class C: public A, public B{ int c; public: void showC(void); }; void A::showA(void){ cout<<"I'm A"<<endl; } void B::showB(void){ cout<<"I'm B"<<endl; } void C::showC(void){ cout<<"I'm C"<<endl; } int main() { C c; c.showA(); c.showB(); c.showC(); return 0; } Giải thích: ví dụ này, lớp C kế thừa từ lớp A và lớp B Khi ta khai báo c l{ đối tượng lớp C, tính kế thừa nên đối tượng c chứa không thành viên lớp c, mà còn có các thành viên lớp A và B Tính đa hình – Polymorphism Một tính theng chốt lớp dẫn xuất là trỏ trỏ vào lớp dẫn xuất tương thích kiểu với trỏ lớp sở Đó chính là thể tính đa hình (cùng lớp sở, trỏ lớp dẫn xuất có các hình thái thể khác nhau) Tính đa hình n{y mang lại cho kĩ thuật lập trình hướng đối tượng thêm ưu điểm việc tạo dựng tính đơn giản hữu dụng và linh hoạt Chúng ta bắt đầu viết chương trình hình chữ nhật và tam giác Lớp chữ nhật và tam giác kế thừa từ lớp đa gi|c v{ chúng có phương T r a n g | 160 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Con trỏ trỏ vào lớp sở (162) Chương 13 Lập trình hướng đối tượng thức th{nh viên riêng Phương thức thành viên này cùng nội dung, lại có cách thể khác (cùng tính diện tích, diện tích hình chữ nhật và hình tam giác có công thức tính khác nhau) Kết #include <iostream> using namespace std; class Polygon{ protected: int w, h; public: void setValue(int w, int h){ this->w = w; this->h = h; } }; class Rectangle:public Polygon{ public: int area(){ return w*h; } }; class Triangle:public Polygon{ public: int area(){ return w*h/2; } }; int main() { Rectangle rec; Triangle tri; Polygon *pol1 = &rec; Polygon *pol2 = &tri; pol1->setValue(4, 5); pol2->setValue(4, 5); Area of Rectangle: 20 Area of Triangle: 10 C++ Ví dụ T r a n g | 161 CuuDuongThanCong.com https://fb.com/tailieudientucntt (163) Chương 13 Lập trình hướng đối tượng cout<<"Area of Rectangle: "<<rec.area()<<endl; cout<<"Area of Triangle: "<<tri.area()<<endl; return 0; } Trong hàm main, chúng ta tạo hai trỏ đối tượng lớp Polygon là pol1 và pol2 Chúng ta truy cập tham chiếu cho rec v{ tri đến hai trỏ n{y, vì chúng l{ hai đối tượng lớp dẫn xuất từ Polygon, nên phép g|n trường hợp này là hợp lệ Chỉ có giới hạn là việc sử dụng *pol1 và *pol2 thay cho rec và tri là không thể (ta không thể sử dụng phương thức pol1->area() hay pol2>area()) Bởi vì phương thức area() không phải là thành viên Polygon Để giải vấn đề này, chúng ta sử dụng phương thức thành viên ảo Như chúng ta thấy ví dụ n{y, hai đối tượng tri v{ rec l{ hai đối tượng khác thừa kế từ polygon Nhưng chúng có cùng phương thức area để tính diện tích Tuy nhiên, cách tính diện tích hình chữ nhật và hình tam giác là hoàn toàn khác Tính đa hình Là tính chất thể nhiều hình thái đối tượng C|c đối tượng khác có thể có cùng phương thức thực thi hành động Nhưng đối tượng lại thực thi h{nh động theo cách riêng mình, mà không giống cho tất c|c đối tượng Thành viên ảo Để quy định phương thức là ảo, chúng ta sử dụng từ khóa virtual Nhờ v{o phương thức ảo, ta có thể định nghĩa lại phương thức thành viên lớp sở bên lớp dẫn xuất Kết #include <iostream> using namespace std; class Polygon{ protected: int w, h; public: void setValue(int w, int h){ Area of Rectangle: 20 Area of Triangle: 10 Area of Polygon: C++ Ví dụ T r a n g | 162 CuuDuongThanCong.com https://fb.com/tailieudientucntt (164) Chương 13 Lập trình hướng đối tượng this->w = w; this->h = h; } virtual int area(){ return (0); }; "<<pol1"<<pol2"<<pol3- T r a n g | 163 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ }; class Rectangle:public Polygon{ public: int area(){ return w*h; } }; class Triangle:public Polygon{ public: int area(){ return w*h/2; } }; int main() { Rectangle rec; Triangle tri; Polygon pol; Polygon *pol1 = &rec; Polygon *pol2 = &tri; Polygon *pol3 = &pol; pol1->setValue(4, 5); pol2->setValue(4, 5); pol3->setValue(4, 5); cout<<"Area of Rectangle: >area()<<endl; cout<<"Area of Triangle: >area()<<endl; cout<<"Area of Polygon: >area()<<endl; (165) Chương 13 Lập trình hướng đối tượng return 0; } Nếu gỡ bỏ từ khóa virtual thì kết là 0, và Sở dĩ l{ vì trường hợp n{y area() l{ phương thức thành viên lớp Polygon Dù phương thức n{y đ~ bị quá tải các lớp th{nh viên, c|c đối tượng *pol1, *pol2, *pol3 l{ c|c đối tượng lớp Polygon, nên gọi phương thức area() là gọi đến phương thức area() Polygon Như vậy, với từ khóa virtual, c|c phương thức các lớp dẫn xuất có thể thực c|c phương thức th{nh viên riêng m{ phương thức th{nh viên đó có thể trùng tên với phương thức thành viên lớp sở Ta cần lưu ý rằng, số ngôn ngữ quy định virtual là mặc định cho các phương thức thành viên (nếu không khai báo từ khóa n{y) Java, C# Nhưng ngôn ngữ lập trình C++ thì không Trong C++, từ khóa virtual là không mặc định cho các phương thức thành viên Hình 22 – Lớp sở ảo T r a n g | 164 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Từ khóa virtual còn có thêm ứng dụng định nghĩa lớp sở ảo Chúng ta xét ví dụ sau (166) Chương 13 Lập trình hướng đối tượng Trong cây thừa kế này, ta thấy lớp F thừa kế từ lớp D và E Lớp D thừa kế từ lớp A và B; lớp E thừa kế từ lớp A v{ C Điều đó có nghĩa l{ lớp F thừa kế lớp A lần Khi ta muốn truy cập đến thuộc tính a (thừa kế từ lớp D và E) thì trình biên dịch không nhận biết ta muốn truy cập đến giá trị a lớp nào Ví dụ Kết #include<iostream> error C2385: ambiguous access of 'a' using namespace std; class A { public: int a; }; class B { public: int b; }; class C { public: int c; }; C++ class D:public A, public B { public: int d; }; class E:public A, public C T r a n g | 165 CuuDuongThanCong.com https://fb.com/tailieudientucntt (167) Chương 13 Lập trình hướng đối tượng { public: int e; }; class F:public D, public E { public: int f; }; int main() { F f; f.a = 1;//Lỗi return 0; } Trình biên dịch thông báo lỗi “error C2385: ambiguous access of 'a'” Để khắc phục nhược điểm này, ta sử dụng lớp sở ảo Không có nhiều khác biệt trường hợp n{y Đơn bổ sung từ khóa virtual vào trước các mức thừa kế lớp D và E (bởi hai lớp n{y thừa kế từ lớp A v{ thừa kế lớp F, đó l{ nguyên nh}n g}y nên lỗi thừa kế lớp A nhiều lần) Ví dụ Kết #include<iostream> Không lỗi using namespace std; C++ class A { public: int a; }; class B T r a n g | 166 CuuDuongThanCong.com https://fb.com/tailieudientucntt (168) Chương 13 Lập trình hướng đối tượng { public: int b; }; class C { public: int c; }; class D:virtual public A, public B { public: int d; }; class E:virtual public A, public C { public: int e; }; class F:public D, public E { public: int f; }; C++ int main() { F f; f.a = 1; return 0; } T r a n g | 167 CuuDuongThanCong.com https://fb.com/tailieudientucntt (169) Chương 13 Lập trình hướng đối tượng Thêm khái niệm mà ta cần quan tâm – quá tải hàm Chúng ta cần lưu ý, khái niệm chồng chất hàm thành viên khác với khái niệm quá tải hàm thành viên (đôi lúc gọi l{ ghi đè hàm thành viên) Chồng chất hàm thành viên (overload) là việc quy định nhiều h{m cùng tên kh|c tham số, các hàm này là thành viên cùng lớp nội các hàm toàn cục Trong đó, qu| tải hàm thành viên (override) là các hàm có cấu trúc giống thuộc hai lớp khác và số chúng thừa kế từ lớp còn lại, đó, ta nói phương thức lớp cha đ~ bị phương thức lớp quá tải Lớp sở trừu tượng Lớp sở trừu tượng (abstract base class) có nhiều nét tương đồng với lớp Polygon ví dụ trên Chỉ có khác biệt l{ phương thức area() lớp Polygon không thực hoàn toàn chức h{m m{ đơn ta khai báo virtual int area() = 0; Nghĩa l{ ta bổ sung vào giá trị sau toán tử gán Với khai báo dạng n{y, người ta gọi l{ phương thức ảo túy (pure virtual function) Một lớp gọi là lớp sở trừu tượng, nó chứa ít phương thức thành viên ảo túy Sự khác biệt lớp sở trừu tượng và lớp đa hình l{ lớp sở trừu tượng không thể tạo thể cho nó Nghĩa l{ ta không thể viết Polygon pol; ví dụ trên Ta có thể sử dụng trỏ, để trỏ đến nó và sử dụng c|c tính đa hình nó mà thôi Kết #include <iostream> using namespace std; class Polygon{ protected: int w, h; public: void setValue(int w, int h){ this->w = w; this->h = h; } virtual int area() = 0; }; Area of Rectangle: 20 Area of Triangle: 10 C++ Ví dụ T r a n g | 168 CuuDuongThanCong.com https://fb.com/tailieudientucntt (170) Chương 13 Lập trình hướng đối tượng Tham số nó rỗng thì ta quy định là void Nếu nó là kiểu tham chiếu (như c|c đối tượng lớp) thì ta nên quy định là trỏ void* và sau này, cần sử dụng đến nó, ta có thể chuyển đổi trỏ void sang trỏ đối tượng Giá trị trả phương thức ảo túy NÊN quy định là T r a n g | 169 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ class Rectangle:public Polygon{ public: int area(){ return w*h; } }; class Triangle:public Polygon{ public: int area(){ return w*h/2; } }; int main() { Rectangle rec; Triangle tri; /* Polygon pol; Phát sinh lỗi vì Polygon là lớp trừu tượng, ta không thể tạo thể cho lớp trừu tượng */ Polygon *pol1 = &rec; Polygon *pol2 = &tri; pol1->setValue(4, 5); pol2->setValue(4, 5); cout<<"Area of Rectangle: "<<pol1>area()<<endl; cout<<"Area of Triangle: "<<pol2>area()<<endl; return 0; } Trong ví dụ trên, ta đ~ sử dụng phương thức ảo túy đơn giản Khi khai báo phương thức ảo túy, ta cần chú ý: (171) Chương 13 Lập trình hướng đối tượng Ví dụ sau đ}y cho ta thấy cách sử dụng lớp trừu tượng trường hợp tham số bên phương thức ảo túy là đối tượng Ta có lớp trừu tượng Point (lớp điểm) và hai lớp Point2D và Point3D thừa kế thừa Point Lớp Point là lớp trừu tượng chứa phương thức ảo túy là KhoangCach Các lớp Point2D và Point3D quá tải phương thức ảo túy này Kết #include <iostream> #include <math.h> .:: -Nhap toa cho hai Point2D u va v :: + Toa u: 1 + Toa v: D(u,v) = :: -Nhap toa cho hai Point3D u va v :: + Toa u: 1 + Toa v: 1 D(u3,v3) = using namespace std; class Point { protected: float x, y; public: float& getX(void); float& getY(void); virtual float KhoangCach(void*)=0; }; float& Point::getX(void) { return x; } float& Point::getY(void) { return y; } class Point2D:public Point { public: float KhoangCach(void*); }; C++ Ví dụ class Point3D:public Point { T r a n g | 170 CuuDuongThanCong.com https://fb.com/tailieudientucntt (172) Chương 13 Lập trình hướng đối tượng float z; public: float& getZ(void); float KhoangCach(void*); }; float Point2D::KhoangCach(void* p) { Point2D* q = (Point2D*)(p); return sqrt(pow(q->x-x,2)+pow(q->y-y,2)); } float& Point3D::getZ(void) { return z; } int main() { Point2D v2; Point2D u2; cout<<".:: -Nhap toa cho hai Point2D u va v -::."<<endl; cout<<"+ Toa u:"<<endl; cin>>v2.getX(); cin>>v2.getY(); cout<<"+ Toa v:"<<endl; cin>>u2.getX(); cin>>u2.getY(); Point* u = &v2; Point* v = &u2; cout<<"D(u,v) = "<<u->KhoangCach(v); Point3D v3; Point3D u3; cout<<endl<<".:: -Nhap toa cho hai T r a n g | 171 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ float Point3D::KhoangCach(void* p) { Point3D* q = (Point3D*)(p); return sqrt(pow(q->x-x,2)+pow(q->yy,2)+pow(q->z-z,2)); } (173) Chương 13 Lập trình hướng đối tượng Point3D u va v -::."<<endl; cout<<"+ Toa u:"<<endl; cin>>v3.getX(); cin>>v3.getY(); cin>>v3.getZ(); cout<<"+ Toa v:"<<endl; cin>>u3.getX(); cin>>u3.getY(); cin>>u3.getZ(); Point* p = &v3; Point* q = &u3; cout<<"D(u3,v3) = "<<p->KhoangCach(q); cout<<endl; return 0; } Giải thích: chúng ta cần chú ý rằng, khai báo lớp trừu tượng (chứa phương thức ảo túy) thì lớp thừa kế từ nó nên có phương thức có khai báo hoàn toàn trùng khớp với phương thức ảo túy này (về tên gọi, danh sách tham số; trừ phép gán = và từ khóa virtual không xuất phương thức quá tải lớp con) Nếu ta khai báo không trùng khớp, thì lớp có phương thức ảo túy này, và hiển nhiên nó là lớp trìu tượng Trong ví dụ này, khoảng cách có thể là hai điểm Point2D hai điểm Point3D Do đó, lớp trừu tượng, ta sử dụng void* Trong lớp con, ta chuyển đổi từ void* sang Point2D* Point3D* tùy thuộc vào lớp Point2D hay Point3D Tính trừu tượng hóa - Abstraction Khi phân tích nhóm đối tượng, ta thường tìm c|c điểm chung v{ đặc trưng cho c|c đối tượng, từ đó x}y dựng nên lớp trừu tượng sở để chứa c|c phương thức t|c động đến c|c c|c đặc trưng chung đó Mỗi đối tượng nhóm đối tượng trên thừa kế từ lớp trừu tượng sở T r a n g | 172 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Tính trừu tượng hóa là tính chất tập trung vào phần cốt lõi đối tượng, bỏ qua tiểu tiết không cần thiết Nó còn thể lớp trừu tượng sở: lớp trừu tượng sở chứa c|c đặc tính chung, tổng quát cho nhóm đối tượng Khi đó, nhóm đối tượng thừa kế từ lớp trừu tượng n{y để nhận các thuộc tính chung, đồng thời bổ sung thêm tính (174) Chương 13 Lập trình hướng đối tượng có phương thức đặc trưng cho nhóm đối tượng này Tính trừu tượng l{ đặc trưng ngôn ngữ lập trình hướng đối tượng Hàm mẫu – Template Function Trong nhiều trường hợp chúng ta cần xây dựng hàm mà kiểu liệu các tham số và kiểu liệu hàm trả l{ không tường minh (có nghĩa l{ chúng có dạng tổng quát, có thể là số nguyên, số nguyên d{i, x}u…) Khi đó, để giải vấn đề này, chúng ta có thể sử dụng quá tải h{m trên, trỏ hàm và thêm cách thức nữa, đó l{ h{m mẫu – template function Để khai báo kiểu liệu không tường minh này, chúng ta có thể khai b|o sau: template <class T> Ví dụ sau đ}y minh họa việc sử dụng hàm mẫu template function Ví dụ Kết #include <iostream> using namespace std; abcd template <class T> T add(T a, T b) { return a+b; } int main() { cout<<add<int>(1, 2)<<endl; cout<<add<string>(“ab”, “cd”)<<endl; } Giải thích: khai báo hàm mẫu template function dùng để tạo kiểu liệu không tường minh Việc xây dựng hàm add với kiểu liệu trả là kiểu T và các tham số nó l{ kiểu T Do đó, h{m add n{y có thể sử dụng dạng tổng qu|t, dùng để cộng số, cộng x}u… Để quy định là cộng số, ta có thể quy định sau tên hàm: tên_hàm<kiểu_dữ_liệu> Như vậy, ví dụ trên, đối tượng cout thứ in giá trị tổng hai số nguyên v{ Đối tượng cout thứ hai in giá trị ghép nối hai x}u “ab” v{ “cd” Tương tự hàm template, ta có khai b|o lớp template template <class T> T r a n g | 173 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lớp mẫu – Template class (175) Chương 13 Lập trình hướng đối tượng class mypair{ T values[2]; public: mypair(T first, T second) { values[0]=first; values[1]=second; } }; int main() { mypair<int>x(2, 3); mypair<float>f(2.3, 3.3); return 0; } Ta có thể sử hàm tạo với tham số first và second thuộc loại T các tham số cụ thể ấn định Ví dụ mypair<int> x(1, 2) sử dụng hàm tạo lớp mypair để tạo đối tượng chứa hai thuộc tính là và thuộc số nguyên Tương tự cho trường hợp mypair<float>f(2.3, 3.3) Khi cần sử dụng nhiều kiểu liệu không tường minh, ta có thể bổ sung thêm khai báo template: template<class T, class U, class V,…> Và triệu gọi phương thức, ta đơn đích danh kiểu liệu tương ứng: myclass<kiểu_dữ_liệu_1, kiểu_dữ_liệu_2, kiểu_dữ_liệu_3, > đối_tượng; Ví dụ #include <iostream> Kết real=1 image=2 real=3 image=4 4+I*6 using namespace std; template <class T> T r a n g | 174 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Lớp template sử dụng rộng rãi lập trình C++ có nhiều lớp có chung c|c phương thức và liệu chúng khác kiểu liệu cho các biến thành viên Ví dụ lớp số phức: các liệu thành viên là phần thực và phần ảo có thể là số nguyên, số thực Ví dụ sau đ}y xây dựng lớp số phức theo hướng tiếp cận template class (176) Chương 13 Lập trình hướng đối tượng class complex{ private: T real; T imag; public: complex(void); complex(T, T); complex(const complex<T>&); complex operator+(const complex<T>&); template <class T, class charT, class traits> friend basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&, const complex<T>&); template <class T, class charT, class traits> friend basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>&, complex<T>&); }; template <class T> complex<T>::complex(void) { real = 0; imag = 0; } template <class T> complex<T>::complex(T real, T imag) { this->real = real; this->imag = imag; } template <class T> complex<T> complex<T>::operator+(const complex<T>& c) { T r a n g | 175 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ template <class T> complex<T>::complex(const complex<T>& c) { this->real = c.real; this->imag = c.imag; } (177) Chương 13 Lập trình hướng đối tượng } return complex(real+c.real, imag+c.imag); template <class T, class charT, class traits> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const complex<T>& c) { os<<c.real<<"+I*"<<c.imag; return os; } int main () { complex<int> c; complex<int> d; cin>>c>>d; complex<int> x = c + d; cout<<x; return 0; } Giải thích: ví dụ này, không có nhiều khác biệt so với lớp số phức thông thường Chúng ta đơn bổ sung khai báo template<class T> v{o bên trước khai báo lớp v{ khai b|o phương thức có sử dụng kiểu liệu dùng chung T Riêng phương thức nhập xuất liệu, cần lưu ý: ta không thể sử dụng kiểu liệu thông thường là ostream& và istream&, m{ thay v{o đó l{ basic_ostream<?, ?> v{ basic_istream<?,?> Đ}y l{ hai lớp template với hai đối số Điều này giải thích trước các toán tử nhập/xuất lại bổ sung thêm khai báo template<class T, class charT, class traits> Chúng ta cần chú ý thêm điểm, tất các từ complex ví dụ trên phân thành hai nhóm: nhóm kiểu liệu complex và nhóm T r a n g | 176 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ template <class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& c) { cout<<"real="; is>>c.real; cout<<"image="; is>>c.imag; return is; } (178) Chương 13 Lập trình hướng đối tượng tên hàm complex Tên hàm tạo complex – ta không bổ sung kiểu liệu định T – đơn là complex Đối với các từ complex còn lại, thiết phải định kiểu liệu T – complex<T> Định nghĩa class template có phạm vi tác dụng lớp h{m định sau nó Nó không phải là khai báo có thể dùng chung Điều này giải thích vì ta lại sử dụng quá nhiều khai b|o class template trên Tổng kết lập trình hướng đối tượng Cơ sở lập trình hướng đối tượng đó l{ c|c đối tượng và lớp Đối tượng là thực thể giới thực Lớp mô tả đối tượng ngôn ngữ lập trình Ngược lại, đối tượng là thể (instance) lớp Ngôn ngữ lập trình hướng đối tượng có đặc trưng bản: - Tính trừu tượng hóa liệu (abstraction): là khía cạnh mà các ngôn ngữ không quan t}m đến tiểu tiết đối tượng Nó tập trung vào thứ cốt lõi đối tượng Tính trừu tượng còn thể khái niệm lớp trừu tượng sở - Tính đóng gói (encapsulation) hay tính đóng gói và che dấu thông tin (encapsulation and information hiding): là tính chất mức độ chia sẻ thông tin Một đối tượng khác không có quyền truy cập và làm thay đổi thông tin đối tượng nội tại, có đối tượng nội định có nên thay đổi thông tin mình hay không - Tính thừa kế (hay kế thừa - inheritance): là tính chất cho phép c|c đối tượng tiếp nhận liệu từ đối tượng khác mà nó thừa kế Nhờ vào tính thừa kế, c|c đối tượng có thể sử dụng c|c tính đối tượng khác Tính thừa kế chia làm hai loại: đơn thừa kế v{ đa thừa kế Đơn thừa kế là tính chất cho phép lớp thừa kế từ lớp sở; còn tính đa thừa kế cho phép lớp kế thừa từ nhiều lớp sở C++ là ngôn ngữ lập trình hỗ trợ đa thừa kế T r a n g | 177 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Tính đa hình (polymorphism): là nhiều hình th|i C|c đối tượng khác có thể có cùng phương thức thực thi h{nh động, c|ch thức thực thi h{nh động đó có thể khác (179) Chương 14 Namespace CHƯƠNG 14 NAMESPACE Từ khóa namespace Nhờ vào namespace, ta có thể nhóm các thực thể lớp, đối tượng và các h{m tên gọi tương ứng với từ khóa namespace Theo cách này, các phạm vi toàn cục lại chia nhỏ thành các phạm vi toàn cục con, mà phạm vi toàn cục này có tên gọi riêng Để khai báo namespace, ta sử dụng từ khóa namespace theo cú pháp sau namespace tên_của_namespace { các_thực_thể } Để truy cập đến các thực thể namespace, ta sử dụng toán tử phạm vi :: Ví dụ Kết #include <iostream> using namespace std; 3.14 namespace first { int var = 5; } namespace second { double var = 3.14; } int main() { cout<<first::var<<endl; cout<<second::var<<endl; return 0; } Như ta thấy, ví dụ này có hai biến toàn cục l{ var (lưu ý, hai biến var n{y là biến toàn cục vì phạm vi hoạt động nó là toàn chương trình) Dù l{ trùng tên, chúng thuộc vào các namespace khác nhau, nên khai báo này là hợp lệ Trang | 178 CuuDuongThanCong.com https://fb.com/tailieudientucntt (180) Chương 14 Namespace Từ khóa using Từ khóa using sử dụng để đưa tên gọi từ namespace sang vùng khai báo Khi sử dụng using namespace tên_namespace, chúng ta không cần sử dụng tên_namespace gọi đến thực thể nó Ví dụ Kết #include <iostream> using namespace std; namespace first { int x = 5; int y = 5; } namespace second { double x = 1.60; double y = 3.14; } int main() { using namespace first; cout<<x <<endl; cout<<y<<endl; return 0; } Trong trường hợp này, sử dụng namespace first, chúng ta có thể sử dụng các biến namespace nó mà không cần gọi đến tên namespace Nhưng lưu ý rằng, using hai namespace thì trường hợp này, ta không thể truy cập đến các biến x và y theo cách này (vì chúng trùng tên), mà có thể truy cập theo cách sử dụng toán tử phạm vi :: Ta có thể sử dụng từ khóa using sau Kết 3.14 C++ Ví dụ #include <iostream> using namespace std; namespace first { int x = 5; int y = 5; T r a n g | 179 CuuDuongThanCong.com https://fb.com/tailieudientucntt (181) Chương 14 Namespace } namespace second { double x = 1.60; double y = 3.14; } int main() { using first::x; using second::y; cout<<x <<endl; cout<<y<<endl; return 0; } Phạm vi namespace Một namespace khai báo sử dụng từ khóa using có tác dụng phạm vi m{ nó khai b|o Điều đó có nghĩa l{ ta using namespace tên_namespace, thì nó có tác dụng khối lệnh mà ta khai báo Kết 5 1.6 3.14 C++ Ví dụ #include <iostream> using namespace std; namespace first { int x = 5; int y = 5; } namespace second { double x = 1.60; double y = 3.14; } int main() { { using namespace first; cout<<x<<endl; cout<<y<<endl; } { T r a n g | 180 CuuDuongThanCong.com https://fb.com/tailieudientucntt (182) Chương 14 Namespace using namespace second; cout<<x<<endl; cout<<y<<endl; } } return 0; Tái định danh cho namespace Nếu muốn khai báo tên cho namespace đ~ tồn tại, hãy sử dụng cú pháp namespace tên_mới_của_namespace = tên_đã_khai_báo_của_name_space; Namespace std C++ Tất các tệp thư viện chuẩn C++ khai b|o l{ thực thể namespace std Điều đó giải thích viết chương trình C++ có sử dụng các hàm nhập xuất bản, các kiểu liệu string… thì ta phải sử dụng khai báo using namespace std T r a n g | 181 CuuDuongThanCong.com https://fb.com/tailieudientucntt (183) Chương 15 Ngoại lệ CHƯƠNG 15 NGOẠI LỆ Các ngoại lệ là cách thức giúp chúng ta t|c động ngược trở lại với các tình sinh ngoại lệ đó Để nắm bắt ngoại lệ, chúng ta sử dụng cú ph|p try…catch throw Mệnh đề try…catch Nếu viết chương trình có khả nảy sinh ngoại lệ, chúng ta cần đặt nó vào khối lệnh từ khóa try, ngoại lệ ph|t sinh, h{nh động xử lý đặt khối lệnh từ khóa catch Ví dụ Kết #include <iostream> 20 using namespace std; int main() { int a; try{ throw 20; }catch(int e){ cout<<e; } return 0; } Giải thích: Trong chương trình n{y, lệnh throw cố vượt qua ngoại lệ tương ứng với mã 20, ngoại lệ này bị nắm bắt câu lệnh try…catch Do ngoại lệ phát sinh, nên lệnh mệnh đề catch thực thi Nếu có nhiều ngoại lệ phát sinh, ta có thể sử dụng cấu trúc đa tầng mệnh đề catch: try{…}catch(…){…}catch(…){…}… Mệnh đề throw Khi khai báo hàm, h{m đó có khả ph|t sinh ngoại lệ, chúng ta có thể định từ khóa throw cho nó type tên_hàm(danh_sách_tham_số) throw (int) Trang | 182 CuuDuongThanCong.com https://fb.com/tailieudientucntt (184) Chương 15 Ngoại lệ Nếu có throw() – nghĩa l{ không định loại liệu throw – thì hàm cho phép vượt qua ngoại lệ Nếu hàm không có mệnh đề throw thì không phép vượt qua ngoại lệ Thư viện chuẩn exception Thư viện chuẩn C++ cung cấp cho chúng ta thư viện để quản lý các ngoại lệ đó l{ exception Nó nằm namespace std Lớp này có hàm tạo mặc định, hàm tạo chép, các toán tử, hàm hủy và hàm thành viên ảo what() Hàm này trả trỏ kí tự phát sinh ngoại lệ Nó có thể quá tải lớp dẫn xuất Ví dụ #include <iostream> #include <exception> Kết Co ngoai le ! using namespace std; Khi ta xây dựng ứng dụng có khả ph|t sinh ngoại lệ, ta cần quy định lớp đối tượng xây dựng phải thừa kế từ lớp exception n{y Điều này giúp chúng ta có thể nắm bắt ngoại lệ và bỏ qua chúng số trường hợp các ngoại lệ này có thể gây các lỗi không mong muốn cho ứng dụng Ví dụ sau đ}y giúp ứng dụng chúng ta vượt qua phép chia cho và tiếp tục thực thi chương trình T r a n g | 183 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ class myexception:public exception{ virtual const char* what() const throw(){ return “Co ngoai le !”; } }; int main(){ try{ myexception myex; throw myex; }catch(exception& e){ cout<<e.what(); } return 0; } Mỗi có ngoại lệ xảy ra, mệnh đề catch thực hiện, và ngoại lệ nắm bắt, kết in luôn l{ c}u thông b|o “Co ngoai le” (185) Chương 15 Ngoại lệ Ví dụ #include<iostream> Kết Chia cho using namespace std; class MyNumber:public exception { private: float x, y; public: virtual const char* what() const throw() { return "Chia cho 0"; }; } MyNumber(float, float); float DivMe(void); MyNumber::MyNumber(float x, float y) { this->x = x; this->y = y; } float MyNumber::DivMe(void) { x/y; } C++ int main() { MyNumber m(1, 0); try { m.DivMe(); throw m; } catch(exception& e) { cout<<e.what(); } T r a n g | 184 CuuDuongThanCong.com https://fb.com/tailieudientucntt (186) Chương 15 Ngoại lệ return 0; C++ } Giải thích: ví dụ này, lớp khai báo có khả ph|t sinh ngoại lệ vì nó thực phép chia (ngoại lệ tương ứng l{ chia cho 0) Điều này giải thích vì ta cần cho nó thừa kế từ lớp exception Mỗi giá trị nhập vào cho biến thành viên y là 0, thì ngoại lệ “Chia cho 0” phát sinh Khi ngoại lệ phát sinh, ta sử dụng cú ph|p try để bao bọc quanh vùng lệnh phát sinh ngoại lệ (cụ thể là m.DivMe()) Khi ngoại lệ phát sinh, mệnh đề catch thực thi và in lỗi tương ứng với hàm thành viên what – tức l{ “Chia cho 0” T r a n g | 185 CuuDuongThanCong.com https://fb.com/tailieudientucntt (187) Chương 16 L{m việc với file CHƯƠNG 16 LÀM VIỆC VỚI FILE C++ cung cấp cho ta các lớp sau đ}y để làm việc với file ofstream: lớp ghi liệu file ifstream: lớp đọc liệu từ file fstream: lớp để đọc/ghi liệu từ/lên file Các lớp này là dẫn xuất trực tiếp gián tiến từ lớp istream và ostream Chúng ta sử dụng đối tượng cin là thể lớp istream và cout là thể lớp ostream Chúng ta có thể sử dụng c|c đối tượng lớp ofstream, ifstream fstream để làm việc trực tiếp với file Ví dụ sau đ}y cho thấy điều này Ví dụ #include <iostream> #include <fstream> using namespace std; int main(){ ofstream myfile; myfile.open(“example.txt”); myfile.<<”Ghi du lieu file”; myfile.close(); return 0; } Ví dụ này đơn ghi c}u “Ghi du lieu file” lên tệp example.txt Chúng ta nghiên cứu bước làm việc với c|c đối tượng ba lớp mà chúng ta nêu trên Mở file Để mở file chương trình đối tượng stream, chúng ta sử dụng hàm thành viên open(tên_file, chế_độ_mở) Trong đó, Trang | 186 CuuDuongThanCong.com https://fb.com/tailieudientucntt (188) Chương 16 L{m việc với file - tên_file: là tên file mà chúng ta cần mở Ta cần đảm bảo cung cấp đường dẫn chính x|c đến tập tin này Ta cần lưu ý đường dẫn đến tập tin Đường dẫn có thể l{ đường dẫn tuyệt đối tương đối Nếu cung cấp đường dẫn tương đối, ta cần tuân thủ nguyên tắc l{m việc với tệp cpp v{ h tôi đ~ trình b{y trên - chế_độ_mở: là tham số tùy chọn, thường C++ nó có thể là các cờ hiệu sau đ}y: Cờ hiệu Giải thích ios::in ios::out ios::binary Mở file để đọc Mở file để ghi Mở file chế độ nhị ph}n (thường áp dụng cho các file mã hóa) ios::ate Thiết lập vị trí khởi tạo vị trí cuối cùng file Nếu cờ hiệu này không thiết lập bất kì giá trị nào, vị trí khởi tạo đặt đầu file ios::app Mọi liệu ghi file tiến hành bổ sung vào cuối file (không ghi đè lên file) Cờ hiệu này có thể sử dụng tác vụ mở file để ghi ios::trunc Nếu file mở để ghi đ~ tồn tại, nó ghi đè lên nội dung cũ Các cờ hiệu này có thể kết hợp cách sử dụng toán tử dịch bit OR (|) Ví dụ, tôi muốn mở file nhị phân example.bin để ghi liệu và bổ sung liệu cuối file này, tôi có thể viết sau Lớp chế_độ_mở mặc định ofstream ios::out ifstream ios::in fstream ios::in|ios::out Nếu tham số ấn định giá trị cụ thể, thì tham số sử dụng ghi đè lên tham số mặc định mà không phải là kết hợp với tham số mặc định Ví dụ, sử dụng ofstream để mở file với tham số chế_độ_mở T r a n g | 187 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ ofstream myfile; myfile.open(“example.bin”, ios::out|ios::app|ios::binary); Thành viên open các lớp ofstream, ifstream và fstream có tham số chế_độ_mở mặc định (trong trường hợp tham số n{y không định) đưa bảng sau: (189) Chương 16 L{m việc với file quy định là ios::binary, thì tham số mở là ios::binary mà không phải là ios::out|ios::binary Nếu sử dụng hàm khởi tạo cho các lớp n{y, thì phương thức thành viên open tự động triệu gọi Nghĩa l{ ta có thể viết ofstream myfile(“example.bin”, ios::out|ios::app, ios::binary); thay cho cách viết trên Để kiểm tra file đ~ mở th{nh công hay chưa, chúng ta có thể sử dụng phương thức is_open Nếu đ~ mở thành công, nó trả giá trị true và ngược lại, mở không thành công, nó trả giá trị false Đóng file Khi chúng ta hoàn tất công việc với file, chúng ta cần thực thao tác đóng file lại Tác vụ này là bắt buộc ta đ~ ho{n tất các tác vụ trên file Khi đó, ta đơn triệu gọi phương thức thành viên close myfile.close(); Nếu phương thức hủy đối tượng triệu gọi, phương thức close tự động gọi theo File văn Đối với file văn bản, thì cờ hiệu ios::binary không sử dụng Những file văn đơn chứa văn Để đọc ghi liệu trên file này ta sử dụng toán tử xuất – nhập liệu (<< và >>) Ghi liệu lên file văn #include <iostream> #include<fstream> int main(){ ofstream myfile (“example.txt”); if (myfile.is_open(){ myfile<<”Dong da ghi\n”; myfile<<”Dong da ghi\n”; myfile.close(); } else cout<<”Khong the ghi du lieu len file”; T r a n g | 188 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ using namespace std; (190) Chương 16 L{m việc với file return 0; } Ví dụ trên cho thấy việc ghi liệu lên file văn nhờ vào toán tử << Ví dụ sau đ}y minh họa cho việc đọc liệu từ file văn toán tử >> Đọc liệu từ file văn #include <iostream> #include<fstream> #include<string> using namespace std; int main(){ ifstream myfile (“example.txt”); if (myfile.is_open(){ while(!myfile.eof()){ getline(myfile, line); cout<<line<<endl; } myfile.close(); } else cout<<”Khong the ghi du lieu len file”; return 0; } Trong ví dụ này, chúng ta có sử dụng hàm thành viên eof đối tượng ifstream Hàm thành viên này có chức kiểm tra vị trí đọc đ~ là vị trí cuối cùng file hay chưa, chưa, liệu từ file tiếp tục đọc Ngược lại, nó dừng việc đọc liệu Kiểm tra trạng thái các cờ hiệu Trạng thái bad() fail() eof() Giải thích Nếu tác vụ đọc/ghi file bị lỗi, nó trả giá trị true; ngược lại, nó trả giá trị false Trả giá trị true trường hợp bad(), gặp lỗi định dạng, nó trả giá trị true (ví dụ đọc số từ file văn bản) Trả giá trị true file đ~ đọc đến vị trí cuối T r a n g | 189 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ví dụ trên cho ta các thức để kiểm tra trạng thái các cờ hiệu Bảng sau đ}y liệt kê các trạng thái cờ hiệu có thể sử dụng C++ (191) Chương 16 L{m việc với file cùng file, ngược lại, trả giá trị false good() Nó trả giá trị true bad(), fail() và eof() không phát sinh lỗi Để thiết lập lại các mức kiểm tra trạng thái cờ hiệu, ta sử dụng phương thức thành viên clear() Con trỏ get và put Mọi đối tượng luồng xuất nhập có ít trỏ luồng: - Luồng ifstream có trỏ istream mà ta gọi là trỏ get để trỏ vào phần tử có thể đọc liệu - Luồng ofstream có trỏ ostream mà ta gọi là trỏ put để trỏ vào phần tử có thể ghi liệu - Luồng fstream có hai trỏ get v{ put để đọc và ghi liệu Những trỏ luồng nội này trỏ vào vị trí đọc và ghi với luồng có thể sử dụng c|c h{m th{nh viên sau đ}y: Hàm thành viên tellg() và tellp() Hai hàm thành viên này không có tham số và trả giá trị kiểu liệu dạng pos_type Kiểu liệu này chất là số nguyên integer Nó mô tả vị trí của trỏ luồng get và trỏ luồng put Hàm thành viên seekg() và seekp() Những hàm thành viên này cho phép chúng ta thay đổi vị trí trỏ luồng get và put Cả hai h{m n{y chồng chất với hai prototype khác Prototype thứ nhất: seekg(vị_trí); seekp(vị_trí); Việc sử dụng c|c prototype n{y giúp l{m thay đổi vị trí tuyệt đối (vị trí này tính từ đầu file) Kiểu liệu tham số này trùng với kiểu liệu hai hàm tellg() và tellp() trên C++ Prototype thứ hai: seekg(vị_trí, kiểu); T r a n g | 190 CuuDuongThanCong.com https://fb.com/tailieudientucntt (192) Chương 16 L{m việc với file seekp(vị_trí, kiểu); Việc sử dụng prototype này l{m thay đổi vị trí trỏ get và trỏ put x|c định theo vị trí tương đối theo tham số vị_trí và tham số kiểu Tham số vị_trí thành viên thuộc kiểu liệu off_type, nó l{ kiểu số nguyên, nó tương ứng với vị trí trỏ get/put đặt vào Tham số kiểu là kiểu liệu seekdir, nó là kiểu enum để x|c định vị_trí trỏ get/put kể từ tham số kiểu, nó có thể nhận các giá trị sau đ}y ios::beg vị_trí đếm từ vị trí bắt đầu luồng ios::cur vị_trí đếm từ vị trí luồng ios::end vị_trí đếm từ vị trí cuối luồng Điều n{y có nghĩa l{ h{m chồng chất hai tham số n{y tương tự hàm tham số, vị trí bắt đầu tính hàm tham số luôn là từ vị trí đầu tiên, còn hàm hai tham số có ba vị trí có thể bắt đầu đếm – bắt đầu (ios::beg), (ios::cur) hay cuối file (ios::end) Ví dụ Kết #include <iostream> Size=10 bytes #include <fstream> using namespace std; int main(){ long begin, end; ifstream myfile(“example.txt”); begin = myfile.tellg(); myfile.seekg(0, ios::end); end = myfile.tellg(); 10 myfile.close(); 11 cout<<”Size=”<<(end-begin)<<” bytes”; 12 return 0; 13 } Giải thích: chương trình trên chúng ta mở file example.txt Chúng ta đếm kích thước file này Khi mở file, trỏ get đặt vào vị trí đầu file Khi đó, dòng lệnh gán giá trị khởi đầu cho biến begin (trong trường hợp này là 0) Dòng lệnh đặt trỏ get vào vị trí cuối cùng file (vị trí kể từ cuối file tính lên) Dòng lệnh gán vị trí – vị trí cuối file cho biến end Điều đó có nghĩa l{ gi| trị end-begin chính là kích T r a n g | 191 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ta hãy quan sát ví dụ sau đ}y (193) Chương 16 L{m việc với file thước file Ta cần lưu ý rằng, file văn bản, kí tự tương ứng với byte – đó chính l{ quy định C++ (một kiểu char chiếm byte) Hay nói chính x|c, chương trình n{y đếm số kí tự file văn File nhị phân Đối với file nhị phân, việc đọc ghi liệu toán tử tích trách >> và toán tử chèn << h{m getline l{ không có hiệu lực, chúng không định dạng theo kiểu văn file văn trên (không dùng phím space để tạo khoảng cách, không có kí tự xuống dòng…) Các luồng file gồm hai h{m th{nh viên để đọc và ghi liệu là read và write Hàm thành viên write là hàm thành viên lớp ostream thừa kế cho ofstream Và hàm read là thành viên lớp istream thừa kế cho ifstream C|c đối tượng lớp fstream có hai hàm thành viên này Chúng có prototype sau: write(khối_bộ_nhớ, kích_thước); read(khối_bộ_nhớ, kích_thước); Ví dụ #include<iostream> #include<fstream> using namespace std; ifstream::pos_type size; char* memblock; int main(){ ifstream file(“example.bin”, ios::in|ios::binary|ios::ate); if(file.is_open()){ size = file.tellg(); memblock = new char[size]; file.seekg(0, ios::beg); file.read(memblock, size); file.close(); cout<<”Hoan tat !”; //Làm việc với liệu trỏ memblock T r a n g | 192 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Ở đó, khối_bộ_nhớ là trỏ kiểu char (char*) và nó biểu diễn địa mảng c|c byte m{ nó đọc ghi Biến kích_thước là kiểu số nguyên integer, nó định số các kí tự có thể đọc/ghi lên khối nhớ Chúng ta hãy quan sát ví dụ sau đ}y (194) Chương 16 L{m việc với file delete[] memblock; } else cout<<”Khong mo duoc file.”; return 0; } Giải thích: chương trình, ta mở file example.bin Chế độ mở file để đọc (ios::in), theo kiểu file nhị phần (ios::binary), đặt trỏ get vào cuối file (ios::ate) Sau mở file, hàm file.tellg() cho biết kích thước thực file Sau đó h{m file.seekg đặt vị trí trỏ get v{o đầu file (vị trí kể từ vị trí đầu tiên) và tiến h{nh đọc theo khối nhờ nhờ vào file.read Sau hoàn tất, phương thức close triệu gọi để kết thúc việc đọc file Khi đó, liệu từ file đ~ đọc vào mảng memblock Chúng ta có thể bổ sung tác vụ thao tác với liệu muốn Cuối cùng, trỏ memblock bị xóa để giải phóng nhớ Khi thực thi các tác vụ đọc/ghi liệu với file, chúng ta thực thi trên thông qua đệm có kiểu liệu streambuf Bộ đệm này là khối nhớ đóng vai trò trung gian các luồng và file vật lý Ví dụ, với ofstream, thời điểm h{m put gọi, kí tự không ghi trực tiếp lên file mà nó ghi lên đệm Khi đệm đầy, liệu chứa đó ghi lên file (nếu đó l{ luồng ghi liệu) hay xóa bỏ để làm rãnh nhớ (nếu đó là luồng đọc liệu) Tiến trình n{y gọi l{ đồng hóa và có các tình sau đ}y: - Khi file đ~ đóng: trước đóng file, tất liệu nhớ chưa đầy đồng và chuẩn bị để đọc/ghi lên file - Khi nhớ đầy: đệm có kích thước giới hạn Khi nó đầy, nó tự động đồng hóa - Bộ điều phối: các điều phối sử dụng trên luồng, tiến trình đồng dứt điểm diễn Những điều phối này bao gồm: flush và endl - Hàm thành viên sync(): hàm thành viên sync() triệu gọi, tiến trình đồng hóa diễn Hàm này trả kiểu integer (int) tương ứng với -1, luồng không có đệm liên kết trường hợp đọc/ghi thất bại Ngược lại, nó trả giá trị T r a n g | 193 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Bộ đệm và Đồng hóa (195) Chương 17 C|c lớp thư viện CHƯƠNG 17 CÁC LỚP THƯ VIỆN Lớp số phức complex Đ}y l{ lớp template Khi khởi tạo lớp đối tượng số phức, ta cần định kiểu liệu cho nó Nó nằm thư viện complex Ví dụ việc khởi tạo số phức: complex<float> c(10.2, 3); khởi tạo đối tượng số phức mà phần thực và phần ảo nó là các số thực Thông thường, kiểu liệu định cho lớp số phức là kiểu liệu thực: float, double long double Nhưng chúng ta hoàn toàn có thể sử dụng kiểu số nguyên Các hàm thành viên lớp số phức Tên thành viên real imag Phương thức khởi tạo: complex<?> c; complex<?> c(real, imag); complex <?> c(d); imag() Mức truy cập private Chức Phần thực và phần ảo public real() public operator= operator+= operator-= operator*= operator/= public _Add(complex) _Sub(complex) _Mul(complex) _Div(complex) protected C|c phương thức khởi tạo: không tham số, có hai tham số và hàm tạo chép Phương thức getImage – trả giá trị phần ảo Phương thức getReal – trả giá trị phần thực Các hàm toán tử thành viên: toán tử gán, toán tử cộng hợp nhất, toán tử trừ hợp nhất, toán tử nhân hợp nhất, toán tử chia hợp C|c phương thức cộng, trừ, nhân và chia hai số phức public Trang | 194 CuuDuongThanCong.com https://fb.com/tailieudientucntt (196) Chương 17 C|c lớp thư viện Tên phương thức operator + operator – operator * operator / operator = operator == operator != operator >> operator << real(complex) imag(complex) abs(complex) norm(complex) conj(complex) polar(float, float) cos(complex) sin(complex) tan(complex) cosh(complex) sinh(complex) tanh(complex) exp(complex) sqrt(complex) log(complex) log10(complex) pow(complex, <T>) Chức Toán tử cộng hai số phức Toán tử trừ hai số phức Toán tử nhân hai số phức Toán tử chia hai số phức Toán tử gán số phức Toán tử so sánh Toán tử so sánh khác Toán tử nhập số phức Toán tử xuất số phức Trả phần thực số phức Trả phần ảo số phức Trả giá trị modul số phức Modul số phức tính theo công thức √ Trả giá trị là chuẩn số phức Chuẩn số phức l{ bình phương gi| trị modul, tức là Trả số phức liên hợp Số phức liên hợp số phức a nhận cách thay phần ảo a –a Trả số phức hệ tọa độ Decac Hai tham số truyền v{o tương ứng với modul và argument số phức ( ) { ( ) Trả giá trị cosin số phức Trả giá trị sin số phức Trả giá trị tan số phức Trả giá trị cosin hyperbol số phức Trả giá trị sinh hyperbol số phức Trả giá trị tan hyperbol số phức Trả giá trị e lũy thừa số phức Trả giá trị bậc hai số phức Trả giá trị logarith số tự nhiên (logarith nepe) số phức Trả giá trị logarith số 10 số phức Trả lũy thừa số phức Tham số thứ hai có thể là số phức, số thực, số nguyên T r a n g | 195 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Các hàm toàn cục (hoặc hàm bạn) (197) Chương 17 C|c lớp thư viện Đ}y l{ lớp số học khá hữu dụng tính toán khoa học Chúng ta có thể sử dụng nó mà không cần xây dựng lại lớp này Tuy nhiên, bắt đầu tiếp xúc với lập trình hướng đối tượng, cần thiết phải xây dựng nó Lớp ngăn xếp stack Lớp stack l{ lớp template Nó làm việc theo nguyên tắc đợi – v{o trước sau Lớp stack cung cấp c|c phương thức để làm việc theo nguyên tắc Lifo học phần cấu trúc liệu và giải thuật Khai báo đối tượng thuộc lớp stack: stack<int> s; Tên phương thức stack<?>c; stack<?,deque, allocator> Mức truy cập public empty() public size() public top() public push() public pop() public Chức Hàm tạo Đối với hàm tạo thứ hai, ta cần sử dụng c|c đối tượng deque v{ allocator Đ}y l{ c|c lớp template Phương thức Trả kiểu bool Nhận giá trị true stack rỗng v{ ngược lại stack không rỗng thì nhận giá trị false Phương thức Trả kiểu số nguyên l{ kích thước stack (tức số phần tử stack) Có hai phương thức top quá tải: và không Nó trả phần tử nằm đỉnh stack (tức phần tử đưa v{o sau cùng) Bổ sung phần tử vào stack Lấy phần tử stack Phương thức này không trả giá trị phần tử vừa lấy T r a n g | 196 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Cần lưu ý rằng, lớp stack nằm thư viện stack Sau đ}y l{ số hàm thành viên lớp stack (198) Chương 17 C|c lớp thư viện Stack trường hợp này không bị giới hạn kích thước (bởi nó khai b|o động) Lớp hàng đợi queue Queue là lớp template Để sử dụng queue, ta cần khai b|o thư viện queue tương ứng Queue làm việc theo nguyên tắc Fifo – tức v{o trước thì trước Sau đ}y l{ ví dụ việc tạo đối tượng queue: queue<int> q; Tên phương thức Mức truy cập Chức queue<?> public Hàm tạo Đối với hàm tạo thứ queue<?, deque, allocator> hai, ta cần sử dụng c|c đối tượng deque v{ allocator Đ}y l{ c|c lớp template empty() public Phương thức Trả kiểu bool Nhận giá trị true queue rỗng v{ ngược lại queue không rỗng thì nhận giá trị false size() public Phương thức Trả kiểu số nguyên l{ kích thước queue (tức số phần tử queue) front() public Có hai phương thức front quá tải: và không Nó trả phần tử nằm đầu (tức phần tử đưa vào đầu tiên) back() public Có hai phương thức front quá tải: và không Nó trả phần tử nằm đầu vào (tức phần tử đưa v{o sau cùng) push() public Bổ sung phần tử vào queue Phần tử đưa v{o theo hướng back pop() public Lấy phần tử queue Phương thức này không trả giá trị phần T r a n g | 197 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ C|c phương thức thành viên lớp queue: (199) Chương 17 C|c lớp thư viện tử vừa lấy Phần tử lấy theo hướng front Cũng tương tự stack, queue trường hợp n{y không bị giới hạn kích thước (vì kha b|o động) Lớp vector Cần lưu ý rằng, lớp vector có cấu trúc tương đối giống với mảng Vector không phải là lớp đối tượng hình học Các phần tử vector xếp liên tục nhớ Chúng ta không thể rời rạc hóa các phần tử (khi bổ sung các phần tử, cần bổ sung cách liên tục; không bị lỗi cấp phát nhớ) Chúng ta có thể truy cập đến các phần tử vector thông qua số iterator using namespace std; Kết Theo chi so: 10 20 22 Theo tro iterator: 10 20 22 int main() { vector<int> v; v.push_back(10); v.push_back(20); v.push_back(22); //=====Cách 1===== cout<<"Theo chi so:"<<endl; for (unsigned i=0; i<v.size(); i++) cout<<v[i]<<endl; //=====Cách 2===== cout<<"Theo tro iterator:"<<endl; vector<int>::iterator it; for (it=v.begin(); it<v.end();++it) cout<<*it<<endl; return 0; } Theo cách 1, chúng ta truy cập đến các phần tử vector theo cách truy cập mảng; cách thứ hai cho phép ta truy cập thông qua trỏ iterator Sau đ}y, chúng ta tham khảo c|c phương thức thành viên lớp vector T r a n g | 198 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương trình #include <iostream> #include <vector> (200) Tên phương thức vector<?> vector<?, allocator> ~vector() operator = begin() Mức truy cập Chức public Hàm tạo end() public rbegin() public rend() public size() max_size() public public resize(int) resize(int, ?) public capacity() public empty() public reserve(int) public operator[int] public at(int) public public public public Hàm hủy Toán tử gán Trả iterator tương ứng với iterator đầu tiên vector Trả iterator tương ứng với iterator cuối vector Trả iterator nghịch đảo iterator đầu tiên vector Trả iterator nghịch đảo iterator cuối vector Trả kích thước vector Trả kích thước cực đại vector Thay đổi kích thước vector Nó có hai chồng chất hàm, tham số int thứ tương ứng với kích thước vector; tham số template thứ hai tương ứng với giá trị bổ sung mặc định Trả kích thước nhớ đ~ cấp phát cho các phần tử vector Cần lưu ý, chế cấp phát này C++ là tự động và luôn đảm bảo size()<=capacity() Trả giá trị true vector rỗng v{ ngược lại Thay đổi kích thước cho vùng nhớ lưu trữ các phần tử đ~ khởi tạo vector Tham số h{m tương ứng với giá trị trả phương thức capacity Trả phần tử tương ứng với vị trí định (như mảng) Tham chiếu đến phần tử tương ứng với số định Phương thức n{y tương tự T r a n g | 199 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương 17 C|c lớp thư viện (201) Chương 17 C|c lớp thư viện front() public back() public assign(iterator, iterator) assign(int, ?) push_back(?) public pop_back() public public iterator(iterator, ?) public insert(iterator, ?) insert(iterator, int , ?) insert(iterator, iterator, iterator) erase(iterator) erase(iterator, iterator) public swap(vector&) clear() public public get_allocator() public toán tử [] trên Trả phần tử đầu tiên (không phải l{ iterator begin) Trả phần tử cuối cùng (không phải l{ iterator end) Khởi gán giá trị cho vector Bổ sung phần tử vào cuối vector Loại bỏ phần tử phía cuối vector Phương thức thứ trả iterator, ba phương thức còn lại trả void Vector nới rộng cách bổ sung thêm các phần tử Phương thức đầu tiên trả iterator cuối cùng vector sau nới rộng kích thước nó Xóa bỏ các phần tử vector Phương thức đầu xóa phần tử, phương thức hai – xóa các phần tử vùng hai tham số định Sau xóa bỏ, nó trả iterator phần tử cuối Ho|n đổi giá trị hai vector Xóa bỏ hoàn toàn các phần tử vector Sau xóa bỏ, kích thước nó là Trả số lượng c|c đối tượng cấp phát nhớ sử dụng khở tạo vector Lớp string Tên phương thức string() string(const string&) Mức truy cập Chức public C|c phương thức khởi tạo: - Khởi tạo không tham số T r a n g | 200 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Thư viện chuẩn string C++ cung cấp lớp template tương ứng với string và wstring C|c phương thức thành viên lớp string bao gồm: (202) string(const string&, int, int) string(const char*, int) string(const char*) string(int, char) Các iterator: begin, end, rbegin, rend size, max_size, resize, capacity, reserve, clear, empty operator[int] at length() operator+= append(const string&) append(const string&, int, int) append(const char*, int) append(const char*) append(int, char) push_back(char) assign, erase, swap insert(int, const string&) insert(int, const string&, int, int) insert(int, const char*) insert(int, int, char) insert(iterator, char) insert(iterator, int, char) insert(iterator, iterator, iterator) public - Sao chép hàm tạo - Sao chép xâu từ vị trí int thứ với độ dài int thứ hai - Sao chép các kí tự mảng xâu kí tự với độ dài là tham số int (kể từ vị trí đầu tiên) - Tạo xâu kí tự có nội dung là kí tự char v{ độ dài là tham số int Xem phần lớp vector public Xem phần lớp vector public Xem phần lớp vector public public public Trả độ dài xâu Cộng dồn xâu Bổ sung xâu phần x}u v{o x}u cũ Các tham số n{y tương tự phương thức khởi tạo Bổ sung kí tự vào xâu Xem lớp vector Chèn xâu vào xâu Các phương thức sử dụng tham số iterator hoàn toàn tương tự trường hợp lớp vector Phương thức chèn xâu vào xâu ban đầu vị trí int Phương thức hai tương tự, x}u lấy từ tham số int thứ với độ dài là tham số int thứ Phương thức tương tự phương thức Phương thức chèn kí tự char vào vị trí tham số int thứ với số lần là tham số public public public T r a n g | 201 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Chương 17 C|c lớp thư viện (203) Chương 17 C|c lớp thư viện replace public c_str public data get_allocator copy(char*, int, int=0) find(const string&, int=0) find(const char*, int, int) find(const char*, int=0) find(char, int=0) find_fist_of find_last_of find_fist_not_of find_last_not_of substr(int, int) public public public public compare public public public int thứ Thay phần xâu theo các tham số tương tự phương thức khởi tạo Chuyển sang xâu dạng C tương ứng Trả mảng các kí tự Xem lớp vector Sao chép xâu Tìm kiếm xâu xâu Tìm kiếm xâu đầu tiên, cuối cùng, không phải là đầu tiên, không phải là cuối cùng Trả xâu từ vị trí int thứ với độ dài int thứ hai So sánh hai xâu Nhận giá trị 0, hai xâu v{ ngược lại Các hàm toàn cục Tên phương thức operator+ Chức Cộng hai xâu swap(string&, string&) operator== operator!= operator< operator> operator>= operator<= getline operator<< Ho|n đổi nội dung hai xâu Các toán tử so sánh hai xâu Xâu s>ss các kí tự s đứng trước ss bảng chữ cái operator>> Trích tách xâu từ luồng stream C++ Nhập xâu Chèn xâu và chuyển hóa thành luồng stream T r a n g | 202 CuuDuongThanCong.com https://fb.com/tailieudientucntt (204) Chương 17 C|c lớp thư viện Lớp list Tên phương thức list<?>(allocator) list<?>(int, ?, allocator) list<?>(list) ~list() begin, end, rbegin, rend empty, size, max_size, resize front, back, push_front, push_back, pop_front, pop_back, insert, erase, swap, clear splice(iterator, list) Mức truy cập Chức public Hàm tạo remove(const T&) public remove_if public unique merge sort reverse public public public public get_allocator public public public public public Hàm hủy Xem lớp vector Xem lớp vector Xem lớp vector public Chuyển các phần tử từ danh sách list sang danh sách chứa từ vị trí định Xóa các phần tử có giá trị định Xóa phần tử và dồn danh sách lại Xóa các phần tử trùng lặp Nhập hai danh sách lại Sắp xếp danh sách Đảo ngược thứ tự các phần tử danh sách Xem lớp vector Lớp map Tên phương thức map<?,?> Mức truy cập Chức public Hàm tạo ~map() begin, end, rbegin, rend empty, size, max_size, resize insert, erase, swap, clear operator[] public public public public public Hàm hủy Xem lớp vector Xem lớp vector Xem lớp vector Xem lớp vector T r a n g | 203 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Map là cấu trúc liệu gồm có hai phần là khóa và giá trị Mỗi khóa là v{ nó tương ứng với giá trị cụ thể Lớp map là lớp template (205) Chương 17 C|c lớp thư viện key_comp value_comp public public find public count lower_bound upper_bound equal_range get_allocator public Trả key đối tượng Trả giá trị đối tượng Trả số dạng iterator khóa tìm kiếm Đếm số đối tượng có khóa định Trả iterator biên Trả iterator biên trên Trả cặp cận trên và cận Xem lớp vector Lớp set Set là dạng cấu trúc liệu mà phần tử nó là khóa (nghĩa là không cho phép chứa các phần tử trùng lặp) C|c phương thức set ho{n to{n tương tự map Set l{ lớp template Các lớp thư viện nhập xuất Thư viện các luồng nhập xuất Các luồng quản lý v{ lưu trữ tài nguyên vật lý dạng kí tự tập tin, bàn phím, console, các kí tự này có thể đọc từ ghi lên luồng T r a n g | 204 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Thư viện iostream l{ thư viện hướng đối tượng cung cấp các chức nhập xuất sử dụng luồng stream Một luồng là đối tượng trừu tượng có thể làm việc với các thiết bị xuất nhập Một luồng có thể biểu diễn dãy các kí tự có độ dài vô hạn (206) Chương 17 C|c lớp thư viện Ví dụ các tập tin C++ tổ chức v{ tương t|c với tập tin Một luồng tập tin sử dụng để mở tập tin, đọc, ghi trên tập tin Để làm việc với luồng, C++ cung cấp thư viện iostream chuẩn chứa c|c đối tượng sau: Các lớp template Cơ sở thư viện iostream là cấu trúc cây phả hệ các lớp template Các lớp template cung cấp hầu hết các tính thư viện C++ để có thể làm việc với các kiểu liệu đại bổ sung sau này Nó là tập hợp các lớp template, lớp có hai tham số template: kiểu char (charT) để x|c định kiểu các phần tử và tham số traits để cung cấp các đặc tính bổ sung cho loại phần tử Lớp template cây phả hệ lớp có cùng tên với đối tượng thể lớp char và có tiền tố là basic_ Ví dụ lớp template istream là basic_istream, fstream l{ basic_fstream,… Chỉ có ngoại lệ đó là ios_base là lớp độc lập và không tồn lớp tương ứng là base Thực thể lớp template Thư viện chia thành hai tập hợp thực thể lớp iostream: là theo hướng hẹp để quản lý các phần tử kiểu char và phần tử thuộc kiểu khác; theo hướng rộng để tổ chức các phần tử kiểu wchar_t Thực thể theo hướng hẹp char có thể biết đến nhiều l{ thực thể thư viện iostream Các lớp ios, iostream v{ ofstream l{ c|c thực thể theo hướng rộng Biểu đồ trên là biểu đồ biểu diễn tên và mối quan hệ các lớp theo hướng hẹp Các thực thể theo hướng rộng wchar_t có cùng tên với thực thể theo hướng hẹp có tiền tố bổ sung là w Ví dụ wios và ios, wistream và wofstream Là phần thư viện iostream, đặc tả tệp tiêu đề <iostream> thường sử dụng để thực các chức nhập xuất chuẩn Chúng chia th{nh hai nhóm: nhóm đối tượng định hướng hẹp bao gồm ba đối tượng phổ biến nhất: cin, cout, cer v{ clog v{ nhóm định hướng rộng bao gồm wcin, wcout, werr và wlog T r a n g | 205 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Các đối tượng chuẩn (207) Chương 17 C|c lớp thư viện Các kiểu liệu Các lớp iostream sử dụng các kiểu Chúng sử dụng kiểu liệu phụ thuộc vào thể chúng Ví dụ theo mặc định char và wchar_t, kiểu streampos, streamoff v{ streamsize dùng để biểu diễn vị trí, offset v{ kích thước tương ứng Các thao tác thực Các thao tác thực thi trên luồng là các hàm toàn cục thiết kế để sử dụng cùng với toán tử chèn << và toán tử trích tách >> Ngoài còn có các chức định dạng endl, hex, scientific Các tập tin <ios>, <istream>, <ostream>, <streambuf> v{ <iosfwd> không thường sử dụng cách trực tiếp c|c chương trình C++ Chúng mô tả lớp sở cây phả hệ v{ đưa v{o chương trình thông qua tập tin tiêu đề khác thư viện chứa các lớp dẫn xuất <iostream> đặc tả c|c đối tượng sử dụng để kết nối thông qua các chuẩn nhập xuất (bao gồm cin, cout) <fstream> x|c định các lớp luồng (như c|c lớp template basic_ifstream hay lớp ofstream) c|c đối tượng đệm (basic_filebuf) Những lớp n{y sử dụng để làm việc với tập tin cách sử dụng luồng <sstream> các lớp x|c định tập tin n{y thường sử dụng đối tượng string chúng là luồng <iomanip> đặc tả vài chức chuẩn với các tham số sử dụng kết hợp với các toán tử t|ch v{ chèn để chỉnh sửa các cờ hiệu và các thông số định dạng Các thành phần thư viện iostream (với thực thể char) 1) Các lớp iostream (có thể tham khảo thêm thư viện này) Lớp ios_base Lớp ios Lớp istream Lớp ostream Lớp ifstream C++ T r a n g | 206 CuuDuongThanCong.com https://fb.com/tailieudientucntt (208) Chương 17 C|c lớp thư viện Lớp ofstream Lớp fstream Lớp istringstream Lớp ostringstream Lớp streambuf Lớp filebuf Lớp stringbuf 2) Các đối tượng Đối tượng cin Xem chương “Nhập xuất bản” C|c phương thức và thuộc tính khác xem thêm lớp istream Đối tượng cout Xem chương “Nhập xuất bản” C|c phương thức và thuộc tính lớp khác xem thêm lớp ostream Đối tượng cerr Thuộc lớp ostream sử dụng để in luồng lỗi Mặc định, hầu hết hệ thống có chuẩn lỗi để in màn hình Bởi vì cerr là đối tượng lớp ostream, chúng ta có thể viết các kí tự theo liệu định dạng nhờ vào toán tử chèn << không định dạng liệu sử dụng hàm thành viên write int main() { int num; std::cin>>num; if (std::cin.good()) { std::cout<<"Good !"; }else { std::cerr<<"Error"; //Hoặc std::cerr.write("Error", sizeof(std::streamsize)); T r a n g | 207 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ #include <iostream> (209) Chương 17 C|c lớp thư viện } return 0; } Nếu số nguyên nhập v{o đúng định dạng thì thực chức in “Good”, ngược lại in “Error” Đối tượng clog Tương tự cerr, clog dùng để biểu diễn luồng đăng nhập chuẩn 3) Các kiểu liệu Kiểu fpos: x|c định vị trí luồng Về chất nó là lớp template Nó có hai th{nh viên getter v{ setter tương ứng là: state() và state(stateT) Kiểu streamoff: x|c định vị trí offset luồng Ta hoàn toàn có thể chuyển đổi nó thành streamsize, fpos streampos Kiểu streampos: tương tự với fpos Kiểu streamsize: biểu diễn kích thước luồng 4) Các thao tác thực boolalpha: thường sử dụng kết hợp với toán tử nhập >> xuất << Nếu tham số là boolalpha thì tham số bool l{ v{ tương ứng hiểu là true và false #include <iostream> int main() { bool num; std::cin>>std::boolalpha>>num; std::cout<<num; return 0; } true true #include <iostream> int main() true true T r a n g | 208 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ dec: đọc ghi liệu số theo dạng thập phân (mặc định) (210) Chương 17 C|c lớp thư viện { int num = 10; std::cout<<std::dec<<num; return 0; } endl: chèn dấu xuống dòng Tương tự “\n” ends: chèn kí tự trống “\0” fixed: áp dụng cho định dạng số thực có dấu chấm Khi cout.precision(int) triệu gọi, thì số chữ số thập phân (tham số int) áp dụng chuẩn xuất liệu có sử dụng fixed (khác với none - áp dụng theo mặc định và không phụ thuộc vào việc triệu gọi phương thức trên) #include <iostream> 3.14 //Không bật fixed int main() { float a = 3.141516f, b = 1.00f; 3.142 1.000 std::cout.precision(3); //Bật fixed std::cout<<a<<"\t"<<b<<std::endl; std::cout<<std::fixed<<a<<"\t"<<b; return 0; } flush: đồng hóa đệm với trỏ luồng Điều n{y có nghĩa l{ tất các kí tự chưa ghi đệm ghi liên tục #include <fstream> using namespace std; int main () { //Dữ liệu ghi liên tục lên tập tin test.txt Dữ liệu file này cập nhập liên tục 100 lần ofstream outfile ("test.txt"); for (int n=0; n<100; n++) outfile << n << flush; } C++ outfile.close(); return 0; T r a n g | 209 CuuDuongThanCong.com https://fb.com/tailieudientucntt (211) Chương 17 C|c lớp thư viện hex: thiết lập cờ hiệu chuỗi thập lục phân cho số in //Kết - F internal: kéo giãn vùng liệu hai phía (tương tự justify Microsoft Word) theo độ rộng thiết lập cout.width(int) left: tương tự internal, liệu kéo bên trái noboolalpha: ngược lại với boolalpha, in giá trị là và 0, dù cho giá trị đúng có thiết lập là true, và giá trị sai thiết lập là false noshowbase: không hiển thị dạng thức số Ví dụ số có dạng 0x… Nếu cờ hiệu n{y triệu gọi, dòng liệu in không chứa 0x noshowpoint: không hiển thị dấu chấm động noshowpos: không hiển thị dấu số dương noskipws: không bỏ qua các kí tự trắng tách liệu (khi kết hợp sử dụng lớp istringstream) nounitbuf: đệm không bị làm cạn sau lần thực thao tác ghi liệu nouppercase: không hiển thị phần chữ các giá trị hệ số 16 dạng chữ hoa (nghĩa l{ luôn viết thường a, b, c, d, e, f) oct: thiết lập cờ hiệu cho chuỗi bát phân in (chuyển số thành chuỗi bát phân) resetiosflags : hủy bỏ cờ hiệu tương ứng với hệ số chọn right: tương tự internal, liệu kéo bên phải scientific: tương tự fixed, nó hiển thị số thập phân dấu chấm động dạng E setbase: thiết lập hệ số chuyển đổi Nó tương đương với hex – số thiết lập là 16; oct – 8; dec – 10 setfill: bổ sung các kí tự định vào phần còn trống vùng liệu định setiosflags: thiết lập cờ hiệu với hệ số chọn setprecision: thiết lập số ô trống dành cho phần nguyên lẫn phần thập phân (không tính dấu chấm) Cũng hoạt động tương ứng với fixed setw: thiết lập độ rộng cho vùng liệu hiển thị showbase: hiển thị dạng thức số Cờ hiệu n{y ngược với noshowbase T r a n g | 210 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ cout<<hex<<15; (212) Chương 17 C|c lớp thư viện showpoint: hiển thị dấu chấm động showpos: hiển thị dấu + trước số dương skipws: bỏ qua kí tự trắng tách liệu unitbuf: đệm bị làm cạn sau lần ghi liệu uppercase: hiển thị phần chữ hệ số 16 dạng chữ hoa (A, B, C, D, E, F) ws: bỏ qua các kí tự trắng (hoặc các kí tự tab, enter) tách liệu C++ T r a n g | 211 CuuDuongThanCong.com https://fb.com/tailieudientucntt (213) Phần Thực hành HƯỚNG DẪN THỰC HÀNH BÀI THỰC HÀNH SỐ Nhập xuất và Hàm C++ Hãy thực các công việc sau đ}y: a Xây dựng chương trình gồm có hai phần: phần file header đặt tên là tieude.h, phần chương trình chính l{ main.cpp b Tất c|c h{m chương trình cần phải khai báo prototype Các tiêu đề file đặt tệp tiêu đề Chương trình chính chứa các hàm thực các công việc sau và hàm main - Sử dụng hàm nhập xuất để xây dựng hàm nhập vào xâu kí tự dạng kiểu liệu string Sau đó: + In x}u đảo ngược Ví dụ “Toi di hoc”, thì in “coh id ioT” Hàm này gọi là hàm DaoXau + In các kí tự đảo ngược Ví dụ “Toi di hoc”, thì in “hoc di Toi” Hàm này gọi là hàm DaoTu - Nhập vào mảng số nguyên, in tổng các phần tử chúng Hàm này gọi là hàm TinhTong - Sử dụng khai báo chồng chất h{m, để xây dựng hai hàm giải phương trình bậc v{ phương trình bậc hai Tên gọi hai hàm này là GiaiPhuongTrinh - Sử dụng khai báo hàm với tham số mặc định để giải phương trình bậc v{ phương trình bậc hai, hàm này có tên gọi là GiaiPhuongTrinhTSMD - Trong hàm main, hãy thực các công việc sau: in thông báo nhập vào xâu kí tự và gọi hàm DaoXau và DaoTu, nhập vào mảng số nguyên và gọi hàm TinhTong Sử dụng hàm xuất nhập để đưa thông báo nhập các hệ số cho phương trình Nếu hệ số c nhập vào 0, thì thực giải phương trình bậc nhất, còn c khác không thì thực giải Trang | 212 CuuDuongThanCong.com https://fb.com/tailieudientucntt (214) Phần Thực hành phương trình bậc hai (giải c|c phương trình n{y cách gọi các hàm xây dựng trên) BÀI THỰC HÀNH SỐ Xây dựng Lớp và Làm việc với Đối Tượng Xây dựng lớp HangHoa gồm có c|c phương thức và thuộc tính sau: Tên gọi Mức truy cập Loại Giải thích tenHang private Tên mặt hàng ngaySanXuat private Ngày sản xuất Thuộc tính donGia private Đơn gi| soLuong private Số lượng SetTenHang public Thiết lập tên hàng GetTenHang public Tiếp nhận tên hàng SetNgaySanXuat public Thiết lập ngày sản xuất GetNgaySanXuat public Tiếp nhận ngày sản xuất Phương SetDonGia public Thiết lập đơn gi| thức GetDonGia public Tiếp nhận đơn gi| SetSoLuong public Thiết lập số lượng GetSoLuong public Tiếp nhận số lượng TinhTien public Tính tiền C|c phương thức trên bao gồm setter v{ getter Phương thức TinhTien là phương thức dùng để tính số tiền mà khách hàng mua Tính tiền donGia*soLuong Tên gọi Mức truy cập Loại Giải thích username private Thuộc Tên tài khoản người dùng tính password private Mật người dùng SetUsername public Lập tài khoản người dùng GetUsername public Tiếp nhận tài khoản người dùng Phương SetPassword public Lập mật người dùng thức GetPassword public Tiếp nhận mật người dùng MuaHang public Mua hàng C|c phương thức setter và getter ho{n to{n tương tự trên Phương thức MuaHang triệu gọi c|c phương thức thiết lập tên hàng, ngày sản xuất, và tính tiền đối tượng HangHoa trên Phương thức mua hàng T r a n g | 213 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Xây dựng lớp KhachHang gồm c|c phương thức và thuộc tính sau: (215) Phần Thực hành này, chấp nhận username và password khách hàng nhập vào trùng với username và password hệ thống Trong chương trình chính, h~y tạo hai đối tượng hai lớp HangHoa và KhachHang Username và Password khách hàng nhập vào từ bàn phím Kiểm tra username và password trùng với username và password thiết lập sẵn thì đó tiến hành khởi tạo đối tượng KhachHang, ngược lại, hủy bỏ giao dịch v{ in thông b|o: “Xin loi, tai khoan cua quy khach khong ton tai he thong Xin lien he dang ki voi chi nhanh khach hang gan nhat” Trong trường hợp đăng nhập thành công, hãy thực h{nh động mua hàng đối tượng khách hàng này (thao t|c mua nhập vào từ bàn phím) BÀI THỰC HÀNH SỐ Hàm tạo, chép hàm tạo, hàm bạn, trỏ this Lưu ý: bài thực hành chương này, cho phép sử dụng trỏ đối tượng để thực Mọi phương án sử dụng khai báo đối tượng thông thường không chấp nhận ! Quay trở lại với bài thực hành số Hãy tạo chương trình cách thay c|c phương thức setter các hàm tạo tương ứng Hãy sử dụng trỏ this trường hợp này Bổ sung phương thức ResetHangHoa cho lớp HangHoa để đưa các tham số mặc định cho các thuộc tính (xâu kí tự thì thiết lập “”, số nguyên/thực thì thiết lập 0) Bổ sung phương thức HuyBo cho đối tượng KhachHang để hủy bỏ việc mua h{ng Trong chương trình chính, hãy tạo trỏ đối tượng để thực h{nh động mua hàng và h{nh động hủy bỏ Khuyến khích: Nên sử dụng hàm bạn và lớp bạn T r a n g | 214 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Xây dựng lớp điểm Point và lớp hình tròn Round Sử dụng hàm random để tạo tọa độ ngẫu nhiên cho đối tượng điểm hàm tạo đối tượng điểm Cho trước tọa độ tâm đường tròn, bán kính nó Hãy đưa c|c kết luận điểm khởi tạo nằm hay ngo{i đường tròn Đ}y l{ phương thức thành viên lớp hình tròn (216) Phần Thực hành BÀI THỰC HÀNH SỐ Chồng chất Toán tử C++ Xây dựng lớp số phức và lớp phân số Sử dụng chồng chất toán tử để thực các thao tác tính toán sau: - Lớp số phức: phương thức khởi tạo (hoặc phương thức setter), phương thức chép hàm tạo, phương thức + (cộng hai số phức), - (trừ hai số phức), * (nhân hai số phức), / (chia hai số phức) v{ phương thức tính modul và argument số phức Xây dựng hàm chồng chất toán tử nhập/xuất liệu với lớp số phức này - Lớp phân số: phương thức khởi tạo, phương thức chép hàm tạo, phương thức + (cộng hai phân số), phương thức – (trừ hai phân số), phương thức * (nhân hai phân số), phương thức / (chia hai phân số) Xây dựng hàm chồng chất toán tử nhập/xuất liệu với lớp phân số này BÀI THỰC HÀNH SỐ Kĩ thuật thừa kế C++ Xây dựng lớp HinhKhoi, chứa thuộc tính chiều cao (chieucao) Xây dựng hàm tạo tương ứng v{ phương thức chép hàm tạo Xây dựng lớp HinhKhoi1 thừa kế từ lớp HinhKhoi Bổ sung thêm thuộc tính chiều dài (chieudai) Bổ sung hàm tạo v{ phương thức chép hàm tạo Xây dựng tiếp hàm tính thể tích cho HinhKhoi1 (bằng chieucao*chieudai2) Xây dựng lớp lớp HinhKhoi2 thừa kế từ lớp HinhKhoi1 Bổ sung thêm thuộc tính chiều rộng (chieurong) Bổ sung hàm tạo v{ phương thức chép hàm tạo Quá tải hàm tính thể tích cho HinhKhoi2 (bằng chieucao*chieudai*chieurong) C++ Xây dựng lớp HinhKhoi3 thừa kế từ lớp HinhKhoi Bổ sung thêm thuộc tính bán kính (bankinh) Bổ sung hàm tạo v{ phương thức chép hàm tạo Xây dựng hàm tính thể tích cho HinhKhoi3 (bằng chieucao*Pi*bankinh2) T r a n g | 215 CuuDuongThanCong.com https://fb.com/tailieudientucntt (217) Phần Thực hành BÀI THỰC HÀNH SỐ Lớp sở trừu tượng C++ Xây dựng lớp sở trừu tượng Vector chứa ba phương thức ảo túy: TinhDoDai (tính độ dài), SinGoc (tính sin góc hai Vector), TrucGiao (tìm vector trực giao – tức vector vuông góc với vector trên), hai thuộc tính thành viên là tọa độ x v{ y, hai phương thức setter (không sử dụng hàm tạo trường hợp này, vì lớp trừu tượng không có khả tạo thể hiện, và hàm tạo không thừa kế); sử dụng phương thức tham chiếu getter Xây dựng lớp Vector2D thừa kế từ lớp sở trừu tượng Vector để thực thi c|c phương thức ảo túy nêu trên Xây dựng lớp Vector3D thừa kế từ lớp sở trừu tượng Vector (bổ sung thêm tọa độ z v{ c|c phương thức getter, setter tương ứng) để thực thi các phương thức ảo túy nêu trên Trong đó, Vector2D l{ vector chiều (chỉ có hai tọa độ x và y); Vector3D là vector chiều (có ba tọa độ x, y và z) THANG ĐIỂM ĐÁNH GIÁ KĨ NĂNG Bài thực hành số Điểm 20 15 20 15 Ngưỡng đạt 60-70 Cộng điểm 70-80 80-90 Quy đổi +1 +1.5 20 10 90-100 +2 - Hạn nộp bài thực h{nh tương ứng với buổi thực hành Ví dụ: Bài thực hành số phải nộp đúng hạn vào buổi thứ (mới đạt điểm tối đa) Nếu muộn buổi, trừ điểm Nếu nộp tất các bài vào buổi cuối cùng, thì tối đa đạt Ngưỡng Đạt T r a n g | 216 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Mỗi bài thực hành có thể thực nhà trên lớp, phải nộp b{i đúng thời hạn (218) Phần Thực hành - Điểm tổng kết môn học gồm có cột điểm qu| trình v{ điểm thi Điểm Quá trình = Điểm chuyên cần; Điểm Quá trình 2, Điểm Quá trình tương ứng với bài trắc nghiệm; Điểm Quá trình = Điểm Thực hành/10 Điểm thi cuối cùng kết thi đạt cộng thêm điểm Quy đổi Điểm tổng kết tính theo công thức: ) C++ ( T r a n g | 217 CuuDuongThanCong.com https://fb.com/tailieudientucntt (219) Phần Bài tập BÀI TẬP NÂNG CAO Bài tập Xây dựng c|c h{m để tính các tổng sau đ}y: ( ) ( ( ) ) Trong đó: n , x và k là các tham số nhập vào từ bàn phím Bài tập Khai báo chuỗi kí tự cách sử dụng trỏ Sau đó x}y dựng c|c h{m để thực các câu sau: - Đếm số kí tự có giá trị là a - Đảo xâu kí tự - Đảo từ - Đếm số từ - Nhóm các kí tự cùng loại Ví dụ: Xâu ban đầu: aaabbbaacbd Xâu in ra: 5a4b1c1d - Thuật toán nén liệu RLE (Run length Encoding) là thuật toán nén không liệu lossless Nó sử dụng để nén ảnh định dạng bmp Thuật toán RLE thực đếm số kí tự giống và liên tiếp, sau Trang | 218 CuuDuongThanCong.com https://fb.com/tailieudientucntt (220) Phần Bài tập đó, thay toàn dãy kí tự giống này số kí tự đếm sau đó l{ kí tự tương ứng Ví dụ: Xâu ban đầu: aaaabbbbcdddAA Xâu sau nén: 4a4b1c3d2A + Hãy xây dựng h{m RLE để nén liệu Với liệu nhập vào từ bàn phím + Hãy xây dựng h{m IRLE để giải nén liệu Với liệu nhập vào từ bàn phím Bài tập Xây dựng các hàm thực các chức sau đ}y trên mảng chiều hai cách: khai báo theo kiểu thông thường và khai báo trỏ - Hàm nhập liệu cho mảng chiều - Hàm xuất liệu cho mảng chiều - Hàm tính tổng các phần tử mảng - Hàm tính tổng các phần tử mảng là số nguyên tố - Hàm tính tổng các phần tử mảng là số chính phương - Hàm tính tổng các số nguyên tố mảng lớn 10 v{ nhỏ 100 - Hàm tính tổng các phần tử mảng là số chính phương chẵn - Hàm đếm số phần tử mảng là số nguyên tố - Hàm đếm số phần tử mảng là số chính phương - Hàm đếm số phần tử mảng là số nguyên tố lớn 10 v{ nhỏ 50 - Hàm đếm số phần tử mảng là số chính phương chẵn - Hàm tìm kiếm số phần tử có giá trị x mảng - Hàm tính giá trị trung bình mảng C++ - Hàm tìm số phần tử có giá trị nhỏ - Hàm tìm số phần tử có giá trị lớn T r a n g | 219 CuuDuongThanCong.com https://fb.com/tailieudientucntt (221) Phần Bài tập - Hàm xếp mảng theo thứ tự tăng dần (có thể sử dụng thuật toán xếp bất kì) - Hàm tính giá trị trung bình các phần tử mảng có giá trị chẵn - Hàm dồn tất các phần tử chẵn phía, các phần tử lẻ phía Ví dụ, mảng ban đầu là: thì kết là - H{m x|c định phần tử có giá trị gần với giá trị trung bình mảng - H{m đẩy các phần tử mảng lên n vị trí Ví dụ mảng ban đầu là Nếu n = 2, thì mảng thu là Bổ sung hàm main v{ c|c thư viện để nhận chương trình hoàn chỉnh Các hàm cần khai báo theo prototype Bài tập Xây dựng c|c h{m để thực các chức sau đ}y trên mảng hai chiều hai cách khai báo: theo kiểu thông thường và khai báo trỏ - Hàm nhập giá trị cho mảng hai chiều - Hàm xuất giá trị mảng hai chiều theo dạng ma trận - Hàm cộng hai ma trận - Hàm nhân hai ma trận - Hàm thay tất các phần tử có giá trị lẻ ma trận thành - Hàm thay tất các phần tử âm ma trận phần tử dương tương ứng - Hàm thay các phần tử có giá trị nhỏ gi| trị trung bình ma trận phần tử Các hàm yêu cầu xây dựng theo prototype Các ma trận (mảng hai chiều) các bài tập trên là ma trận vuông Bổ sung h{m main v{ c|c thư viện cần thiết để chương trình ho{n chỉnh Bài tập T r a n g | 220 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - H{m tính lũy thừa ma trận vuông (222) Phần Bài tập Hãy chọn lựa c|c phương ph|p phù hợp lập trình hướng đối tượng để lập trình giải c|c b{i to|n sau đ}y a) Xây dựng lớp TamGiac (tam giác) gồm có ba cạnh với c|c phương thức sau: - Các phương thức khởi tạo cho tam giác: không tham số, có tham số và chép hàm tạo - Các phương thức Getter - Phương thức KiemTra biết nó có phải là tam giác thực không - Phương thức tính diện tích tam giác nó là tam giác thực - Các phương thức nhập/xuất cho tam giác Xây dựng theo toán tử b) Xây dựng lớp Diem (điểm) gồm có tọa độ x, y, z v{ c|c phương thức sau: - C|c phương thức khởi tạo - C|c phương thức Getter - Phương thức tính khoảng cách hai điểm Xây dựng lớp Vector gồm có hai thuộc tính tương ứng với hai đối tượng Diem (điểm đầu v{ điểm mút) Hãy bổ sung c|c phương thức sau cho lớp Vector: - C|c phương thức khởi tạo - C|c phương thức Getter - Phương thức tính độ dài vector - Phương thức cộng hai vector Xây dựng theo toán tử - Phương thức tính cosin góc hai vector - Phương thức tính tích hữu hướng hai vector T r a n g | 221 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Phương thức tính tích vô hướng hai vector (223) Phần Bài tập Xây dựng lớp HinhCau (hình cầu) gồm hai thuộc tính l{ đối tượng Diem tương ứng với tâm hình cầu và bán kính R Hãy bổ sung c|c phương thức sau cho lớp HinhCau: - C|c phương thức khởi tạo - C|c phương thức Getter - Phương thức tính diện tích hình cầu - Phương thức x|c định vị trí tương đối hình cầu và điểm - Phương thức x|c định vị trí tương đối hai hình cầu Yêu cầu chung: hãy bổ sung các hàm toán tử nhập xuất cho lớp đối tượng trên Bài Xây dựng lớp ConNguoi gồm có hai thuộc tính thành viên là: tên và tuổi Các phương thức khởi tạo v{ c|c phương thức Getter tương ứng Lớp NhanVien thừa kế từ lớp ConNguoi, cách bổ sung thêm hai thuộc tính l{: m~ nh}n viên, lương v{ mức đóng góp (tính theo lương) Bổ sung phương thức khởi tạo v{ c|c phương thức Getter Các công ty quản lý nhân viên mình Đối tượng CongTy có các thuộc tính: tên công ty, mức đóng góp chuẩn, ngân sách có, nguồn thu theo tháng, nguồn chi theo tháng Giả sử nguồn chi này không bao gồm chi phí trả lương cho nh}n viên Đối tượng CongTy có phương thức tuyển dụng để tuyển thêm nh}n viên, v{ phương thức sa thải để sa thải nhân viên Một nhân viên bị sa thải, mức đóng góp họ nhỏ mức đóng góp chuẩn công ty Bài Hãy xây dựng chương trình ứng dụng theo mô tả sau T r a n g | 222 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Một công ty tuyên bố phá sản vốn điều lệ họ bị âm Vốn điều lệ là tổng ngân sách có cộng với mức đóng góp nhân viên trừ cho nguồn chi theo tháng và trừ tiếp cho tổng lương chi trả cho toàn nhân viên Hãy xây dựng chương trình để thực thi mô tả này (224) Phần Bài tập - Lớp đối tượng người dùng gồm các thuộc tính: username, password, câu hỏi bảo mật và câu trả lời Bổ sung c|c phương thức tương ứng cho phù hợp Khi chạy chương trình, người dùng nhập vào username và password Nếu trùng khớp với username và password đ~ tạo hệ thống thì thông b|o đăng nhập thành công - Sau đăng nhập th{nh công, người dùng có quyền triệu gọi c|c phương thức tính toán lớp số phức và phân số (cần xây dựng thêm hai lớp này) - Nếu người dùng đăng nhập không thành công, yêu cầu họ xác minh có phải họ đ~ quên mật hay không câu hỏi bảo mật Nếu trả lời đúng, thì cho phép họ thay đổi mật - Nếu đăng nhập không thành công và trả lời sai câu hỏi bảo mật, hãy in thông b|o “Bạn chưa phải là thành viên”, h~y chọn “y” để đăng kí v{ chọn “n” để thoát Bài Mỗi đối tượng Shape Microsoft Word có có các thuộc tính: màu viền, màu nền, nội dung văn bên trong, thứ bậc, tình trạng chọn hay không v{ c|c phương thức khởi tạo, thay đổi giá trị cho thuộc tính (phương thức setter) Hãy tạo mảng 10 phần tử Shape Các giá trị thứ bậc không trùng (và phân bố từ 0-9) Trong 10 đối tượng này, thời điểm, có đúng đối tượng tình trạng chọn Nếu đối tượng tình trạng chọn, thì ta có quyền thay đổi giá trị cho nó Hãy bổ sung thêm c|c phương thức cần thiết để thực các yêu cầu trên Bài Gợi ý: Các lớp đối tượng mô hình này bao gồm – Sinh viên, Giáo viên chủ nhiệm, Phòng Công tác học sinh sinh viên, Phòng Đ{o tạo, Phòng Tài chính, Khoa chuyên môn, Đoàn TN, Lớp, Phòng học, Môn học… Bài 10 T r a n g | 223 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ Hãy phân tích mô hình quản lý sinh viên trường đại học Từ mô hình ph}n tích được, hãy xây dựng chương trình quản lý sinh viên Trong mô hình này, yêu cầu quản lý không 10 lớp đối tượng (225) Phần Bài tập H~y ph}n tích theo hướng đối tượng mô hình quản lý Nh{ nước thu nhỏ cho bên đ}y Từ mô hình ph}n tích đó, h~y x}y dựng chương trình để quản lý Nh{ nước thu nhỏ Biết rằng, tương t|c c|c đối tượng có tính bắc cầu Bộ GD Sở GD Bộ Y tế Sở Y tế Bộ Công Thương Bộ Quốc Phòng Bộ Nông Nghiệp Kế thừa Cơ sở Tương t|c C++ Hình 23 – Mô hình tương t|c quản lý Nh{ nước T r a n g | 224 CuuDuongThanCong.com https://fb.com/tailieudientucntt (226) Phần Bài tập BÀI TẬP LỚN Dự án Phân tích, thiết kế và xây dựng chương trình quản lý thư viện Trong chương trình n{y, cần đảm bảo các chức sau đ}y: - Trong mô hình quản lý này cần đảm bảo các thông tin: mã sách, tên sách, tên tác giả, năm xuất bản, nhà xuất bản, số trang, giá và số lượng - Cho phép bổ sung thêm nhiều sách - Cập nhập lại số lượng s|ch có người mượn/trả sách - Tìm kiếm sách theo tên tác giả tên sách - Kiểm tra tình trạng sách còn hay không - Chương trình thực thi trên màn hình Console Có menu tùy chọn Dữ liệu lưu trữ vào tập tin data.dat dạng mã hóa nhị phân Dự án Phân tích, thiết kế và xây dựng chương trình quản lý website bán m|y tính Trong chương trình trình n{y, cần đảm bảo các chức sau đ}y: - Trong mô hình quản lý này, cần đảm bảo các thông tin: mã hàng, màu sắc, nước sản xuất, hãng, giá tiền, số lượng, thời gian bảo hành, có c{i đặt hệ điều hành hay không, địa khác hàng, số điện thoại khách hàng - Tìm kiếm mặt hàng theo tên hãng, giá tiền v{ nước sản xuất - Kiểm tra tình trạng còn hàng hay không - Bổ sung (nhập thêm hàng) xóa bỏ (bán hàng) Dự án Phân tích, thiết kế và xây dựng chương trình quản lý nhân viên công ty Trong chương trình trình n{y, cần đảm bảo các chức sau đ}y: T r a n g | 225 CuuDuongThanCong.com https://fb.com/tailieudientucntt C++ - Chương trình thực thi trên màn hình Console Có menu tùy chọn Dữ liệu lưu trữ vào tập tin data.dat dạng mã hóa nhị phân (227) Phần Bài tập - Trong mô hình quản lý này, cần đảm bảo các thông tin: mã nhân viên, họ tên nh}n viên, ng{y th|ng năm sinh, hệ số lương, năm bắt đầu công tác, tình trạng hôn nhân, phận làm việc - Tìm kiếm nhân viên theo họ tên - Thống kê số lượng nhân viên theo phận làm việc - Bổ sung (tuyển dụng) xóa bỏ (kết thúc hợp đồng) - Chương trình thực thi trên màn hình Console Có menu tùy chọn Dữ liệu lưu trữ vào tập tin data.dat dạng mã hóa nhị phân Dự án Phân tích, thiết kế và xây dựng game FarmVille (một game tiếng trên Facebook) Trong game này, cần đảm bảo các chức sau đ}y: - Trong mô hình quản lý này, cần đảm bảo các thông tin: người chơi – email, tên người chơi, tổng số tiền C|c đối tượng game: tên đối tượng, trị giá, thời gian khởi tạo, thời gian thu hoạch - Tìm kiếm người chơi theo họ tên - Thống kê số tiền thu người chơi - Bổ sung người chơi đối tượng game - Khi thu hoạch đối tượng, thì trị giá đối tượng cập nhập vào cho tổng tiền người chơi, đồng thời đối tượng bị hủy Đối tượng có thể thu hoạch nếu: thời gian – thời gian khởi tạo >= thời gian thu hoạch - Chương trình có menu điều khiển, không yêu cầu tạo giao diện đồ họa C++ Ghi chú: Các sinh viên nộp đủ bài thực h{nh v{o trước buổi thứ có hội nhận bài tập lớn Khi nhận bài tập lớn, ngoài yêu cầu bổ sung giảng viên, sinh viên cần thực thi thên các yêu cầu sau: - Phân tích mô hình lên giấy (nộp in) T r a n g | 226 CuuDuongThanCong.com https://fb.com/tailieudientucntt (228) Phần Bài tập C++ - Nộp chương trình ho{n chỉnh Trong đó, chương trình hoàn chỉnh và tập tin word (*.doc; *.docx) phải ghi lên đĩa CD, bên ngo{i đĩa có ghi: tên sinh viên, lớp v{ “B{i tập lớn: Lập trình hướng đối tượng C++ Giảng viên hướng dẫn: ….” T r a n g | 227 CuuDuongThanCong.com https://fb.com/tailieudientucntt (229) DANH SÁCH HÌNH Hình – Tạo dự án CodeBlocks 10 Hình – Khởi tạo th}n phương thức 11 Hình – Cấu hình MinGW Eclipse Helios 12 Hình – Chọn đường dẫn đến thư mục bin MinGW 13 Hình - Tạo dự án 13 Hình - Cấu trúc thư mục dự án 14 Hình - Biên dịch dự án 14 Hình - Hộp thoại tạo class 15 Hình - Giao diện tổng thể Visual Studio 2010 17 Hình 10 - Tạo dự án Win32 Console 18 Hình 11 - Win32 Application Wizard 18 Hình 12 - Bổ sung thêm tập tin 20 Hình 13 - Bổ sung thêm lớp đối tượng 20 Hình 14 - Tạo lớp Class Wizard 21 Hình 15 - Xem biểu đồ lớp 22 Hình 16 – Sơ đồ minh họa việc sử dụng hàm 72 Hình 17 – Tham chiếu trỏ 94 Hình 18 – Tham chiếu ngược trỏ 95 Hình 19 – Tăng/Giảm địa trỏ 101 Hình 20 – Minh họa sơ đồ lớp 125 Hình 21 – Tính kế thừa 153 Hình 22 – Lớp sở ảo 164 Hình 23 – Mô hình tương t|c quản lý Nh{ nước 224 Trang | 228 CuuDuongThanCong.com https://fb.com/tailieudientucntt (230) TRA CỨU TỪ KHÓA MỘT SỐ THUẬT NGỮ ANH-VIỆT ĐƯỢC SỬ DỤNG TRONG GIÁO TRÌNH Dịch sang tiếng Việt Lớp sở trừu tượng Tính trừu tượng Toán tử số học Toán tử gán Lớp sở/ Lớp cha Toán tử dịch bit Lớp Lớp Toán tử phân tách Toán tử gán hợp Toán tử điều kiện Tính đóng gói Ngoại lệ Toán tử chuyển đổi kiểu liệu Toán tử tăng giảm Che dấu/ẩn dấu thông tin Tính thừa kế/ Tính kế thừa Sự thể Toán tử logic Tính đa thừa kết/Tính đa kế thừa Đối tượng Toán tử Chồng chất toán tử Chồng chất Quá tải Tính đa hình Nguyên mẫu Hàm ảo túy Tham chiếu Toán tử quan hệ và so sánh C++ Nguyên tiếng Anh Abstract base class Abstraction Arithmetic operators Assignment operators Base class Bitwise operators Child class/SubClass Class Comma operators Compound assignment operator Conditional operators Encapsulation Exception Explicit type casting operators Increase and decrease operators Information hiding Inheritance Instance Logical operators Multiple inheritance Object Operator Operator overloading Overload Override Polymorphism Prototype Pure virtual function Reference Relational and equality operators T r a n g | 229 CuuDuongThanCong.com https://fb.com/tailieudientucntt (231) TÀI LIỆU THAM KHẢO C++ [1] http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index jsp // Mục: XL C/C++ V8.0 for AIX // Language Reference [2] http://msdn.microsoft.com/enus/library/3bstk3k5%28v=VS.80%29.aspx [3] Ivor Horton// Beginning Visual C++ 2010// Wrox pub [4] C++ for Mathematicians// An introduction for Student and Professional//Edward Scheinerman//Chapman & Hall/CRC T r a n g | 230 CuuDuongThanCong.com https://fb.com/tailieudientucntt (232)