




























































































Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Community
Ask the community for help and clear up your study doubts
Discover the best universities in your country according to Docsity users
Free resources
Download our free guides on studying techniques, anxiety management strategies, and thesis advice from Docsity tutors
Here is my document. Happy to share with everyone......................................
Typology: Summaries
1 / 186
This page cannot be seen from the preview
Don't miss anything!
NGÔN NGỮ LẬP TRÌNH C++
PGS.TS. Trần Đình Quế KS. Nguyễn Mạnh Hùng
Lập trình nâng cao với C++ Lập trình hướng đối tượng với C++
GIỚI THIỆU
C++ là ngôn ngữ lập trình hướng đối tượng được mở rộng từ ngôn ngữ C. Do vậy, C++ có ưu điểm là kế thừa được các điểm mạnh truyền thống của ngôn ngữ C như uyển chuyển, tương thích với các thiết bị phần cứng. Hiện nay, C++ là một ngôn ngữ lập trình phổ biến, được giảng dạy tại các trường đại học trong nước và trên thế giới và đặc biệt được sử dụng rộng rãi cho nhu cầu phát triển của công nghiệp phần mềm hiện nay. Tài liệu này không những nhằm giới thiệu cho sinh viên ngôn ngữ lập trình C++, mà còn mong muốn qua đó sinh viên có thể hiểu được tư tưởng của phương pháp lập trình hướng đối tượng nói chung. Nội dung của tài liệu bao gồm hai phần chính:
Nội dung tài liệu được tổ chức thành 7 chương:
Chương 1: Giới thiệu tổng quan về các phương pháp lập trình Trình bày các phương pháp lập trình tuyến tính, lập trình cấu trúc và đặc biệt, làm quen với các khái niệm trong lập trình hướng đối tượng. Chương 2: Con trỏ và mảng Trình bày cách khai báo và sử dụng các kiểu con trỏ và mảng trong ngôn ngữ C++. Chương 3: Kiểu dữ liệu có cấu trúc Trình bày cách biểu diễn và cài đặt một số kiểu cấu trúc dữ liệu trừu tượng trong C++. Sau đó, trình bày cách áp dụng các kiểu dữ liệu này trong các ứng dụng cụ thể. Chương 4: Vào ra trên tệp Trình bày các thao tác đọc, ghi dữ liệu trên các tệp tin khác nhau: tệp tin văn bản và tệp tin nhị phân. Trình bày các cách truy nhập tệp tin trực tiếp. Chương 5: Lớp đối tượng Trình bày các khái niệm mở đầu cho lập trình hướng đối tượng trong C++, bao gồm cách khai báo và sử dụng lớp, các thuộc tính của lớp; cách khởi tạo và huỷ bỏ đối tượng, các quy tắc truy nhập đến các thành phần của lớp. Chương 6: Tính kế thừa và tương ứng bội Trình bày cách thức kế thừa giữa các lớp trong C++, các nguyên tắc truy nhập trong kế thừa, định nghĩa nạp chồng các phương thức và tính đa hình trong lập trình hướng đối tương với C++. Chương 7: Một số lớp quan trọng Trình bày cách sử dụng một số lớp có sẵn trong thư viện chuẩn của C++, bao gồm các lớp: lớp tập hợp, lớp chuỗi, lớp ngăn xếp, lớp hàng đợi và lớp danh sách liên kết.
Để đọc được cuốn sách này, sinh viên phải quen biết các khái niệm cơ bản về lập trình, có một số kỹ năng lập trình với ngôn ngữ C hoặc C++. Cuốn sách này cũng có thể dùng tài liệu tham khảo cho những sinh viên muốn tìm hiểu các kỹ thuật lập trình nâng cao và lập trình hướng đối tượng
với C++. Cuốn sách này có kèm theo một đĩa chương trình chứa toàn bộ các chương trình được lấy làm minh hoạ và các bài tập trong cuốn sách.
Mặc dù các tác giả đã có nhiều cố gắng trong việc biên soạn tài liệu này, song không thể tránh khỏi những thiếu sót. Rất mong nhận được những ý kiến đóng góp quý báu từ các sinh viên và các bạn đồng nghiệp.
Chương 1: Giới thiệu về các phương pháp lập trình
Chương trình = Cấu trúc dữ liệu + Giải thuật
Trong đó:
Trong chương trình, giải thuật có quan hệ phụ thuộc vào cấu trúc dữ liệu:
Tính chất
Ưu điểm
Nhược điểm
Vấn đề
Vấn đề cơ bản của lập trình cấu trúc là bằng cách nào để phân chia chương trình chính thành các chương trình con cho phù hợp với yêu cầu, chức năng và mục đích của mỗi bài toán. Thông thường, để phân rã bài toán trong lập trình cấu trúc, người ta sử dụng phương pháp thiết kế trên xuống (top-down).
1.2.2 Phương pháp thiết kế trên xuống (top-down)
Phương pháp thiết kế top-down tiếp cận bài toán theo hướng từ trên xuống dưới, từ tổng qúat đến chi tiết. Theo đó, một bài toán được chia thành các bài toán con nhỏ hơn. Mỗi bài toán con lại được chia nhỏ tiếp, nếu có thể, thành các bài toán con nhỏ hơn nữa. Quá trình này còn được gọi là quá trình làm mịn dần. Quá trình này sẽ dừng lại khi các bài toán con không cần chia nhỏ thêm
Chương 1: Giới thiệu về các phương pháp lập trình
nữa. Nghĩa là khi mỗi bài toán con đều có thể giải quyết bằng một chương trình con với một giải thuật đơn giản.
Ví dụ, sử dụng phương pháp top-down để giải quyết bài toán xây một căn nhà mới. Chúng ta có thể phân rã bài toán theo các bước như sau:
Quá trình phân rã có thể dừng ở mức này, bởi vì các công việc con thu được như đo đạc, cắm mốc, chăng dây, đào… có thể thực hiện được ngay, không cần chia nhỏ thêm nữa.
Lưu ý :
Ví dụ, vẫn áp dụng phương pháp top-down để giải quyết bài toán xây nhà, nhưng nếu sử dụng một cách khác để phân chia bài toán, ta có thể thu được kết quả khác biệt so với phương pháp ban đầu:
Rõ ràng, với cách làm mịn thế này, ta sẽ thu được một kết quả khác hẳn với cách thức đã thực hiện ở phần trên.
1.3.1 Lập trình hướng đối tượng
Trong lập trình hướng đối tượng:
Chương 1: Giới thiệu về các phương pháp lập trình
Lớp (Class)
Khi có nhiều đối tượng giống nhau về mặt dữ liệu và phương thức, chúng được nhóm lại với nhau và gọi chung là lớp:
Đóng gói dữ liệu (Encapsulation)
Tuy nhiên, vì C++ chỉ là ngôn ngữ lập trình nửa đối tượng, cho nên C++ vẫn cho phép định nghĩa các biến dữ liệu và các hàm tự do, đây là kết quả kế thừa từ ngôn ngữ C, một ngôn ngữ lập trình thuần cấu trúc.
Kế thừa (Inheritance)
Tính kế thừa của lập trình hướng đối tượng cho phép một lớp có thể kế thừa từ một số lớp đã tồn tại. Khi đó, lớp mới có thể sử dụng dữ liệu và phương thức của các lớp cơ sở như là của mình. Ngoài ra, lớp dẫn xuất còn có thể bổ sung thêm một số dữ liệu và phương thức. Ưu điểm của kế thừa là khi thay đổi dữ liệu của một lớp, chỉ cần thay đổi các phương thức trong phạm vi lớp cơ sở mà không cần thay đổi trong các lớp dẫn xuất.
Đa hình (Polymorphsim)
Đa hình là khái niệm luôn đi kèm với kế thừa. Do tính kế thừa, một lớp có thể sử dụng lại các phương thức của lớp khác. Tuy nhiên, nếu cần thiết, lớp dẫn xuất cũng có thể định nghĩa lại một số phương thức của lớp cơ sở. Đó là sự nạp chồng phương thức trong kế thừa. Nhờ sự nạp chồng phương thức này, ta chỉ cần gọi tên phương thức bị nạp chồng từ đối tượng mà không cần quan tâm đó là đối tượng của lớp nào. Chương trình sẽ tự động kiểm tra xem đối tượng là thuộc kiểu lớp cơ sở hay thuộc lớp dẫn xuất, sau đó sẽ gọi phương thức tương ứng với lớp đó. Đó là tính đa hình.
1.3.3 Lập trình hướng đối tượng trong C++
Vì C++ là một ngôn ngữ lập trình được mở rộng từ một ngôn ngữ lập trình cấu trúc C nên C++ được xem là ngôn ngữ lập trình nửa hướng đối tượng, nửa hướng cấu trúc.
Những đặc trưng hướng đối tượng của C++
Chương 1: Giới thiệu về các phương pháp lập trình
Những hạn chế hướng đối tượng của C++
Những hạn chế này là do C++ được phát triển từ một ngôn ngữ lập trình thuần cấu trúc C.
Chương 1 đã trình bày tổng quan về các phương pháp lập trình hiện nay. Nội dung tập trung vào ba phương pháp lập trình có liên quan trực tiếp đến ngôn ngữ lập trình C++:
C++ là ngôn ngữ lập trình được mở rộng từ ngôn ngữ lập trình cấu trúc C. Do đó, C++ vừa có những đặc trưng của lập trình cấu trúc, vừa có những đặc trưng của lập trình hướng đối tượng.
Chương 2: Con trỏ và mảng
Dùng con trỏ để lưu địa chỉ của biến
Bản thân con trỏ sẽ được trỏ vào địa chỉ của một biến có cùng kiểu dữ liệu với nó. Cú pháp của phép gán như sau: <Tên con trỏ> = &<tên biến>;
Lưu ý
Ví dụ: int x, *px; px = &x;
sẽ cho con trỏ px có kiểu int trỏ vào địa chỉ của biến x có kiểu nguyên. Phép toán &<Tên biến> sẽ cho địa chỉ của biến tương ứng.
Lấy giá trị của biến do con trỏ trỏ đến
Phép lấy giá trị của biến do con trỏ trỏ đến được thực hiện bằng cách gọi tên: *<Tên con trỏ>;
Lưu ý
Ví dụ: int x = 12, y, *px; px = &y; *px = x;
Quá trình diễn ra như sau:
con trỏ px vẫn trỏ tới địa chỉ biến y và giá trị của biến y sẽ là 12.
Phép gán giữa các con trỏ
Các con trỏ cùng kiểu có thể gán cho nhau thông qua phép gán và lấy địa chỉ con trỏ: <Tên con trỏ 1> = <Tên con trỏ 2>;
Lưu ý
int x = 12, y, *px; x = 12^ y = 0^ px^ null
px = &y; x = 12^ y = 0^ px
px = x; x = 12^ y=x =12^ px
Chương 2: Con trỏ và mảng
Ví dụ: int x = 12, *px, *py; px = &x; py = px;
con trỏ py cũng trỏ vào địa chỉ của biến x như con trỏ px. Khi đó *py cũng có giá trị 12 giống như *px và là giá trị của biến x.
Chương trình 2.1 minh hoạ việc dùng con trỏ giữa các biến của một chương trình C++.
Chương trình 2. #include <stdio.h> #include <conio.h> void main(void){ int x = 12, *px, *py; cout << ”x = ” << x << endl;
px = &x; // Con trỏ px trỏ tới địa chỉ của x cout << ”px = &x, *px = ” << *px << endl;
*px = px + 20; // Nội dung của px là 32 cout << ”px = *px+20, x = ” << x << endl;
py = px; // Cho py trỏ tới chỗ mà px trỏ: địa chỉ của x *py += 15; // Nội dung của py là 47 cout << ”py = px, *py +=15, x = ” << x << endl; }
Trong chương trình 2.1, ban đầu biến x có giá trị 12. Sau đó, con trỏ px trỏ vào địa chỉ của biến x nên con trỏ px cũng có giá trị 12. Tiếp theo, ta tăng giá trị của con trỏ px thêm 20, giá trị của con trỏ px là 32. Vì px đang trỏ đến địa chỉ của x nên x cũng có giá trị là 32. Sau đó, ta cho con trỏ py trỏ đến vị trí mà px đang trỏ tới (địa chỉ của biến x) nên py cũng có giá trị 32. Cuối cùng, ta tăng giá trị của con trỏ py thêm 15, py sẽ có giá trị 37. Vì py cũng đang trỏ đến địa chỉ của x nên x cũng có giá trị 37. Do đó, ví dụ 2.1 sẽ in ra kết quả như sau:
int x = 12, *px, *py; x = 12^ px^ py^ null
px = &x; x = 12^ px
py = px; x = 12^ px^ py
py null
null
Chương 2: Con trỏ và mảng
int *pa = &A[2];
thì con trỏ pa sẽ trỏ đến địa chỉ của phần tử A[2] và giá trị của pa là: *pa = A[2] = 15.
Khi đó, phép toán: pa = pa + 1;
sẽ đưa con trỏ pa trỏ đến địa chỉ của phần tử tiếp theo của mảng A, đó là địa chỉ của A[3]. Sau đó, phép toán: pa = pa – 2;
sẽ đưa con trỏ pa trỏ đến địa chỉ của phần tử A[1].
Lưu ý :
int A[5] = {5, 10, 15, 20, 25}; A^ A[0]=
int *pa = &A[2];
pa
A[1]=10 A[2]=15 A[3]=20 A[4]= 25
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
pa = pa + 1;
pa
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
pa = pa - 2;
pa
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
Chương 2: Con trỏ và mảng
Chương trình 2.2a minh hoạ việc cài đặt một thủ tục sắp xếp các phần tử của một mảng theo cách thông thường.
int A[5] = {5, 10, 15, 20, 25}; A A[0]=
int *pa = &A[2];
pa
A[1]=10 A[2]=15 A[3]=20 A[4]= 25
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
pa ++;
pa
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
pa = &A[0];
pa
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
null
pa --;
pa
null A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
int A[5] = {5, 10, 15, 20, 25}; A^ A[0]=
int *pa = &A[2]; pa
A[1]=10 A[2]=15 A[3]=20 A[4]= 25
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
pa ++; pa
A A[0]=5 A[1]=10 A[2]=15 A[3]=20 A[4]= 25
Nhưng *pa ++; pa
A A[0]=5 A[1]=10 A[2]= 16 A[3]=20 A[4]= 25
Chương 2: Con trỏ và mảng
Khi đó, địa chỉ của ma trận A chính là địa chỉ của hàng đầu tiên của ma trận A, và cũng là địa chỉ của phần tử đầu tiên của hàng đầu tiên của ma trận A:
Như vậy, một mảng hai chiều có thể thay thế bằng một mảng một chiều các con trỏ cùng kiểu: int A[3][3];
có thể thay thế bằng: int (*A)[3];
Con trỏ trỏ tới con trỏ
Vì một mảng hai chiều int A[3][3] có thể thay thế bằng một mảng các con trỏ int (*A)[3]. Hơn nữa, một mảng int A[3] lại có thể thay thế bằng một con trỏ int A. Do vậy, một mảng hai chiều có thể thay thế bằng một mảng các con trỏ, hoặc một con trỏ trỏ đến con trỏ. Nghĩa là các cách viết sau là tương đương: int A[3][3]; int (A)[3]; int **A;
Mặc dù hàm không phải là một biến cụ thể nên không có một địa chỉ xác định. Nhưng trong khi chạy, mỗi một hàm trong C++ cũng có một vùng nhớ xác định, do vậy, C++ cho phép dùng con trỏ để trỏ đến hàm. Con trỏ hàm được dùng để truyền tham số có dạng hàm.
Khai báo con trỏ hàm
Con trỏ hàm được khai báo tương tự như khai báo nguyên mẫu hàm thông thường trong C++, ngoại trừ việc có thêm kí hiệu con trỏ “” trước tên hàm. Cú pháp khai báo con trỏ hàm như sau: <Kiểu dữ liệu trả về> (<Tên hàm>)([<Các tham số>]);
Trong đó:
Ví dụ khai báo: int (*Calcul)(int a, int b);
là khai báo một con trỏ hàm, tên là Calcul, có kiểu int và có hai tham số cũng là kiểu int.
Lưu ý :
Chương 2: Con trỏ và mảng
Sử dụng con trỏ hàm
Con trỏ hàm được dùng khi cần gọi một hàm như là tham số của một hàm khác. Khi đó, một hàm được gọi phải có khuôn mẫu giống với con trỏ hàm đã được khai báo.
Ví dụ, với khai báo: int (*Calcul)(int a, int b);
thì có thể gọi các hàm có hai tham số kiểu int và trả về cũng kiểu int như sau: int add(int a, int b); int sub(int a, int b);
nhưng không được gọi các hàm khác kiểu tham số hoặc kiểu trả về như sau: int add(float a, int b); int add(int a); char* sub(char* a, char* b);
Chương trình 2.3 minh hoạ việc khai báo và sử dụng con trỏ hàm.
Chương trình 2. #include <ctype.h> #include
// Hàm có sử dụng con trỏ hàm như tham số void Display(char[] str, int (Xtype)(int c)){ int index = 0; while(str[index] != ‘\0’){ cout << (Xtype)(str[index]); // Sử dụng con trỏ hàm index ++; } return; }
// Hàm main, dùng lời gọi hàm đến con trỏ hàm void main(){ char input[500]; cout << “Enter the string: ”; cin >> input;