Tải bản đầy đủ (.pdf) (49 trang)

Lập trình C++ - ĐH xây dựng Hà Nội

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (491.06 KB, 49 trang )

NGUYỄN PHÚ QUẢNG
BỘ MÔN TIN HỌC XÂY DỰNG
KHOA CÔNG NGHỆ THÔNG TIN – ĐẠI HỌC XÂY DỰNG
LẬP TRÌNH C++
Tài liệu lưu hành nội bộ
version 0.0.2
Lập trình C++ Nguyễn Phú Quảng
Mục lục
I. MỞ ĐẦU 4
I.1. Môi trường phát triển 4
I.2. Chương trình đầu tiên 4
I.3. Input & Output 5
II. NGÔN NGỮ 5
II.1. Cơ bản về ngôn ngữ 5
II.2. Kiểu 5
II.3. Chuyển đổi giữa các kiểu 7
II.4. Biểu thức & toán tử 7
III. LỆNH 9
III.1. Lệnh dạng biểu thức 9
III.2. Khối lệnh 9
III.3. Lệnh rẽ nhánh 9
III.4. Vòng lặp for 10
III.5. Vòng lặp while 13
III.6. Vòng lặp do while 13
III.7. Nhảy không điều kiện 13
IV. HÀM 13
IV.1. Định nghĩa hàm 13
IV.2. Ví dụ khai báo hàm 14
IV.3. Khi nào sử dụng hàm 14
IV.4. Hàm được gọi như thế nào? 14
IV.5. Hàm gọi đệ quy 15


V. MẢNG 17
V.1. Định nghĩa 17
V.2. Truy cập phần tử của mảng 17
V.3. Khởi tạo mảng 17
V.4. Mảng nhiều chiều 17
V.5. Sử dụng mảng làm tham số của hàm 17
VI. CON TRỎ 17
VI.1. Khai báo con trỏ 17
VI.2. Các toán tử trên con trỏ 17
VI.3. Con trỏ và mảng 17
VI.4. Con trỏ hàm 17
VII. STRUCT 17
VII.1. Định nghĩa Struct 17
VII.2. Khai báo biến 18
VII.3. Truy cập trường 18
VIII. Stream 18
VIII.1. File stream 18
VIII.2. String stream 19
VIII.3. Ghi có định dạng 19
IX. KIỂU NGƯỜI DÙNG ĐỊNH NGHĨA 21
X. CẤP PHÁT BỘ NHỚ ĐỘNG 21
2
Lập trình C++ Nguyễn Phú Quảng
XI. LỚP 21
XII. MẪU 21
XIII. THƯ VIỆN CHUẨN 21
XIII.1. Các khái niệm 21
XIII.2. Kiểu vector 22
XIII.3. Kiểu string 26
XIII.4. Kiểu list (danh sách) 29

XIII.5. Kiểu set (tập hợp) 32
XIII.6. Kiểu map (ánh xạ) 32
XIII.7. Kiểu hash_map (ánh xạ dùng mảng băm) 34
XIII.8. Kiểu hash_set (tập hợp) 35
XIII.9. Thuật toán (Algorithm) 35
XIV. GIẢI MỘT SỐ BÀI TOÁN BẰNG STL 43
XIV.1. Quản lý sinh viên 43
XIV.2. Rào đất 45
XIV.3. Robot 46
XIV.4. Dijsktra 47
XIV.5. Hợp diện tích hình chữ nhật .47
XV. INPUT VÀ OUTPUT 49
3
Lập trình C++ Nguyễn Phú Quảng
I. MỞ ĐẦU
C++ là ngôn ngữ mạnh, được phát triển từ ngôn ngữ C. Học C++ đem lại nhiều lợi thế cho bạn với
khối lượng mã nguồn vào loại lớn nhất trong các ngôn ngữ, cũng như tính tương đồng với hầu hết
các ngôn ngữ tựa C (C-like language).
Tài liệu này mong muốn giúp bạn có một cái nhìn tổng thể với cách tiếp cận thật ngắn gọn. Cấu trúc
sách dựa trên cuốn "C++ in a nutshell" của nhà xuất bản O'Reilly.
Tại Việt nam, đã có rất nhiều cuốn sách giới thiệu về C, C++. Trong tài liệu này, chúng tôi không
mang tham vọng xây dựng một giáo trình về C++ mà chỉ là những đúc kết kinh nghiệm mà chúng
tôi đã trực tiếp làm việc với C++, nhất là trong những bài toán thiên về giải thuật.
Mọi góp ý xin gửi về địa chỉ: Xin chân thành cảm ơn mọi đóng góp
của các bạn.
I.1. Môi trường phát triển
Để thuận tiện cho việc học ngôn ngữ, chúng tôi xin giới thiệu 2 môi trường quen thuộc để bạn có
thể thực hành lập trình C++.
Microsoft Visual C++ (6.0, 7.0, 7.1): phần mềm này nằm trong bộ phần mềm Microsoft Visual
Studio.

DevC++ 5.0: Bạn có thể tải miễn phí từ địa chỉ chúng tôi
khuyến khích các bạn sử dụng phần mềm miễn phí này.
Toàn bộ chương trình minh họa đều có thể chạy trên cả 2 môi trường VC++ và DevC.
I.2. Chương trình đầu tiên
Mở DevC, bạn tạo file mới, gõ vào đoạn chương trình dưới đây và lưu lại (tất nhiên là không gõ vào
số dòng, trong tất cả các chương trình minh họa, chúng tôi sử dụng số dòng để giải thích cho
chương trình). Sau đó bấm Ctrl+F9 để dịch chương trình, bấm Ctrl+F10 để chạy chương trình, bạn
sẽ thấy một màn hình màu đen và dòng chữ "Hello every body!", khi bạn gõ 1 phím bất kỳ thì màn
hình màu đen sẽ đóng lại.
01 #include <iostream>
02 #include <stdio.h>
03
04 using namespace std;
05 int main() {
06 cout << "Hello every body!" << endl;
07 getchar();
08 }
Dòng 01, 02: Sử dụng 2 thư viện iostream và stdio.h
Dòng 04: Khi các chương trình dài và phức tạp, việc đặt tên cho các biến, hàm gặp khó khăn. Để dễ
dàng hơn khi đặt tên, người ta chia tên thành nhiều không gian khác nhau gọi là namespace. std là
một namespace được sử dụng trong iostream và một số thư viện chuẩn khác.
Dòng 05: Hàm main là hàm không thể thiếu trong các chương trình C++. Khi chương trình được
thực thi, nó sẽ gọi đến hàm main.
Dòng 06: Trong C++, khái niệm stream chỉ một dòng dữ liệu (đầu vào hoặc đầu ra). Trong trường
hợp này cout là dòng dữ liệu ra (mặc định là màn hình). Sử dụng toán tử <<, chúng ta thực hiện việc
đưa 2 dữ liệu lần lượt là xâu ký tự "Hello every body!" và ký tự xuống dòng endl ra màn hình.
4
Lập trình C++ Nguyễn Phú Quảng
Dòng 07: getchar() là hàm giúp chương trình dừng lại và đợi người dùng gõ một phím bất kỳ. Thực
chất, trong ví dụ này không cần phải dừng chương trình. Nhưng do đặc tính của môi trường DevC

và VC là màn hình output sẽ biến mất sau khi chương trình chạy xong, do đó chúng tôi đưa thêm
lệnh này để dừng chương trình lại, giúp các bạn quan sát thấy xâu ký tự in trên màn hình.
I.3. Input & Output
II. NGÔN NGỮ
II.1. Cơ bản về ngôn ngữ
Ngôn ngữ lập có nhiều điểm tương tự với ngôn ngữ tự nhiên
- Chứa một tập từ khóa (giống với từ vựng của ngôn ngữ tự nhiên)
- Chứa một loạt các quy tắc kết hợp các từ khóa, toán tử với nhau (giống với ngữ pháp trong
ngôn ngữ tự nhiên)
Khi bạn đã thạo một ngôn ngữ lập trình, việc học một ngôn ngữ khác là tương đối đơn giản (cũng
như bạn đã học tiếng Anh thì có thể học tiếng Pháp rất nhanh vì hai ngôn ngữ này có nhiều từ tương
đồng với nhau).
Việc học ngôn C++ (cũng như học tiếng Anh) đem lại cho bạn lợi thế rất lớn vì đây là ngôn ngữ
được sử dụng phổ biến nhất. Hơn thế nữa có rất nhiều các ngôn ngữ lập trình khác được xây dựng
trên cơ sở cú pháp của C++ (gọi là các ngôn ngữ giống-C – C-like), do đó bạn có thể tiếp cận rất
nhanh với các ngôn ngữ này nếu đã thông thạo với C++.
II.2. Kiểu
II.2.1. Các kiểu cơ bản
Phần này trình bày các kiểu dữ liệu cơ bản của C++. Trong giới hạn của tài liệu này, tôi không trình
bày về con trỏ. Mặc dù con trỏ là một trong nhưng đặc điểm nổi bật của C++ nhưng tính phức tạp
của con trỏ dễ gây nhầm lẫn cho người mới làm quen. Chúng ta sẽ giải quyết các bài toán trên C++
mà không hoặc hạn chế tối đa sử dụng con trỏ.
a. Kiểu số
• Số nguyên
- Kiểu char (1 byte): (-128 đến 127)
- Kiểu short (2 byte): (-32768 đến 32767)
- Kiểu int, long (4 byte): (-2 tỷ đến 2 tỷ)
- Tất cả các kiểu trên, nếu bổ sung unsign :
o unsign char:
o unsign short:

o unsign int:
- Việc lựa chọn kiểu dữ liệu trong bài toán phục thuộc vào vùng giá trị của biến. Khoảng giá
trị càng lớn, biến càng chiếm nhiều bộ nhớ. Chương trình C++ dịch trên DevC hoạt động
trên môi trường Windows thường là hệ điều hành 32 bit, số nguyên được sử dụng phổ biến
nhất là kiểu int.
5
Lập trình C++ Nguyễn Phú Quảng
- Ví dụ
char a;
int b = 20;
long c = 30;
unsign char d = 200;
• Số thực
- float (4 byte): Số thực độ chính xác đơn.
- double (8 byte): Số thực độ chính xác kép.
• Logic
- bool : có 2 giá trị là true và false
II.2.2. Các kiểu phức
a. Kiểu mảng
- Mảng là một dãy các phần tử cùng kiểu được sắp liên tiếp nhau trong bộ nhớ
- Các phần tử được phân biệt với nhau bởi chỉ số, là các số tuần tự bắt đầu từ 0
- Ví dụ
int a[100]; // Khai báo mảng gồm 100 số int
// a[0] là 1 số int, phần tử đầu tiên của mảng
// a[1] là 1 số int, phần tử thứ 2 của mảng
// a[99] là 1 số int, phần tử cuối cùng của mảng
long b[10]; // Khai báo mảng gồm 10 số long
- Mảng được khai báo theo ví dụ trên là mảng 1 chiều. Chiều của mảng là số chỉ số sử dụng
để xác định một phần tử (giống như hệ tọa độ 1 chiều, 2 chiều, 3 chiều)
- Ví dụ khai bao và sử dụng mảng nhiều chiều như sau

int a[3][3]; // Khai báo a là mảng 2 chiều (giống như 1 bảng)
// a[0] là một mảng 1 chiều gồm 3 số nguyên (giống như 1 dòng của bảng)
// a[0][0] là số int đầu tiên của dòng
// a[0][1] là số int thứ 2 của dòng
// a[0][2] là số int cuối cùng của dòng
// a[1] là dòng thứ 2 của bảng
// a[2] là dòng cuối cùng của bảng
a[1][1] = 5; // Gán giá trị của số nguyên ở dòng 1, cột 1 là 5
b. Kiểu struct
Struct là kiểu dữ liệu cho phép bạn tập hợp nhiều thành phần vào trong cùng một biến. Lấy ví dụ
thông tin của một điểm gồm 2 tọa độ x, y, thông tin của một đoạn thẳng gồm điểm đầu và điểm
cuối.
Struct là kiểu dữ liệu, do đó bạn phải khai báo kiểu dữ liệu đó bằng lệnh typedef rồi mới có thể khai
báo được các biến thuộc kiểu này. Cú pháp khai báo struct như sau:
struct <Tên_kiểu> { // Khai báo kiểu struct
<Kiểu_trường_1> <Tên_trường_1>; // Trường của struct (giống khai báo biến)
<Kiểu_trường_2> <Tên_trường_2>, <Tên_trường_3>;
};
<Tên_kiểu> <biến_1>, <biến_2>; // Khai báo biến thuộc kiểu mới tạo ra
<biến_1>.<Tên_trường_1> = <Giá_trị>; // Truy cập trường dữ liệu của biến
Các thành phần khai báo bên trong struct được gọi là trường (field). Một struct gồm nhiều trường,
biến thuộc kiểu struct
6
Lập trình C++ Nguyễn Phú Quảng
II.2.3. Các kiểu stl
a. Kiểu string
b. Kiểu vector
c. Kiểu vector
II.3. Chuyển đổi giữa các kiểu
II.4. Biểu thức & toán tử

II.4.1. Biểu thức
Biểu thức là kết hợp của các toán tử và toán hạng. Toán hạng có thể là các số hoặc các biến.
II.4.2. Toán tử
a. Các phép toán số học
- Phép cộng +
o a + b
- Phép trừ -
o a - b
- Phép nhân *
o a * b
- Phép chia /: Chú ý trong phép chia
o Nếu cả 2 toán hạng cùng là số nguyên thì phép chia là chia nguyên
 10 / 3 cho giá trị 3
o Nếu 1 trong 2 toán hạng là số thực thì phép chia là chia số thực
 10.0 / 3 cho giá trị 3.3333333
- Phép lấy dư: %
o 10 % 3 cho giá trị 1
b. Các phép toán so sánh
- So sánh bằng ==
o 10 == 3 cho giá trị false
o 10 == 10 cho giá trị true
- So sánh khác !=
o 10 != 3 cho giá trị true
7
Lập trình C++ Nguyễn Phú Quảng
- So sánh lớn hơn >
- So sánh nhỏ hơn <
- So sánh lớn hơn hay bằng >=
- So sánh nhỏ hơn hay bằng <=
c. Các phép toán logic

- Phép toán and &&
o 10==3 && 5>4: cho giá trị false
o 10>3 && 5>4: cho giá trị true
- Phép toán or ||
o 10==3 || 5>4: cho giá trị true
o 10==3 || 5<4 cho giá trị false
- Phép toán not !
o !(10==3) cho giá trị
d. Các phép toán trên bit
- Phép toán and từng bit &
o 10 & 3 = 1010
2
& 0011
2
= 0010
2
o 10 & 15 = 1010
2
& 1111
2
=1010
2
- Phép toán or từng bit |
o 10 | 3 = 1010
2
| 0011
2
= 1011
2
o 10 | 15 = 1010

2
| 1111
2
= 1111
2
e. Các phép toán 1 ngôi
- Phép tăng 1 đơn vị
o i++ (chỉ dành cho biến có giá trị nguyên)
- Phép giảm 1 đơn vị
o i—
- Phép tăng giá trị của biến
o i += 2
- Phép giảm giá trị của biến
o i -= 2
- Phép nhân giá trị của biến với 1 số
o i *= 5
- Phép chia giá trị của biến cho 1 số
o i /= 5
- Các phép toán khác như &=, |=, &&=, ||= tương tự
8
Lập trình C++ Nguyễn Phú Quảng
III. LỆNH
III.1. Lệnh dạng biểu thức
III.1.1. Phép gán
Tên biến = Biểu thức;
- Biểu thức có thể là 1 hằng số
- Biểu thức có thể là 1 biến
- Biểu thức có thể là biến và hằng số kết hợp với nhau bởi toán tử
III.2. Khối lệnh
Các khối lệnh để phân chương trình thành nhiều khối con. Việc phân khối giúp chương trình rõ

ràng, dễ hiểu hơn theo nguyên tắc chia để trị. Một khối lệnh được đặt trong cặp ngoặc { } và được
coi như một lệnh đơn.
C++ cho phép khai báo biến tại bất kỳ nơi nào trong chương trình. Biến khai báo trong khối lệnh
nào thì chỉ tồn tại trong khối chương trình đó (biến khai báo trong khối chương trình trong không
thể sử dụng trong khối chương trình ngoài).
Ví dụ về việc sử dụng các khối chương trình.
if (a>b) {
int m; // Biến m khai báo trong khối if (a>b)
m = 1; // m có thể được sử dụng tại cùng khối chương trình
if (d>e) {
m = 5; // m có thể được sử dụng tại khối chương trình bên trong
}
} else {
m = 10; // m không thể sử dụng được ở khối chương trình ngoài
}
III.3. Lệnh rẽ nhánh
Lệnh if sử dụng để có hay không thực hiện một đoạn chương trình theo một điều kiện (gọi là rẽ
nhánh). Lệnh if gồm 2 dạng: rút gọn và đầy đủ
- Dạng rút gọn: Nếu <Điều_kiện> đúng thì thực hiện <Lệnh>
if (<Điều_kiện>) {
<Lệnh>
}
o <Điều_kiện> có thể là
 Một biểu thức logic
 Một biểu thức kiểu số (số khác 0 được coi là đúng, bằng 0 là sai)
o Nếu <Lệnh> chỉ là 1 lệnh đơn, bạn không cần để trong khối chương trình bằng cặp
ngoặc {}. Tuy nhiên, việc này không được khuyến khích.
- Dạng đầy đủ: Nếu <Điều_kiện> là đúng thì thực hiện <Lệnh_nếu_đúng>, ngược lại thực
hiện <Lệnh_nếu_sai>
if (<Điều_kiện>) {

<Lệnh_nếu_đúng>;
} else {
<Lệnh_nếu_sai>;
}
If là một cấu trúc điều khiển được sử dụng phổ biến nhất. Đoạn chương trình sau sẽ nêu ví dụ sử
dụng lệnh if.
9
Lập trình C++ Nguyễn Phú Quảng
#include <iostream>
#include <stdlib.h>
using namespace std;
int main ()
{
int gio;
cout << "Nhap vao gio (0-24): ";
cin >> gio;
if (gio<12)
{
cout << "Chao buoi sang";
} else
{
cout << "Chao buoi chieu";
}
getchar();
getchar();
}
III.4. Vòng lặp for
III.4.1. Cú pháp vòng lặp for
Vòng lặp for của C++ khái quát hơn vòng lặp for của Pascal. Bạn có thể sử dụng vòng lặp với cú
pháp rất mềm dẻo cho nhiều hoàn cảnh khác nhau. Cú pháp của vòng lặp for như sau:

for (<Lệnh_khởi_tạo> ; <Điều_kiện> ; <Lệnh_thay_đổi>)
{
<Lệnh_lặp>;
}
Trình tự thực hiện của vòng lặp như sau:
- Thực hiện <Lệnh_khởi_tạo>
- Kiểm tra <Điều_kiện>
o Nếu <Điều_kiện> là đúng:
 Thực hiện <Lệnh_lặp>
 Thực hiện <Lệnh_thay_đổi> để thay đổi trạng thái
 Lặp lại bước thứ 2 (Kiểm tra điều kiện)
o Nếu <Điều_kiện> sai, thoát khỏi vòng lặp
Xét ví dụ sau:
for (int i=0; i<3; i++)
{
cout << "i = " << i << endl;
}
- Thực hiện lệnh khởi tạo (i=0)
- Kiểm tra điều kiện (i<3)
o i=0 nên điều kiện là đúng
 Thực hiện lệnh cout lệnh này viết ra màn hình dòng chữ "i = 0"
 Thực hiện lệnh thay đổi i++ (i tăng từ 0 thành 1)
 Lặp lại bước kiểm tra
- Kiểm tra điều kiện (i<3)
o i=1 nên điều kiện là đúng
 Thực hiện lệnh cout lệnh này viết ra màn hình dòng chữ "i = 1"
10
Lập trình C++ Nguyễn Phú Quảng
 Thực hiện lệnh thay đổi i++ (i tăng từ 1 thành 2)
 Lặp lại bước kiểm tra

- Kiểm tra điều kiện (i<3)
o i=2 nên điều kiện là đúng
 Thực hiện lệnh cout lệnh này viết ra màn hình dòng chữ "i = 2"
 Thực hiện lệnh thay đổi i++ (i tăng từ 2 thành 3)
 Lặp lại bước kiểm tra
- Kiểm tra điều kiện (i<3)
o i=3 nên điều kiện sai, thoát khỏi vòng lặp
Như vậy, với vòng lặp nêu trên, chương trình thực hiện lệnh lặp 3 lần, viết ra màn hình 3 dòng
 i = 0
 i = 1
 i = 2
Vòng lặp kết thúc khi i = 3 (điều kiện kiểm tra sai)
III.4.2. Vòng lặp for lồng nhau
Ví dụ sử dụng vòng lặp for lồng nhau để viết bảng cửu chương ra màn hình. Trong trường hợp này
vòng lặp for sẽ hoạt động như sau:
 i = 0
- Thực hiện vòng lặp for với j = 0, 1, 2, , 8, 9 (10 lần lặp, mỗi lần viết ra tích i*j), sau đó
viết xuống dòng.
 i = 1
- Thực hiện vòng lặp với j = 0, 1, 2, 9
 i = 2
-
 i = 9
- Thực hiện vòng lặp với j = 0, 1, 2, 9
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main ()
{

for (int i=0; i<10; i++)
{
for (int j=0; j<10; j++) cout << setw(4) << i*j;
cout << endl;
}
getchar();
}
Kết quả của đoạn chương trình sẽ như sau:
11
Lập trình C++ Nguyễn Phú Quảng
Vòng lặp for của C++ có thể thay thế vòng lặp for downto của Pascal bằng cách thay đổi điều kiện
kiểm tra và lệnh tăng thành lệnh giảm. Ví dụ sau viết ra 100 số từ 990, 980, 970 đến 0
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main ()
{
for (int i=990; i>=0; i-=10)
{
cout << i << " ";
}
getchar();
}
III.4.3. Sử dụng break và continue trong vòng lặp for
Trong vòng lặp for, bạn có thể sử dụng các lệnh sau:
- break để thoát khỏi vòng lặp
- continue để tiếp tục thực hiện bước tiếp theo của vòng lặp
Sau đây là ví dụ sử dụng break và continue:
#include <iostream>

#include <stdlib.h>
#include <math.h>
using namespace std;
int main()
{
int n = 100; // Viết tất cả các số nguyên tố từ 1 đến n ra màn hình
int i, j, m;
for (i=1; i<=n; i++) { // Xét từng số
// Xét tất cả các số từ 2 đến căn 2 của i
for (j=2, m = sqrt(i); j<=m; j++) {
if (i%j==0) break; // Nếu có 1 số là ước thì thoát
}
// Nếu thoát khi i chia hết cho j thì i ko phải là số nguyên tố
// Nếu i không chia hết cho j, số i là nguyên tố
if (i%j!=0) cout << i << " ";
}
getchar();
}
12
Lập trình C++ Nguyễn Phú Quảng
III.5. Vòng lặp while
Cú pháp
while (<Điều_kiện>)
{
<Lệnh_lặp>;
}
Trong khi <Điều_kiện> còn đúng thì thực hiện <Lệnh_lặp>
Vòng lặp while ít khi được sử dụng vì bạn có thể sử dụng vòng lặp for thay cho while.
Vòng lặp while và do while đều có thể sử dụng break và continue tương tự vòng lặp
III.6. Vòng lặp do while

do
{
<Lệnh_lặp>;
} while (<Điều_kiện>);
Thực hiện <Lệnh_lặp> cho đến khi <Điều_kiện> sai.
III.7. Nhảy không điều kiện
Lệnh goto sử dụng để nhảy đến một vị trí trong chương trình. Lệnh goto phá vỡ tính cấu trúc của
chương trình nhưng nếu biết tận dụng sẽ làm giảm độ phức tạp của chương trình. Vẫn với ví dụ viết
ra các số nguyên tố, bạn có thể chỉnh sửa chương trình cho đơn giản hơn bằng cách sử dụng goto:
#include <iostream>
#include <stdlib.h>
#include <math.h>
using namespace std;
int main()
{
int n = 100;
int i, j, m;
for (i=1; i<=n; i++) {
for (j=2, m = sqrt(i); j<=m; j++) {
// Nếu i chia hết cho j, nhảy đến vị trí "KoNguyenTo"
if (i%j==0) goto KoNguyenTo;
}
// Nếu i không chia hết cho bất cứ j nào, i là số nguyên tố
cout << i << " ";
// Nếu i chia hết cho 1 số j, Chương trình sẽ nhảy trực tiếp đến
// vị trí KoNguyenTo
KoNguyenTo:;
}
getchar();
}

IV. HÀM
IV.1. Định nghĩa hàm
Cú pháp:
<Kiểu_hàm> <Tên_hàm>([Danh_sách_tham_số])
{
<Lệnh>;
return <Giá_trị_trả_về>;
<Lệnh>;
}
Trong đó:
- <Kiểu_hàm>: Kiểu giá trị trả về của hàm, (nếu hàm không trả về giá trị, kiểu hàm là void)
13
Lập trình C++ Nguyễn Phú Quảng
- <Tên_hàm>: Tên của hàm (quy ước giống như đặt tên biến – không có dấu cách, không
chứa ký tự đặc biệt)
- Nếu hàm có kiểu void, có thể dùng lệnh return để thoát khỏi hàm
- Nếu hàm có kiểu khác void, bạn phải trả về giá trị tương ứng với kiểu (ví dụ kiểu int thì
phải là return 1)
- Nếu không có tham số, bạn vẫn phải có cặp ngoặc ()
Danh sách tham số được liệt kê như sau:
<Kiểu_tham_số_1> <Tên_tham_số_1>, <Kiểu_tham_số_2> <Tên_tham_số_2>,
IV.2. Ví dụ khai báo hàm
#include <iostream>
#include <fstream>
#include <stdio.h>
using namespace std;
// Hàm tính tổng của 2 số
// Có 2 tham số a, b (kiểu int)
// Trả về tổng của a và b (kiểu int)
int Tong(int a, int b)

{
return a+b;
}
int main()
{
cout << "Ham tinh tong: " << Tong(20,34) << endl;
getchar();
}
IV.3. Khi nào sử dụng hàm
Hàm được sử dụng trong các trường hợp sau:
- Một đoạn lệnh được lặp đi, lặp lại nhiều lần
- Một đoạn chương trình quá dài, cần chia nhỏ để dễ quản lý
IV.4. Hàm được gọi như thế nào?
Các biến và tham số của hàm được lưu trong stack, mỗi khi bạn gọi hàm, máy tính thực hiện các
công việc sau:
- Lần lượt đưa các tham số vào trong stack
- Lưu lại vị trí gọi hàm (của hàm hiện tại)
- Trỏ chương trình sang vị trí mới (của hàm được gọi)
- Lấy các tham số ra khỏi stack
- Mỗi biến được khai báo trong chương trình con sẽ được đưa vào stack
Khi thoát khỏi chương trình con, máy tính làm việc theo quy trình ngược lại
- Lấy các biến đã khai báo ra khỏi stack
- Lưu lại giá trị trả về của hàm (trong thanh ghi)
- Trỏ chương trình về vị trí cũ (trước khi gọi hàm)
Với quy trình này, có thể nhận ra các đặc điểm sau:
- Khi gọi hàm, bộ nhớ của stack đầy lên bằng tổng kích thước của các tham số và các biến
khai báo trong hàm
14
Lập trình C++ Nguyễn Phú Quảng
- Sau khi thoát khỏi hàm, vùng bộ nhớ cấp cho các tham số và biến trong hàm được khôi

phục, stack trở về trạng thái cũ
- Các hàm có thể gọi lẫn nhau, mỗi lần gọi hàm stack lại đầy thêm. Stack chứa thông tin thứ
tự các hàm gọi nhau và các biến trong hàm đó (thông tin này gọi là Call stack)
IV.5. Hàm gọi đệ quy
Đệ quy là cơ chế một hàm tự gọi chính nó. Nhờ sử dụng stack, mỗi hàm sẽ có vùng nhớ lưu giữ
tham số và biến của riêng nó. Các vùng nhớ này được sắp xếp trên stack (hàm gọi cuối cùng sẽ
được lấy ra trước tiên).
Xét ví dụ kinh điển sau:
#include <iostream>
#include <fstream>
#include <stdio.h>
using namespace std;
// Hàm tính giai thừa của một số
// Sử dụng công thức truy hồi n! = n * (n-1)!
// Hàm GiaiThua(n) được tính từ hàm GiaiThua(n-1)
// Tức là hàm GiaiThua tự gọi lại chính nó (đệ quy)
int GiaiThua(int n)
{
if (n>=1) return n*GiaiThua(n-1); else return 1;
}
int main()
{
int n;
cout << "n = "; cin >> n;
cout << "n! = " << GiaiThua(n) << endl;
fflush(stdin);
getchar();
}
Chương trình trên tính giai thưa của một số bằng cách sử dụng hàm GiaiThua. Bản thân hàm này sử
dụng công thức truy hồi n! = n * (n-1)! Hàm GiaiThua(n) gọi hàm GiaiThua(n-1), GiaiThua(n-1)

gọi GiaiThua(n-2). Cứ tuần tự cho đến hàm GiaiThua(1), hàm này quá đơn giản vì cho giá trị 1 nên
không gọi đệ quy.
Như vậy, hàm GiaiThua(n) được tính bằng cách gọi đệ quy n lần thay vì dùng vòng lặp. Để các bạn
hiểu rõ hơn về đệ quy, chúng ta phân tích hoạt động của hàm GiaiThua(n) với n=4.
 Chúng ta hãy thử phân tích quá trình hoạt động của các hàm khi gọi đệ quy qua bảng sau
4>=1
4*GiaiThua(3)
3
3*GiaiThua(2)
2>=1
2*GiaiThua(1
)
1>=1
End
return
1
return
2*1=2
return
3*2=6
return
4*6=24
Begin
Đ
Đ
Đ
S
15
Lập trình C++ Nguyễn Phú Quảng
Bước Mô tả Stack

main() Chương trình bắt đầu hoạt
động từ hàm main()
gọi GiaiThua(4) Hàm main() thực hiện lời gọi
hàm GiaiThua(4)
gọi GiaiThua(3) Hàm GiaiThua(4) gọi hàm
GiaiThua(3)
gọi GiaiThua(2) Hàm GiaiThua(3) gọi hàm
GiaiThua(2)
gọi GiaiThua(1) Hàm GiaiThua(2) gọi hàm
GiaiThua(1)
Trả về giá trị
của
GiaiThua(1)
n=1, hàm GiaiThua(1) không
gọi đệ quy mà trả về giá trị 1,
sau đó thoát ra, trở về hàm đã
gọi nó (là hàm GiaiThua(2))
Trả về giá trị
của
GiaiThua(2)
n=2, Tính n*GiaiThua(1) = 2*1
= 2
Trả về giá trị 2
Thoát trở về hàm đã gọi hàm
này (là hàm GiaiThua(3))
Trả về giá trị
của
GiaiThua(3)
n=3, Tính n*GiaiThua(2) = 3*2
= 6

Trả về giá trị 6
Thoát trở về hàm đã gọi hàm
này (là hàm GiaiThua(4))
Trả về giá trị
của
GiaiThua(4)
n=4, tính n*GiaiThua(3) = 4*6 =
24
Trả về giá trị 24
4
4
3
4
3
2
4
3
2
1
4
3
2
1
4
3
2
4
3
4
16

Lập trình C++ Nguyễn Phú Quảng
Thoát trở về hàm đã gọi hàm
này (là hàm main())
V. MẢNG
V.1. Định nghĩa
V.2. Truy cập phần tử của mảng
V.3. Khởi tạo mảng
V.4. Mảng nhiều chiều
V.5. Sử dụng mảng làm tham số của hàm
VI. CON TRỎ
Trong máy tính, bộ nhớ được sắp xếp trong các ô liên tiếp nhau, mỗi ô chiếm 1 byte. Các ô nhớ này
phân biệt với nhau bởi địa chỉ (gần giống các phần tử của mảng phân biệt với nhau bởi chỉ số).
Mỗi biến, tùy theo kích thước sẽ chiếm 1 hoặc nhiều ô nhớ liên tục trong bộ nhớ. Địa chỉ của biến
là địa chỉ của ô nhớ đầu tiên của biến đó.
Trong C++, có một loại biến lưu lại địa chỉ của các ô nhớ, các biến này được gọi là con trỏ. Con trỏ
cũng là biến, có kích thước 4 byte, lưu 1 số là địa chỉ của các biến.
VI.1. Khai báo con trỏ
VI.2. Các toán tử trên con trỏ
VI.3. Con trỏ và mảng
VI.4. Con trỏ hàm
VII. STRUCT
VII.1. Định nghĩa Struct
17
Lập trình C++ Nguyễn Phú Quảng
VII.2. Khai báo biến
VII.3. Truy cập trường
VIII. Stream
VIII.1. File stream
VIII.1.1. Ghi file
Để ghi dữ liệu ra file text, bạn sử dụng kiểu dữ liệu ofstream (o = output, f = file). Kiểu dữ liệu này

yêu cầu bạn phải khai báo header là <fstream>.
iostream có các phương thức sau:
- f.open("tên file"): Mở file
- f.close(): Đóng file
#include <iostream>
#include <fstream>
using namespace std;
int main () {
ofstream myfile;
myfile.open ("c:/vidu.txt");
myfile << "Ghi thong tin ra file." << endl;
myfile << 123.43 << endl;
myfile.close();
return 0;
}
VIII.1.2. Đọc file
- Tương tự ghi file, đọc file text sử dụng kiểu dữ liệu ifstream (i = input, f = file).
#include <iostream>
#include <fstream>
#include <stdio.h>
using namespace std;
int main () {
ifstream myfile;
myfile.open ("c:/vidu.txt");
int a, b, c, d, e;
myfile >> a >> b >> c >> d >> e;
myfile.close();
cout << a << " " << b << " " << c << " " << d << " " << e << endl;
getchar();
return 0;

}
// Noi dung file
/*
3 4 5 6 7
*/
Ví dụ trên mới chỉ minh họa đọc các giá trị liên tiếp từ file. Để đọc các dòng, xâu ký tự ifstream
có các phương thức sau:
- f.eof() : Kiểm tra xem đã hết file chưa? Trả về 0 nếu chưa hết file, 1 nếu đã hết file
18
Lập trình C++ Nguyễn Phú Quảng
- f.get() : Đọc 1 ký tự trên file: Sử dụng để đọc ký tự xuống dòng sau khi dùng các toán tử dẫn
hướng từ stream ra biến (tham khảo ví dụ). Trả về ký tự vừa đọc được.
- getline(f, str) : Đọc một dòng từ file (vào xâu ký tự str)
- Sau khi đọc 1 dòng từ file, bạn có thể dùng istringstream để đọc dữ liệu từ luồng xâu ký tự
vào các biến. (tham khảo ví dụ)
/*
Đọc vào từ file readfile.txt với cấu trúc
Dòng 1: Số dòng của mảng 2 chiều
Các dòng tiếp theo, mỗi dòng là các phần tử của mảng 2 chiều (lưu ý, số phần tử
mỗi dòng có thể khác nhau, không có số ghi số lượng phần tử ở đầu)
*/
#include <iostream>
#include <fstream> // header cho đọc ghi file
#include <sstream> // header cho đọc ghi trên string
#include <stdio.h>
#include <vector>
using namespace std;
int main () {
int n;
typedef vector<int> dong; // Kiểu dòng (mảng số nguyên)

vector<dong> a; // Mảng 2 chiều (mảng các dòng)
string line;
int b;
ifstream f("readfile.txt"); // Mở file
f >> n; f.get(); // Đọc số dòng, f.get(): xuống dòng
a.resize(n); // Đặt số dòng cho mảng 2 chiều
for (int i=0; i<n; i++)
{
getline(f, line); // Đọc từ dòng vào xâu line
istringstream fs(line); // Tạo stream fs từ xâu line
while (fs >> b) a[i].push_back( b ); // Đọc từng số từ stream fs
}
f.close(); // Đóng file
for (int i=0; i<a.size(); i++) // In các phần tử của mảng
{
for (int j=0; j<a[i].size(); j++) cout << a[i][j] << " ";
cout << endl;
}
getchar();
}
// Noi dung file readfile.txt
/*
3
1 2 3 4
5 6 7 8 9
10 11
*/
VIII.2. String stream
VIII.2.1. istringstream
VIII.2.2. ostringstream

VIII.3. Ghi có định dạng
C hỗ trợ đọc ghi theo định dạng (ví dụ: số lượng chữ số sau dấu phẩy) bằng cách sử dụng các tham
số % trong phần định dạng.
19
Lập trình C++ Nguyễn Phú Quảng
C++ cũng cho phép làm tương tự với thư viện iomanip (viết tắt của manipulate). Để sử dụng thư
viện này, bạn khai báo
#include <iomanip>
VIII.3.1. Ghi với số lượng dấu phẩy xác định
Nếu muốn ghi số thực với 3 chữ số sau dấu phẩy, bạn sử dụng lệnh setprecision. Chú ý
setpresision(n) sẽ ghi ra n-1 chữ số sau dấu phẩy (tính cả dấu chấm là n chữ).
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main () {
cout << setprecision(4) << (20.0/3) << endl; // Ghi ra 3 chữ số sau dấu phẩy 6.667
getchar();
}
VIII.3.2. Ghi với số lượng ký tự xác định
Bạn có thể sử dụng lệnh setw để xác định số lượng ký tự sẽ được ghi ra cho lần ghi tiếp theo. Căn lề
mặc định là bên phải. Nếu bạn muốn chuyển sang căn lề trái bạn sử dụng lệnh left. Chú ý: các lệnh
left và right sẽ căn lề trái và phải cho tất cả các lệnh viết sau đó.
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main () {
cout << setw(10) << 123 << setw(10) << 456 << endl; // 123 456
cout << setw(10) << 123 << 456 << endl; // 123456

cout << left << setw(10) << 123 << setw(10) << 456 << endl; // 123 456
getchar();
}
VIII.3.3. Ghi với hệ cơ số 8, 10, 16
Bạn có thể sử dụng lệnh setbase để đưa các số với các hệ cơ số khác nhau ra ostream. Các hệ cơ số
có thể sử dụng là 10, 16 và 8. Chú ý, lệnh setbase có tác dụng đối với tất cả các số được viết ra sau
lệnh này. Lệnh showbase sẽ hiển thị các số cùng với cách ghi trong C
- 123: cách ghi bình thường của số 123 ở hệ thập phân
- 0x7b: cách ghi hệ cơ số 16 (hexa) của số 123
- 0173: cách ghi hệ cơ số 8 của số 123
Sau khi ghi với hệ cơ số khác nhau kèm theo showbase, bạn có thể sử dụng noshowbase để tắt chế
độ này đi.
#include <iostream>
#include <stdlib.h>
#include <iomanip>
using namespace std;
int main () {
cout << setbase(16) << 123 << endl; // 7b
cout << 123 << endl; // 7b
cout << setbase(8) << 123 << endl; // 173
cout << showbase;
cout << setbase(16) << 123 << endl; // 0x7b
cout << setbase(10) << 123 << endl; // 123
cout << setbase(8) << 123 << endl; // 0173
cout << noshowbase;
cout << setbase(16) << 123 << endl; // 7b
getchar();
}
20
Lập trình C++ Nguyễn Phú Quảng

IX. KIỂU NGƯỜI DÙNG ĐỊNH NGHĨA
X. CẤP PHÁT BỘ NHỚ ĐỘNG
XI. LỚP
XII. MẪU
XIII. THƯ VIỆN CHUẨN
C++ được đánh giá là ngôn ngữ mạnh vì tính mềm dẻo, gần gũi với ngôn ngữ máy. Ngoài ra, với
khả năng lập trình theo mẫu, C++ đã khiến ngôn ngữ lập trình trở thành khái quát, không cụ thể và
chi tiết như nhiều ngôn ngữ khác. Với khái niệm mẫu, những người lập trình đã đề ra khái niệm lập
trình khái quát (generic programming), C++ được cung cấp kèm với một bộ thư viện chuẩn STL
(Standard Template Library). Bộ thư viện này thực hiện toàn bộ các công việc vào ra dữ liệu
(iostream), quản lý mảng (vector), thực hiện hầu hết các tính năng của các cấu trúc dữ liệu cơ bản
(stack, queue, map, set ). Ngoài ra, STL còn bao gồm các thuật toán cơ bản: tìm min, max, tính
tổng, sắp xếp (với nhiều thuật toán khác nhau), thay thế các phần tử, tìm kiếm (tìm kiếm thường và
tìm kiếm nhị phân), trộn. Toàn bộ các tính năng nêu trên đều được cung cấp dưới dạng mẫu nên
việc lập trình luôn thể hiện tính khái quát hóa cao, làm việc dễ dàng trên tất cả các kiểu dữ liệu. Nếu
bạn lập trình giải quyết các bài toán thiên về thuật toán, STL là sự lựa chọn đúng đắn, hợp lý cho
phép bạn giải bài toán rất nhanh chóng, loại bỏ được nhiều công sức trong việc cài đặt các thuật
toán cơ bản.
XIII.1. Các khái niệm
XIII.1.1. Container
Container (thùng chứa) là khái niệm chỉ các đối tượng lưu trữ các đối tượng (giá trị) khác. Đối
tượng container sẽ cung cấp các phương thức để truy cập các thành phần (element) của nó. Cụ thể
hơn, tất cả các container đều chứa các bộ lặp (iterator) để cho phép duyệt qua toàn bộ các element
của container.
Các container được phân loại theo tính chất thứ tự của các element, bao gồm các loại sau:
- Forward container
- Reversible container
- Random Access container
Một số container hay được sử dụng nhất gồm vector (tương tự như mảng), vector là Random access
container (người dùng có thể truy cập trực tiếp bất cứ phần tử nào trên vector).

XIII.1.2. Iterator
Iterator (bộ lặp) là khái niệm sử dụng để chỉ một con trỏ trỏ đến các đối tượng. Mỗi container có
một loại iterator khác nhau để trỏ đến các thành phần của container. Để iterator lần lượt trỏ đến từng
thành phần trong container, chúng ta sử dụng các toán tử tăng (++).
21
Lập trình C++ Nguyễn Phú Quảng
Khái niệm iterator cho phép bạn làm việc một cách tổng quát với bất kỳ một kiểu dữ liệu nào, từ
những kiểu dữ liệu truy cập ngẫu nhiên (vector) đến các ánh xạ (map), tập hợp (set), danh sách (list)
cho đến những kiểu dữ liệu đơn giản như mảng.
Do đó, iterator gắn liền với tất cả các loại container, đây là khái niệm bạn cần nắm rất vững nếu
muốn làm việc tốt với STL.
XIII.1.3. Một số phương thức cơ bản của Container
Container gồm nhiều loại, nhưng tất cả các container đều gồm các phương thức cơ bản sau
Phương thức Mô tả
a.begin() Trả về iterator bắt đầu của container
a.end() Trả về iterator cuối cùng của container
a.size() Kích thước (số lượng element) của container
a.max_size() Kích thước tối đa của container
a.empty() Trả về giá trị != 0 nếu container trống (không có element nào), 0 nếu
ngược lại
a.swap(b) Hoán đổi 2 container với nhau (giống việc hoán đổi giá trị của 2 biến
kiểu số)
XIII.2. Kiểu vector
Kiểu vector có thể coi là kiểu mảng trong lập trình C truyền thống. Mảng là tập hợp các giá trị cùng
kiểu, được sắp xếp nối tiếp nhau. Các phần tử của mảng có thể được truy cập ngẫu nhiên qua chỉ số.
Vấn đề đặt ra: Nếu vector là mảng thì tại sao lại phải sử dụng vector khi bạn đã quá quen thuộc với
mảng? Chúng tôi xin phân tích một số nhược điểm sau của mảng:
- Nếu bạn sử dụng mảng tĩnh: Mảng này luôn được khai báo với kích thước tối đa mà bạn có
thể dùng đến → tốn nhiều vùng nhớ thừa
- Nếu bạn sử dụng mảng động: Bạn phải xin cấp phát bộ nhớ, làm việc với con trỏ. Con trỏ là

khái niệm hay trong C, C++, nhưng nó là nguyên nhân của rất nhiều rắc rối trong lập trình.
- Nhược điểm quan trọng nhất: Nếu bạn sử dụng mảng vượt chỉ số vượt quá kích thước đã
khai báo, C++ sẽ không thông báo lỗi, điều này dẫn đến lỗi dây chuyền do các lệnh lỗi đã
tác động đến các biến khác trong chương trình (trong Pascal bạn có thể kiểm tra tràn chỉ số
mảng bằng dẫn biên dịch range check).
vector là một container cung cấp khả năng sử dụng mảng mềm dẻo, có kiểm soát range check khi
cần thiết, với kích thước tùy ý (mà không cần phải sử dụng con trỏ). Ngoài ra vector cho phép bạn
chèn thêm hoặc xóa đi một số phần tử chỉ bằng 1 lệnh (không phải sử dụng vòng lặp như đối với
mảng).
Để sử dụng vector, bạn phải khai báo file header với cú pháp
#include <vector>
Cú pháp khai báo vector như sau
vector<Kiểu> V;
22
Lập trình C++ Nguyễn Phú Quảng
XIII.2.1. Ví dụ 1
Trong đó, bạn có thể khai báo kiểu là bất cứ kiểu biến nào. Để hiểu rõ hơn về vector, bạn hãy theo
dõi ví dụ sau
#include <iostream> // Thư viện iostream phục vụ ghi dữ liệu ra màn hình
#include <vector> // Thư viện vector, sử dụng kiểu vector
#include <stdio.h> // Thư viện stdio (sử dụng hàm getchar() để dừng ct)
using namespace std; // Sử dụng namespace std
int main() {
vector<int> V; // V kiểu vector số nguyên (sử dụng giống mảng int)
V.resize(3); // Đặt kích thước của biến V là 3 (giống mảng 3 pt)
V[0] = 5; // Gán giá trị cho các phần tử của biến V
V[1] = 6; // Sử dụng dấu móc [] hoàn toàn giống với mảng
V[2] = 7;
for (int i=0; i<V.size(); i++) { // Ghi giá trị các phần tử của V ra màn hình
cout << V[i] << endl; // Nếu sử dụng mảng, bạn phải có biến lưu kích

} // thước, còn vector có hàm cho biến kích thước
getchar(); // Dừng chương trình để xem kết quả
}
Ví dụ trên cho bạn thấy việc sử dụng vector rất đơn giản, hoàn toàn giống với mảng nhưng bộ nhớ
được quản lý tự động, bạn không phải quan tâm đến giải phóng các vùng bộ nhớ đã xin cấp phát.
XIII.2.2. Ví dụ 2
Tuy nhiên, việc sử dụng vector không chỉ dừng lại ở những ưu điểm trên, chúng ta hãy nghiên cứu
ví dụ tiếp theo để thấy được các ưu điểm khác của vector.
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
int i;
vector<int> V;
for (i=0; i<5; i++) // Lặp 5 lần, mỗi lần đưa thêm 1 số vào vector
V.push_back(i); // Như vậy, vector có thể được sử dụng như stack
cout << endl << "Mang truoc khi insert" << endl;
for (i=0; i<V.size(); i++) // Ghi lại nội dung của mảng ra màn hình
cout << V[i] << endl;
V.insert( V.begin()+3, 100 ); // Chèn vào vị trí thứ 3 của vector giá trị 100
cout << endl << "Mang sau khi insert" << endl;
for (i=0; i<V.size(); i++) // In nội dung của vector sau khi chèn
cout << V[i] << endl; // vector sẽ có 6 phần tử, phần tử 100 chèn vào
// Vị trí thứ 3 của vector
V.erase( V.begin()+3 ); // Xóa phần tử vừa chèn vào đi
cout << endl << "Mang sau khi erase" << endl;
for (i=0; i<V.size(); i++) // In nội dung của vector sau khi xóa
cout << V[i] << endl; // Vector lại giống như lúc mới khởi tạo
getchar();

}
Với ví dụ trên, bạn có thể thấy những ưu điểm sau của vector
- Bạn có thể sử dụng vector như 1 stack. Thao tác push_back() của vector cho phép bạn thêm
1 giá trị mới vào vector. Thao tác này sẽ tự động tăng kích thước của vector, do đó bạn sẽ
không phải quan tâm đến quản lý kích thước của vector.
- vector cho phép bạn chèn thêm 1 phần tử vào các vị trí bất kỳ của vector, các phần tử nằm
sau vị trí này sẽ được dịch đi 1 vị trí đển lấy khoảng trống cho phần tử mới. Thao tác chèn
phần tử có thể thực hiện dễ dàng đối với mảng thường bằng cách sử dụng vòng lặp để
chuyển dịch các phần tử. Tuy nhiên, để chương trình hoạt động đúng trong mọi trường hợp,
bạn phải tính đến nhiều trường hợp: dung lượng hiện tại của mảng có đủ không? Nếu không
đủ phải xin cấp phát thêm Nếu sử dụng vector, các bạn sẽ dễ dàng thực hiện chức năng
này chỉ bằng 1 lệnh và luôn đảm bảo rằng lệnh này sẽ hoạt động chính xác.
23
Lập trình C++ Nguyễn Phú Quảng
- Tương tự với thao tác xóa một phần tử khỏi vector, bạn cũng chỉ cần sử dụng 1 lệnh.
XIII.2.3. Ví dụ 3
Chúng ta tiếp tục theo dõi ví dụ tiếp theo để tìm hiểu tiếp các khả năng của vector
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
// Mảng các xâu ký tự (dữ liệu test)
char *Chao[] = {"Xin", "chao", "tat", "ca", "cac", "ban"};
int n = sizeof(Chao)/sizeof(*Chao); // Tính kích thước của mảng
// Cách tính này sử dụng một mẹo:
// Kích thước của toàn bộ mảng chia cho
// Kích thước của 1 phần tử của mảng
vector<char *> V; // Vector chứa các xâu ký tự
// (cũng giống mảng, vector có thể chứa

// mọi loại giá trị)
int i;
cout << endl << "vector truoc khi xoa" << endl;
for (i=0; i<n; i++) {
V.push_back(Chao[i]); // Đưa các giá trị từ mảng vào vector
}

cout << endl << "vector sau khi xoa" << endl;
for (i=0; i<V.size(); i++) {
cout << V[i] << endl; // In ra các giá trị của vector
}

V.erase(V.begin()+2, V.begin()+2+3); // Xóa vector từ thành phần 2 đến
// thành phần thứ 3 sau thành phần 2
// Tức là xóa 3 thành phần kể từ
// thành phần 2
for (i=0; i<V.size(); i++) {
cout << V[i] << endl; // Lại in vector sau khi xóa ra màn hình
}
cout << endl; // In số lượng thành phần của vector
cout << "Vector truoc khi clear co " << V.size() << " thanh phan" << endl;
V.clear(); // Xóa toàn bộ các thành phần
cout << "Vector sau khi clear co " << V.size() << " thanh phan" << endl;
getchar(); // Lại in ra số lượng thành phần
}
Kết quả của đoạn chương trình trên như sau:
vector truoc khi xoa
Xin
chao
tat

ca
cac
ban
vector sau khi xoa
Xin
chao
ban
Vector truoc khi clear co 3 thanh phan
Vector sau khi clear co 0 thanh phan
Như vậy, chúng ta biết thêm một số chức năng của vector
- Vector không chỉ làm việc với số mà còn có thể làm việc với tất cả các dạng dữ liệu khác
(đây là đặc điểm của lập trình khái quát với template)
- Vector có thể xóa nhiều thành phần một lúc
- Vector có thế xóa tất cả các thành phần bằng phương thức clear().
24
Lập trình C++ Nguyễn Phú Quảng
XIII.2.4. Ví dụ 4
Còn một vấn đề đã được đề cập đến từ khi chúng tôi giới thiệu về vector, đó là khả năng kiểm tra
tràn chỉ số mảng (range check), để biết về khả năng này, chúng ta lại tiếp tục với một ví dụ mới
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
try { // sử dụng try catch để bẫy lỗi
vector<long> V(3, 10); // Khởi tạo vector gồm 3 thành phần
// Tất cả gán giá trị 10
cout << "V[0]=" << V[0] << endl; // Đưa thành phần 0 ra màn hình
cout << "V[1]=" << V[1] << endl; // Đưa thành phần 1 ra màn hình
cout << "V[2]=" << V[2] << endl; // Đưa thành phần 2 ra màn hình

cout << "V[3]=" << V[3] << endl; // Thành phần 3 (lệnh này hoạt động không
// đúng vì V chỉ có 3 thành phần 0,1,2
cout << "V[4]=" << V[4] << endl; // Thành phần 4 (càng không đúng)
// Nhưng 2 lệnh trên đều không gây lỗi

cout << "V[0]=" << V.at(0) << endl; // Không sử dụng [], dùng phương thức at
cout << "V[1]=" << V.at(1) << endl; // Thành phần 1, OK
cout << "V[2]=" << V.at(2) << endl; // Thành phần 2, OK
cout << "V[3]=" << V.at(3) << endl; // Thành phần 3: Lỗi, chương trình dừng
cout << "V[4]=" << V.at(4) << endl;
getchar();
} catch (exception &e) {
cout << "Co loi xay ra" << endl; // Dòng thông báo lỗi (nếu chương trình
getchar(); // phát sinh lỗi khi chạy
}
}
Kết quả của đoạn chương trình trên như sau
V[0]=10 < OK
V[1]=10 < OK
V[2]=10 < OK
V[3]=0 < Sai, ko có giá trị này
V[4]=196692 < Sai
V[0]=10 < OK
V[1]=10 < OK
V[2]=10 < OK
Co loi xay ra < Thông báo lỗi khi sử dụng phần tử không hợp lệ
Trong ví dụ này, chúng ta lại có thêm một số kinh nghiệm sau
- Có thể sử dụng cú pháp vector<kiểu> tên_vector(số_phần_tử, giá_trị_tất_cả_phần_tử) để
khởi tạo một vector có số lượng phần tử biết trước và tất cả các phần tử đều có cùng 1 giá trị
- Nếu sử dụng cú pháp biến_vector[chỉ_số], chương trình sẽ không tạo ra lỗi khi sử dụng chỉ

số mảng nằm ngoài vùng hợp lệ (giống như mảng thường). Trong ví dụ, chúng ta mới chỉ
lấy giá trị phần tử với chỉ số không hợp lệ, trường hợp này chỉ cho kết quả sai. Nhưng nếu
chúng ta gán giá trị cho phần tử không hợp lệ này, hậu quả sẽ nghiêm trọng hơn nhiều vì
thao tác đó sẽ làm hỏng các giá trị khác trên bộ nhớ.
- Phương thức at(chỉ_số) có tác dụng tương tự như dùng ký hiệu [], nhưng có một sự khác
biệt là thao tác này có kiểm tra chỉ số hợp lệ. Minh chứng cho nhận xét này trong ví dụ khi
chương trình chạy đến vị trí lệnh V.at(3), lệnh này không cho ra kết quả mà tạo thành thông
báo lỗi.
XIII.2.5. Ví dụ 5
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;
int main() {
typedef vector<long> vlong;
vector<vlong> V(3, vlong(3) );
int i, j;
for (i=0; i<3; i++)
25

×