Download as pdf or txt
Download as pdf or txt
You are on page 1of 483

KỸ THUẬT LẬP TRÌNH

Chương 1. Tổng quan


TS. Đào Duy Tuấn

1
Chương trình học
• Chương 1. Tổng quan
• Chương 2. Biến, biểu thức và toán tử
• Chương 3. Cấu trúc điều khiển
• Chương 4. Hàm
• Chương 5. Mảng và con trỏ
• Chương 6. Lập trình hướng đối tượng
• Chương 7. Lớp
• Chương 8. Quá tải
• Chương 9: Thừa kế 2
Nội dung chính
• A. Tổng quan.
• B. Cơ sở lập trình C++.
• C. Lập trình hướng đối tượng C++

3
Tài liệu tham khảo
• Osbone, “Borland C++ Builder: The Complete
Referent ”, McGraw Hill, 2001.
• Jarrod Hollingworth, Bob Swart, Mark Cashman, Paul
Gustavson , “Borland C++Builder 6 Developer's
Guide”, SAM, 2003.
• Charlie Calvert's, “Borland C++ Builder Unleashed”,
SAM.
• Frank B. Brokken, Karel Kubat, “C++ Annotations
Version 4.0.0”, University of Groningen, 1997
• Pham Van At, “C++ va Lap trinh huong doi tuong”,
NXB Khoa Hoc Ky Thuat, 2000.
• Jesse Liberty , “Teach Yourself C++ in 21 Days”, SAM,
• Absolute C++, 3rd edition, Walter Savitch, University of
California, San Diego
• Mot so bai giang cua dong nghiep o Dai Hoc Da Nang, 4
Dai Hoc Can Tho.
Môi trường lập trình
• Borland C++ 3.1 for DOS.
• Visual C++ 6.0, Win32 Console
Application
• Code::Block
• Sublime Text 3, cài đặt bộ gcc
• Dev-C ++

5
Môi trường lập trình
• Dev-C ++

6
Môi trường lập trình
• Dev-C ++

7
Môi trường lập trình
• Dev-C ++

8
A.Tổng quan

9
Lịch sử của C và C++
• Lịch sử của C
– Phát triển từ 2 ngôn ngữ khác : BCPL and B
– Dennis Ritchie (Phòng thí nghiệm Bell)
– Ngôn ngữ phát triển UNIX
– Độc lập với phần cứng
• Các chương trình khả chuyển
– 1989: tiêu chuẩn ANSI
– 1990: Phát hành tiêu chuẩn ANSI và ISO
• ANSI/ISO 9899: 1990
10
Lịch sử của C & C++
 Lịch sử của C
 Mục tiêu:
 Đề cao tính hiệu quả
 Có khả năng truy xuất phần cứng ở cấp thấp
 Ngôn ngữ có cấu trúc (thay cho lập trình bằng hợp ngữ)
 C là ngôn ngữ trung gian giữa cấp thấp…
 Có khả năng truy xuất bộ nhớ trực tiếp
 Cú pháp ngắn gọn, ít từ khoá
 … và cấp cao
 Không phụ thuộc phần cứng
 Cấu trúc, hàm, khả năng đóng gói
 Kiểm tra kiểu dữ liệu

11
Lịch sử của C và C++
• Lịch sử của C++
– Mở rộng của C
– Những năm đầu thập kỷ 1980:
• Bjarne Stroustrup (Phòng thí nghiệm Bell)
– Lý do cần thiết phải có C++: sự phức tạp, khó
quản lý trong các chương trình C
– Cung cấp khả năng lập trình hướng đối tượng
• Đối tượng: các thành phần phần mềm có thể
dùng lại
– Mô hình cho các vật thể trong thế giới thực
• Lập trình hướng đối tượng
– Dễ hiểu, dễ sửa lỗi và thay đổi
12
Lịch sử của C và C++

– Ngôn ngữ lai


• Đặc trưng của C
• Đặc trưng hướng đối tượng
•  Sự ra đời của C++
• C++ được thiết kế dành cho lập trình
chuyên nghiệp  không dễ học
• C++ : ngôn ngữ lập trình mạnh nhất từ
trước đến nay

13
Tại sao chọn C/C++
• Ưu điểm
– Hiệu quả
– Linh hoạt, khả năng tuỳ biến cao
– Được hỗ trợ rộng rãi trên các môi trường
khác nhau nhiều thư viện và công cụ sẵn có.
• Nhược điểm:
– Ngôn ngữ [quá] phức tạp
– Khó kiểm soát lỗi hơn so với các ngôn ngữ
bậc cao (Java, .NET, script,…), nhất là
nguyên nhân từ sử dụng con trỏ 14
Một vài điểm chú ý về ngôn ngữ
C/C++
 Cú pháp có phân biệt chữ hoa/thường: int, Int, INT là
hoàn toàn khác nhau
 Dấu ; dùng để phân tách các câu lệnh đơn
 Dấu { … } để quy định một khối câu lệnh
 Không được đặt tên biến/hằng/hàm… trùng với từ khoá
có sẵn (void, int, char, struct, const,…)
 Trong một khối lệnh không có cấu trúc định hướng (if,
for, while,…) thì các lệnh sẽ thực hiện tuần tự từ trên
xuống
 Chú thích:
 trong C bằng: /* … */
 trong C++ có thêm ký hiệu // để chú thích đến hết dòng
15
Thư viện tiêu chuẩn C++

• Các chương trình C++


– Xây dựng từ các mảnh nhỏ (các lớp và các
hàm)
• Thư viện tiêu chuẩn C++
– Tập hợp phong phú các lớp và các hàm có
sẵn
• Viết chương trình theo cách xây dựng các
khối
– Khả năng dùng lại

16
Một môi trường C++ điển hình
Các giai đoạn của
chương trình C++:
Primary
1. Soạn thảo Editor Disk
Loader
Memory

2. Tiền xử lý Preprocessor Disk


Disk
..
..

3. Biên dịch Compiler Disk ..

Primary
Memory
CPU
4. Liên kết Linker Disk

5. Tải vào bộ nhớ ..


..
..

6. Thực hiện
17
Một môi trường C++ điển hình
Các giai đoạn của
chương trình C++:
Primary
1. Soạn thảo Editor Disk
Loader
Memory

2. Tiền xử lý Preprocessor Disk


Disk
..
..

3. Biên dịch Compiler Disk ..

Primary
Memory
CPU
4. Liên kết Linker Disk

5. Tải vào bộ nhớ ..


..
..

6. Thực hiện
18
Một môi trường C++ điển hình
• Input/output
– cin
• Dòng vào chuẩn
• Thường là bàn phím
– cout
• Dòng ra chuẩn
• Thường là màn hình
– cerr
• Dòng lỗi chuẩn
• Trình bày các thông điệp lỗi 19
Biên dịch chương trình C/C++
• Là quá trình chuyển đổi từ mã nguồn (do
người viết) thành chương trình ở dạng mã
máy để có thể thực thi được

20
Kỹ thuật lập trình
 Kỹ thuật lập trình là gì: Kỹ thuật thực thi một giải pháp
phần mềm (cấu trúc dữ liệu + giải thuật) dựa trên nền
tảng một phương pháp luận (methodology) và một hoặc
nhiều ngôn ngữ lập trình phù hợp với yêu cầu đặc thù
của ứng dụng.
Kỹ thuật lập trình
= Tư tưởng thiết kế + Kỹ thuật mã hóa
= Cấu trúc dữ liệu + Giải thuật (hay còn gọi là thuật toán)
+ Ngôn ngữ lập trình
Kỹ thuật lập trình
≠ Phương pháp phân tích & thiết kế (A&D)

21
Thế nào là lập trình tốt?
• Giải đúng đề bài, được khách hàng chấp nhận
Tin cậy
— Chương trình chạy đúng
— Chạy ít lỗi (số lượng lỗi ít, cường độ lỗi thấp)
— Mức độ lỗi nhẹ
Hiệu suất
— Chương trình nhỏ gọn, sử dụng ít bộ nhớ
— Tốc độ nhanh, sử dụng ít thời gian CPU
Hiệu quả:
— Thời gian lập trình ngắn,
— Khả năng bảo trì dễ dàng
— Giá trị sử dụng lại lớn
— Sử dụng đơn giản, thân thiện
— Nhiều chức năng tiện ích. 22
Qui trình công nghệ phần mềm
 Phát triển phần mềm thường trải qua các giai đoạn
(phases) sau:
 Giai đoạn 1: Phân tích tính khả thi
 Giai đoạn 2: Phân tích và đặc tả yêu cầu
 Giai đoạn 3: Thiết kế
 Giai đoạn 4: Lập trình và sửa lỗi
 Giai đoạn 5: Kiểm thử
 Giai đoạn 6: Cài đặt và bảo trì
 Giai đoạn 7: Cải tiến, nâng cấp

23
Phân tích yêu cầu (Requirement
analysis)
• Bởi vì: Khách hàng thường không biết là họ muốn gì,
nhưng họ biết chắc chắn là họ không muốn gì?
• Cho nên: Cần phải cùng với khách hàng làm rõ
những yêu cầu về phạm chức năng, về giao diện sử
dụng.
• Kết quả: Mô hình đặc tả (Specification Model), một
phần của hợp đồng
• Cần một ngôn ngữ mô hình hóa dễ hiểu để trao đổi
giữa khách hàng và nhóm phân tích
=> Trả lời câu hỏi: Khách hàng cần những gì?

24
Phân tích hệ thống (System
analysis)
• Phân tích mối liên hệ của hệ thống với môi trường
xung quanh
• Định nghĩa chức năng cụ thể của các thành phần
• Nhận biết các đặc điểm của từng thành phần
• Phân loại các thành phần, tổng quát hóa, đặc biệt hóa
• Nhận biết mối liên hệ giữa các thành phần
• Kết quả: Mô hình hệ thống (System model)
• Cần một ngôn ngữ mô hình hóa để trao đổi giữa các
thành viên trong nhóm phân tích và với nhóm thiết kế
=> Trả lời câu hỏi: Những gì sẽ phải làm?

25
Thiết kế hệ thống (System
Design)
• Dựa trên mô hình hệ thống, xây dựng các mô hình chi
tiết phục vụ sẵn sàng mã hóa/cài đặt
• Bao gồm:
— Thiết kế cấu trúc (structured design): chương trình, kiểu dữ
liệu, đối tượng, quan hệ cấu trúc giữa các ₫ối tượng và kiểu)
— Thiết kế tương tác (interaction design): quan hệ tương tác
giữa các ₫ối tượng
— Thiết kế hành vi (behaviour design): sự kiện, trạng thái, phép
toán, phản ứng
— Thiết kế chức năng (funtional design): tiến trình hành ₫ộng,
hàm, thủ tục)
• Kết quả: Mô hình thiết kế (các bản vẽ và lời văn mô tả)
=> Trả lời câu hỏi: Làm như thế nào?

26
Phát triển phần mềm theo mô
hình thác nước

27
Lập trình là gì, nằm ở đâu?
• Lập trình => Mã hóa
• Lập trình ≈ Tư tưởng thiết kế + Mã hóa +
Thửnghiệm + sửa lỗi.

28
Khái niệm về chương trình và
lập trình
 Chương trình: tập các lệnh máy tính phải tuân theo
 Phần mềm: tập hợp các chương trình
 Hệ điều hành (HĐH): là một phần mềm cho phép người
dùng thao tác với máy tính quản lý tài nguyên (CPU, bộ
nhớ, phần cứng/mềm,…) chạy chương trình
 Thuật toán: chuỗi lệnh có thứ tự để giải quyết một
vấn đề nhất định
 Trình biên dịch: là chương trình cho phép chuyển đổi
mã nguồn thành mã máy

29
Ngôn ngữ lập trình
 Ngôn ngữ lập trình: Là ngôn ngữ được thiết kế dùng để
tạo ra những chương trình chạy trên máy tính theo ý đồ
mong muốn
 Quá trình phát triển:
 Mã máy: dùng trực tiếp mã nhị phân, không cần biên dịch, phụ thuộc và
bộ vi xử lý
 Thế hệ thứ 2 (hợp ngữ): cần biên dịch, có thể đọc hiểu được, phụ thuộc
và bộ vi xử lý
 Thế hệ thứ 3 (cấu trúc): cấu trúc điều khiển, kiểu dữ liệu, đóng gói. VD:
Fortran, C, C++, Basic, Pascal, COBOL,…
 Thế hệ thứ 4: nâng cao hiệu quả nhưng giảm các yếu tố dễ gây lỗi, cú
pháp gần gũi hơn với ngôn ngữ nói. VD: SQL, LabVIEW, ColdFusion,…

30
Mã lệnh và dữ liệu
• Máy tính chỉ hiểu mã nhị phân (byte, bit)
• Một mã lệnh có thể có mã 01000001
– Chữ cái ‘A’ có mã 01000001
– Số 65 có mã 01000001
• Làm thế nào máy tính hiểu 01000001 biểu diễn gì:
– Phụ thuộc vào mã lệnh đang chạy là gì
– Người viết chương trình phải hiểu vùng nhớ đang truy
xuất chứa gì
• Bộ nhớ trong của máy tính chứa cả dữ liệu và mã
lệnh của chương trình khi chạy

31
Lỗi chương trình
 Lỗi cú pháp (lỗi biên dịch):
 Do viết chương trình không tuân theo cú pháp quy định
 Được phát hiện bởi trình biên dịch
 Chú ý: đôi khi lỗi không được phát hiện vì bị hiểu sai sang cú
pháp khác
 Lỗi khi chạy (runtime errors):
 Khi chương trình chạy vi phạm những điều kiện cho phép
 Được phát hiện khi chạy
 Lỗi logic:
 Do thuật toán sai
 Máy tính không phát hiện

32
KỸ THUẬT LẬP TRÌNH
Chương 2. Biến, biểu thức và
toán tử

33
B. Cơ sở lập trình C++

1. Biến
2. Các toán tử
3. Các kiểu dữ liệu cơ bản

34
Chương Trình C++ Đầu Tiên
Hello.cpp
• Sử dụng bất kỳ trình #include <iostream>
soạn thảo nào int main (void)
{

• Lưu file }
cout << "Hello World\n";

• Biên dịch.
• Chạy chương trình
C++ C
C
Hello.cpp Program
C++
TRANSLATOR
Code
COMPILER

C++ C++ Object


Program NATIVE Code Hello.obj
COMPILER

Execut-
LINKER able Hello.exe

35
Chương Trình C++ Đầu Tiên
Hello.cpp
#include <iostream>
int main (void)
{
cout << "Hello World\n";
}

36
Chương Trình C++ Đầu Tiên

C++ C
C
Hello.cpp Program
C++
TRANSLATOR
Code
COMPILER

C++ C++ Object


Program NATIVE Code Hello.obj
COMPILER

Execut-
LINKER able Hello.exe

37
Biến
• Biến
– Tên tượng trưng cho một vùng nhớ mà dữ liệu có thể
được lưu trữ trên đó hay là được sử dụng lại.
– Phải khai báo biến trước khi sử dụng.
– Có thể khai báo biến ở mọi vị trí trong chương trình
• Thuộc tính của biến
– Kiểu: được thiết lập khi các biến được định nghĩa
– Giá trị: có thể được chuyển đổi bằng cách gán một
giá trị mới cho biến

38
Biến
Ví dụ
int i;
Biến int j, k;
unsigned char dem;
float ketqua, delta;

Cú pháp
<kiểu> <tên biến>;
<kiểu> <tên biến 1>, <tên biến 2>;

39
Chương Trình C++

40
Hằng số
Cú pháp
#define <tênhằng> <giá trị>
Hằng hoặc sử dụng từ khóa const.
ký hiệu

Ví dụ
#define MAX 100 // Không có ;
#define PI 3.14 // Không có ;
const int MAX = 100;
const float PI = 3.14;
41
Hằng số

42
Khai Báo Biến

#include <iostream>

int main()
{
int x; // khai báo biến
x = 8; // khởi tạo biến
int y = 6; // Khai báo và khởi tạo biến
std::cout << std::endl;
std::cout << x - y << " " << x*y << " " << x + y;
std::cout << std::endl;
return 0;
}

Kết quả: 2 48 14

43
Khai Báo Biến
Bài tập: Tính số tiền trả cho 1 tuần
#include <iostream> #include <iostream>
int main (void) int main (void)
{ {
int ngayLamViec; int ngayLamViec = 5;
float gioLamViec, float gioLamViec = 7.5;
soTienTraCho1Gio, float soTienTraCho1Gio =
soTienTraCho1Tuan; 38.55;
ngayLamViec = 5; float soTienTraCho1Tuan =
gioLamViec = 7.5; ngayLamViec * gioLamViec *
soTienTraCho1Gio = 38.55; soTienTraCho1Gio;
soTienTraCho1Tuan =
ngayLamViec*gioLamViec* cout << " So tien phai tra cho
soTienTraCho1Gio; 1 tuan";
cout << “So tien phai tra cho cout << soTienTraCho1Tuan;
1 tuan =“ << cout << '\n’;
soTienTraCho1Tuan << '\n’;
return 0;
return 0; }
} Khai báo và khởi tạo biến
Khởi tạo biến 44
Khai báo biến
Xuất Nhập Đơn Giản
#include <iostream>
using namespace std; #include <iostream>
using namespace std;
int main (void)
{ int main (void)
int ngayLamViec = 5; {
float gioLamViec = 7.5; int ngayLamViec = 5;
float soTienTraCho1Gio, float gioLamViec,
soTienTraCho1Tuan; soTienTraCho1Gio,
cout << “So tien phai tra cho soTienTraCho1Tuan;
1 gio? "; cout << " So giolam viec va So
cin >> soTienTraCho1Gio; tien phai tra cho 1 gio? ";
soTienTraCho1Tuan = cin >> gioLamViec >>
ngayLamViec * gioLamViec * soTienTraCho1Gio;
soTienTraCho1Gio; soTienTraCho1Tuan = ngayLamViec
cout << “So tien phai tra cho *gioLamViec * soTienTraCho1Gio;
1 tuan = "; cout << " So tien phai tra cho 1
cout << soTienTraCho1Tuan; tuan " << soTienTraCho1Tuan <<
cout << '\n’; '\n’;
return 0; return 0;
} }

45
Tìm lỗi của chương trình sau

46
Tìm lỗi của chương trình sau
// C++ program to illustrate
// syntax error

#include <iostream>
using namespace std;

int main()
{
int x = 5;
int y = 6;
cout << " "<< (x, y)

}
47
Điền câu lệnh còn thiếu để tính
diện tích hình tròn

float area = PI * rad * rad;

48
Chú Thích
Chú thích nhiều hàng

1 #include <iostream>

2 /* Chuong trinh nay tinh toan tong so tien phai tra hang tuan cho mot
3 cong nhan dua tren tong so gio lam viec va so tien phai tra moi
4 gio. */
5 int main (void)
6 {
7 int ngayLamViec = 5; // so ngay lam viec trong tuan
8 float gioLamViec = 7.5; // so gio lam viec trong ngay
9 float soTienTraCho1Gio = 38.50; // so tien phai tra moi gio
10 float soTienTraCho1Tuan; // tong so tien phai tra moi tuan
11 soTienTraCho1Tuan = ngayLamViec * gioLamViec * soTienTraCho1Gio;
12 cout << “So tien phai tra trong 1 tuan = " << soTienTraCho1Tuan <<
13 '\n';
}

Chú thích một hàng


49
Tên
• Tên
– 1 dãy kí tự bắt đầu bằng chữ hoặc ký tự gạch
dưới, theo sau là chữ cái, chữ số.
– còn được gọi là định danh
– được sử dụng để tham khảo
– tên biến, tên hàm, tên kiểu, và tên macro
– phải được đặt theo luật
– không giới hạn số ký tự
– không được đặt trùng từ khóa

50
Tên
• Tên
– 1 dãy kí tự bắt đầu bằng chữ hoặc ký tự gạch
dưới, theo sau là chữ cái, chữ số.
– còn được gọi là định danh
– được sử dụng để tham khảo
– tên biến, tên hàm, tên kiểu, và tên macro
– phải được đặt theo luật
– không giới hạn số ký tự
– không được đặt trùng từ khóa

51
Từ khóa
Các từ khóa dành chung cho chương trình C lẫn C++
auto break case char const
continue default do double else
enum extern float for goto
if int long register return
short signed sizeof static struct
switch typedef union unsigned void
volatile while

Các từ khóa dành cho C++


asm bool catch class const_cast
delete dynamic_cast explicit false friend
inline mutable namespace new operator
private protected public reinterpret_cast
static_cast template this throw true
try typeid typename using virtual
wchar_t

52
Biểu thức và các toán tử

53
Biểu thức (Expression)
• Một biểu thức là bất kỳ sự tính toán nào
mà cho ra một giá trị.
• Một biểu thức ước lượng một giá trị nào
đó.

54
Biểu thức
• Khái niệm
– Tạo thành từ các toán tử (Operator) và các
toán hạng (Operand).
– Toán tử tác động lên các giá trị của toán hạng
và cho giá trị có kiểu nhất định.
– Toán tử toán học: +, –, *, /, %….
– Toán hạng: hằng, biến, lời gọi hàm...
• Ví dụ
– 2 + 3,
– a / 5,
55
– (a + b) * 5, …
Chương Trình C++

56
Các toán tử
• Toán tử gán (=).
– Dùng để gán một giá trị nào đó cho một biến.
– Ví dụ gán giá trị nguyên 5 cho biến a: a=5;
– Vế trái bắt buộc phải là một biến còn vế phải có thể là
bất kì hằng, biến hay kết quả của một biểu thức.
– Toán tử gán luôn được thực hiện từ trái sang phải và
không bao giờ đảo ngược
– Ví dụ: a = b;
• gán giá trị của biến a bằng giá trị đang chứa trong biến b.
Chú ý rằng chúng ta chỉ gán giá trị của b cho a và sự thay
đổi của b sau đó sẽ không ảnh hưởng đến giá trị của a.

57
Các toán tử
• Chú ý:
– Toán tử gán trong C++ cho phép vế phải có thể chứa
các phép gán khác.
– Ví dụ:
a = 2 + (b = 5);
tương đương với
b = 5;
a = 2 + b;
– Vì vậy biểu thức sau cũng hợp lệ trong C++
a = b = c = 5;
– gán giá trị 5 cho cả ba biến a, b và c

58
Các toán tử
• Các toán tử số học
– Năm toán tử số học được hỗ trợ bởi ngôn ngữ là:
• +: cộng
• -: trừ
• *: nhân
• /: chia
• %: lấy phần dư (trong phép chia)
• Thứ tự thực hiện giống như trong toán học.
– Dịch trái, dịch phải số nguyên A đi B bit: A<<B, A>>B.
– Toán tử bit: AND &, OR |, XOR ^ và NOT ~.
– Hỏi
59
Các toán tử trên bit
• Các toán tử trên bit
– Tác động lên các bit của toán hạng (nguyên).
– & (and), | (or), ^ (xor), ~ (not hay lấy số bù 1)
– >> (shift right), << (shift left)
– Toán tử gộp: &=, |=, ^=, ~=, >>=, <<=

60
61
62
63
64
Các toán tử trên bit
• Ví dụ
void main()
{
int a = 5; // 0000 0000 0000 0101
int b = 6; // 0000 0000 0000 0110

int z1, z2, z3, z4, z5, z6;


z1 = a & b; // 0000 0000 0000 0100
z2 = a | b; // 0000 0000 0000 0111
z3 = a ^ b; // 0000 0000 0000 0011
z4 = ~a; // 1111 1111 1111 1010
z5 = a >> 2;// 0000 0000 0000 0001
z6 = a << 2;// 0000 0000 0001 0100
}
65
66
Ví dụ

67
Các toán tử
• Các toán tử gán phức hợp (+=, -=, *=, /=, %=,
>>=, <<=, &=, ^=, |=)
– Một đặc tính làm cho C++ nổi tiếng là một ngôn ngữ
súc tích
– value += increase; tương đương với value = value
+ increase;
a -= 5; tương đương với a = a - 5;
a /= b; tương đương với a = a / b;
price *= units + 1; tương đương với price = price *
(units + 1);
– và tương tự cho tất cả các toán tử khác.
68
Các toán tử

69
Các toán tử
• Tăng và giảm (toán tử 1 ngôi).
– Toán tử tăng (++) và giảm (--) giá trị chứa trong một
biến đi 1.
– Tương đương với +=1 hoặc -=1. Vì vậy, các dòng
sau là tương đương:
a++;
a+=1;
a=a+1;
– Chú ý: (++a, hay còn gọi là tiền tố) giá trị được tăng
trước khi biểu thức được tính và giá trị đã tăng được
sử dụng trong biểu thức; (a++, hay còn gọi là hậu
tố) giá trị trong biến a được tăng sau khi đã tính toán.

70
Các toán tử
• Ví dụ 1
B=3;
A=++B;
// A is 4, B is 4
• Ví dụ 2
B=3;
A=B++;
// A is 3, B is 4
F=++(a++-++b/--a-b++)/(b++---a);
71
Ví dụ

72
Ví dụ

73
Các toán tử
• Toán tử 2 ngôi
– Có hai toán hạng trong biểu thức.
– +, –, *, /, % (chia lấy phần dư)
– x = x + y  x += y;
• Ví dụ
– a = 1 + 2; b = 1 – 2; c = 1 * 2; d = 1 / 2;
– e = 1*1.0 / 2; f = float(1) / 2; g = float(1 / 2);
– h = 1 % 2;
– x = x * (2 + 3*5);  x *= 2 + 3*5;
74
Các toán tử
• Các toán tử quan hệ ( ==, !=, >, <, >=, <= )
– So sánh hai biểu thức với nhau.
– Kết quả là giá trị logic - chỉ có thể có giá trị true hoặc
false, tuỳ theo quan hệ giữa vế trái và vế phải.
– Các toán tử quan hệ trong C++
• ==Bằng
• !=Khác
• >Lớn hơn
• <Nhỏ hơn
• > =Lớn hơn hoặc bằng
• < =Nhỏ hơn hoặc bằng

75
Các toán tử
• Các toán tử logic ( !, &&, || ).
– Toán tử ! tương đương với toán tử logic NOT, đổi
ngược giá trị của đối số từ true sang false hoặc
ngược lại.
– Ví dụ:
• !(5 == 5) trả về false vì biểu thức bên phải (5 == 5) có giá trị
true.
• !(6 <= 4) trả về true vì (6 <= 4) có giá trị false.
• !true trả về false.
• !false trả về true.
– Toán tử logic && và || được sử dụng khi tính toán hai
biểu thức để lấy ra một kết quả duy nhất. Tương ứng
với các toán tử logic AND và OR. Kết quả của chúng
phụ thuộc vào mối quan hệ của hai đối số.
76
Các toán tử

77
Các toán tử
• Ví dụ x = 3, y =4;

78
Các toán tử
• Toán tử điều kiện ( ? : ).
– Toán tử điều kiện tính toán một biểu thức và trả về
một giá trị khác tuỳ thuộc vào biểu thức đó là đúng
hay sai. Cấu trúc của nó như sau:
condition ? result1 : result2
– Nếu condition là true thì giá trị trả về sẽ là result1,
nếu không giá trị trả về là result2.
• 7==5 ? 4 : 3 trả về 3 vì 7 không bằng 5.
• 7==5+2 ? 4 : 3 trả về 4 vì 7 bằng 5+2.
• 5>3 ? a : b trả về a, vì 5 lớn hơn 3.
• a>b ? a : b trả về giá trị lớn hơn, a hoặc b.

79
Ví dụ

80
Thứ tự ưu tiên của các toán tử
Mức Toán tử Thứ tự
Cao nhất :: Cả hai
() [] -> . Trái tới phải
+ ++ ! * new sizeof Phải tới trái
- -- ~ & delete ()
->* .* Trái tới phải
* / % Trái tới phải
+ - Trái tới phải
<< >> Trái tới phải
< <= > >= Trái tới phải
== != Trái tới phải
& Trái tới phải
^ Trái tới phải
| Trái tới phải
&& Trái tới phải
|| Trái tới phải
? : Trái tới phải
= += *= ^= &= <<= Phải tới trái
-= /= %= |= >>= 81
Thấp nhất , Trái tới phải
Độ ưu tiên của các toán tử
• Quy tắc thực hiện
– Thực hiện biểu thức trong ( ) sâu nhất trước.
– Thực hiện theo thứ tự ưu tiên các toán tử.
=> Tự chủ động thêm ( )
• Ví dụ
– n = 2 + 3 * 5;
=> n = 2 + (3 * 5);
– a > 1 && b < 2
=> (a > 1) && (b < 2) 82
Viết biểu thức cho các mệnh đề
• x lớn hơn hay bằng 3
x >= 3
• a và b cùng dấu
?????
• p bằng q bằng r
?????
• –5 < x < 5
??????
Đáp án
83
Viết biểu thức cho các mệnh đề
• x lớn hơn hay bằng 3
x >= 3
• a và b cùng dấu
((a>0) && (b>0)) || ((a<0) && (b<0))
(a>0 && b>0) || (a<0 && b<0)
• p bằng q bằng r
(p == q) && (q == r) hoặc (p == q && q == r)
• –5 < x < 5
(x > –5) && (x < 5) hoặc (x > –5 && x < 5) 84
Các toán tử
• Toán tử điều kiện, phẩy, lấy kích thước

Toán tử điều kiện min = (m < n ? m : n);

Toán tử phẩy min = (m < n ? mCount++, m : nCount++, n);

Toán tử lấy kích thước cout << "float size = " << sizeof(float) << " bytes\n";

85
Lệnh
• Khái niệm
– Là một chỉ thị trực tiếp, hoàn chỉnh nhằm ra
lệnh cho máy tính thực hiện một số tác vụ
nhất định nào đó.
– Trình biên dịch bỏ qua các khoảng trắng (hay
tab hoặc xuống dòng) chen giữa lệnh.
• Ví dụ
a=2912;
a = 2912;
a
=
2912; 86
Lệnh
 Mục tiêu
- Cung cấp cú pháp và cách sử dụng các
lệnh
 Nội dung
- Lệnh đơn, lệnh phức
- Lệnh khai báo
- Lệnh gán
- Lệnh rẽ nhánh: if, switch
- Lệnh lặp: while, do..while, for
- Lệnh nhảy: continue, break, goto 87
Lệnh
• Lệnh đơn là một Ví dụ:
sự tính toán được
kết thúc bằng dấu
{
chấm phẩy. int min, i = 10, j = 20;
min = (i < j ? i : j);
min + 5;
• Nhiều lệnh đơn có cout << min << '\n';
thể kết nối lại ;
thành một lệnh }
phức (khối lệnh)
bằng cách rào
chúng bên trong Lệnh rỗng Lệnh vô dụng
các dấu ngoặc
xoắn. 88
Lệnh và khối lệnh

89
Lệnh Rẽ Nhánh
• Lệnh if và if-else • Lệnh switch

90
Lệnh Lặp
• Lệnh while; do-while • Lệnh for

91
Các kiểu dữ liệu cơ bản

92
Các kiểu dữ liệu cơ bản
• Kiểu char:
– Biễu diễn 1 ký tự thuộc ASCII ( thực chất là số nguyên từ 0 đến
255)
– Ví dụ : Ký tự ASCII
• 0 048
• A 065
• a 097
• Kiểu int:
– 3 loại : int, long int ( long ) và unsigned int ( unsigned).
• Kiểu float:
– Biểu diễn các số thực độ chính xác đon.
• Kiểu double:
– Biểu diễn các số thực độ chính xác kép.
93
Các kiểu dữ liệu cơ bản
• Phạm vi, kích thước kiểu dữ liệu:
1. char 0..255 1byte
2. int -32768..32767 2bytes
3. long -2147483648..2147484647 4bytes
4. unsigned 0..65535 2bytes
5. float 3.4e - 38..3.4e + 38 4bytes
6. double 1.7e - 308 .. 1.7e + 308 8bytes
• Kiểu void:
– Kiểu không giá trị, dùng để biểu diễn kết quả hàm
cũng như nội dung của pointer.
94
In kích thước

95
Kiểu số nguyên
• Các kiểu số nguyên (không dấu)
– n bit không dấu: 0 … 2n – 1

Kiểu Độ lớn Miền giá trị


(Type) (Byte) (Range)
unsigned char 1 0 … 255
unsigned int 2 0 … 65.535
unsigned short 2 0 … 65.535
unsigned long 4 0 … 4.294.967.295

96
Kiểu số thực
• Các kiểu số thực (floating-point)
– Ví dụ
• 17.06 = 1.706*10 = 1.706*101
Kiểu Độ lớn Miền giá trị
(Type) (Byte) (Range)
float (*) 4 3.4*10–38 … 3.4*1038
double (**) 8 1.7*10–308 … 1.7*10308

• (*) Độ chính xác đơn (Single-precision) chính xác


đến 7 số lẻ.
• (**) Độ chính xác kép (Double-precision) chính xác
đến 19 số lẻ. 97
Kiểu luận lý
• Đặc điểm
– C ngầm định một cách không tường minh:
• false (sai): giá trị 0.
• true (đúng): giá trị khác 0, thường là 1.
– C++: bool
• Ví dụ
– 0 (false), 1 (true), 2 (true), 2.5 (true)
– 1 > 2 (0, false), 1 < 2 (1, true)

98
Cho biết kết quả bài tập?

99
Kiểu ký tự
• Đặc điểm
– Tên kiểu: char
– Miền giá trị: 256 ký tự trong bảng mã ASCII.
– Chính là kiểu số nguyên do:
• Lưu tất cả dữ liệu ở dạng số.
• Không lưu trực tiếp ký tự mà chỉ lưu mã ASCII của
ký tự đó.
• Ví dụ
– Lưu số 65 tương đương với ký tự ‘A’…
– Lưu số 97 tương đương với ký tự ‘a’. 100
Chuyển kiểu đơn giản

 Một giá trị thuộc về những kiểu xây dựng sẵn mà chúng
ta biết đến thời điểm này đều có thể được chuyển về bất
kỳ một kiểu nào khác. Ví dụ:
 (int) 3.14 // chuyển 3.14sangintđểđược 3
 (long) 3.14 // chuyển 3.14sanglongđểđược 3L
 (double) 2 // chuyển 2sangdoubleđểđược 2.0
 (char) 122 //chuyển122sang char cómã
là122(unsignedshort)3.14 //được 3nhưlàmột
unsignedshort

101
Ví dụ về chuyển kiểu đơn giản

102
Ví dụ về chuyển kiểu đơn giản

103
Sử dụng cout
• cout là một đối tượng được định nghĩa trước trong C++, tương ứng
với luồng ra chuẩn (standard output stream).
• Toán tử << là toán tử chèn, định hướng nội dung cần hiển thị.
• Ví dụ:
– char str[ ]=”Hello world”;
– int i=8;
– cout << str << endl; // endl (hoặc \n) là dấu hiệu xuống dòng.
– cout << ”i=” << i << endl;
• Để định dạng nội dung hiển thị, sử dụng setw (nằm trong iomanip.h)
để thiết lập đồ dài ký tự cho biến kế sau.
– cout<< setw(12) << str << setw(5) << i;
– Kết quả: Hello world 8

104
Sử dụng cin
• cin là toán tử được định nghĩa trước trong C++,
tương ứng với luồng vào chuẩn (standard input
stream).
• Toán tử >> đưa nội dung từ luồng vào chuẩn
vào biến.
• Ví dụ:
– int temperature;
– cout << ”Enter temperature in Celsius: ”;
– cin >> temperature;

105
Nhập xâu ký tự
• C++ không có kiểu dữ liệu cơ bản để lưu các xâu kí tự
 Mảng kiểu char.
• Ví dụ:
– char jenny [20];
– char st[]=“Hello”;
• Nhập xâu:
– cin.getline ( char buffer[], int length, char delimiter = \n );
• buffer (bộ đệm) lưu trữ xâu, length: độ dài cực đại của bộ đệm , delimiter: kí
tự được dùng để kết thúc việc nhập, mặc định - là kí tự xuống dòng ( \n ).

• Ví dụ:
– char mybuffer [100];
cout << "What s your name? ";
cin.getline (mybuffer,100);
cout << "Hello " << mybuffer << ".\n";
106
Header File và Library File
• Một số nhiệm vụ được thực hiện bởi Library Function.
• Header File chứa khai báo các hàm mà ta cần sử dụng
trong chương trình.
• Ví dụ:
• #include <math.h>
• #include ”myprog.h”
– <>: yêu cầu chương trình dịch tìm từ thư mục chuẩn.
– ” ” : yêu cầu chương trình dịch tìm từ thư mục hiện tại.
• Nếu không include Header File thích hợp thì chương
trình dịch sẽ báo lỗi.

107
library header file
#include <somelib.h>
somelib.h
myprog.cpp
#include ”myprog.h” user header file
myprog.h

compiler
object file library file

myprog.obj cs.lib
linker
executable file

myprog.exe
108
Bài tập
1) Viết chương trình C++ để in các dòng sau:
Toi đang là sinh viên.
Toi co nhieu uoc mo.
2) Viết chương trình C++ để khai báo hai biến nguyên, một biến
thực và gán các giá trị tương ứng 100, 105 và 12.5 cho chúng. In
kết quả.
3) Tìm tổng, hiệu, tích và thương của hai số nguyên nhập từ bàn
phím và in kết quả ra màn hình.
4) Nhập hai số nguyên từ bàn phím, tính tổng, trung bình cộng
của chúng và in ra màn hình.

109
Các kiểu dữ liệu tự định nghĩa

110
Cấu trúc (struct)
• Một cấu trúc dữ liệu là một tập hợp của những
kiểu dữ liệu khác nhau được gộp lại với một cái
tên duy nhất.
• Dạng thức
struct <tên_kiểu_cấu_trúc>
{
//Khai báo các thành phần cấu trúc
//…
} <tên_cấu trúc>;
• Khai báo biến, mảng cấu trúc:
tên_kiểu_cấu_trúc danh sách biến, mảng cấu trúc;

111
Cấu trúc (struct)
• Ví dụ:
struct thi_sinh
{
char hoten[25];
long sobaodanh;
float diemToan, diemLy, diemHoa, trungbinh;
};
thi_sinh ts1, bachkhoa[3000], kinhte[2000], caodang[1000];
• Hoặc
struct thi_sinh
{
char hoten[25];
long sobaodanh;
float diemToan, diemLy, diemHoa, trungbinh;
} bachkhoa[2000], kinhte[2000], caodang[2000];

• Truy cập:
bachkhoa[5].hoten
kinhte[12].diemToan

112
Hợp (union)
• Union cho phép một phần bộ nhớ có thể được truy xuất
dưới dạng nhiều kiểu dữ liệu khác nhau.
• Tất cả các phần tử của union đều chiếm cùng một chỗ
trong bộ nhớ.
• Kích thước của nó là kích thước của phần tử lớn nhất.
• Dạng thức
union <tên_kiểu_hợp>
{
//Khai báo các thành phần của hợp
//…
} <tên_hợp>;
• Khai báo biến, mảng Union :
tên_kiểu_hợp danh sách biến, mảng hợp;
113
Hợp (union)
• Ví dụ:
union mytypes_t
{
char c;
int i;
float f;
} mytypes;
– định nghĩa ba phần tử: mytypes.c, mytypes.i,
mytypes.f chiếm cùng chỗ trong bộ nhớ, kích thước 4
bytes (float).

114
Hợp (union)
• Các union vô danh
Ví dụ Union: Ví dụ Union vô danh:
struct { struct {
char title[50]; char title[50];
char author[50]; char author[50];
union { union {
float dollars; float dollars;
int yens; int yens;
} price; };
} book; } book;
Truy cập: Truy cập:
book.price.dollars book.dollars 115
book.price.yens book.yens
Kiểu liệt kê (enum)
• Kiểu dữ liệu liệt kê dùng để tạo ra các kiểu dữ
liệu chứa một cái gì đó hơi đặc biệt.
• Dạng thức:
enum <kiểu_liệt_kê>
{
//Khai báo các giá trị trong kiểu liệt kê
} <tên_biến_liệt_kê>;
• Khai báo:
kiểu_liệt_kê danh sách biến, mảng liệt kê;
• Ví dụ:
enum colors_t {black, blue, green, cyan, red, purple,
yellow, white};

116
Kiểu liệt kê (enum)
• Chú ý: không sử dụng bất kì một kiểu dữ liệu trong phần khai báo.
Ta đã tạo ra một kiểu dữ liệu mới mà không dựa trên kiểu dữ liệu
có sẵn.
• Khai báo và sử dụng
colors_t mycolor;
mycolor = blue;
if (mycolor == green) cout <<“mau xanh luc”;
• Kiểu liệt kê được dịch là một số nguyên và các giá trị của nó là các
hằng số nguyên được chỉ định. Mặc định giá trị nguyên tương
đương với phần tử đầu tiên là 0 và các giá trị tiếp theo cứ thế tăng
lên 1. Vì vậy, black ~ 0, blue ~ 1, green ~ 2 …
– enum {black=3, blue=5, green, cyan, red, purple, yellow, white} color;
• green=?
• white=?

117
Kiểu dữ liệu tự định nghĩa (typedef)
• C++ cho phép chúng ta định nghĩa các kiểu dữ
liệu của riêng mình dựa trên các kiểu dữ liệu đã
có.
• Dạng thức:
– typedef kiểu_dữ_liệu tên_kiểu_mới;
• Ví dụ
Khai báo: Sử dụng:
typedef char C; C achar,
anotherchar,
typedef unsigned int WORD;
*ptchar1;
typedef char * string_t; WORD myword;
string_t ptchar2;
typedef char field [50]; 118
field name;
Bài tập về enum

119
Đáp án
Bài tập về enum

120
KỸ THUẬT LẬP TRÌNH
Chương 3. Cấu trúc điều khiển

121
Cấu trúc điều khiển
• Khái niệm
– Trước khi viết chương trình
• Hiểu thông suốt vấn đề.
• Lập kế hoạch một cách kỹ lưỡng trong phương án
của mình
– Trong khi viết chương trình
• Hiểu tốt từng phần trong chương trình.
• Vận dụng tốt các nguyên lý lập trình.

122
Cấu trúc điều khiển
• Giải thuật
– Vấn đề
• Giải quyết vấn đề bằng cách thực hiện các hoạt
động theo một trình tự xác định.
– Xác định thuật toán
• Thực thi hoạt động
• Thực thi trình tự
– Điều khiển chương trình
• Xác định trình tự thực hiện các câu lệnh
(statements).

123
Cấu trúc điều khiển
• Mã giả
– Ngôn ngữ dễ hiểu để phát triển thuật toán.
– Tương tự như tiếng Anh hằng ngày.
– Không thực hiện được trên máy tính
– Được sử dụng để suy nghĩ trước khi viết mã.
– Dễ dàng chuyển đổi sang ngôn ngữ C++
– Chỉ thực hiện các câu lệnh
• Không tiến hành các khai báo.

124
Cấu trúc điều khiển
• Các cấu trúc điều khiển
– Cấu trúc tuần tự:
• Chương trình được thực hiện theo trình tự mặc
định (default).
– Cấu trúc lựa chọn (điều kiện):
• if, if/else, switch
– Cấu trúc lặp:
• while, do/while, for

125
Cấu trúc điều khiển
• Lưu đồ:
– Đồ thị biểu diễn thuật toán.
– Gồm các ký hiệu xác định nối với nhau bằng các mũi
tên.
– Ký hiệu hình chữ nhật: ký hiệu hoạt động, xử lý, tính
toán
– Ký hiệu oval: bắt đầu, kết thúc chương trình hoặc một
phần nào đó của chương trình.
• Cấu trúc điều khiển một ngõ vào/một ngõ ra
– Ngõ ra của điểm này được nối với ngõ vào của điểm
kia.
– Cấu trúc điều khiển ngăn xếp.
126
Cấu trúc điều khiển
Ký hiệu Ý nghĩa
Bắt đầu, kết thúc chương
Start End
trình
Luồng xử lý

X>=y? Điều khiển chọn lựa

Nhập n In k Nhập, xuất

Y=a.x+b.y+c.z Xử lý, tính toán, gán

A
Điểm nối liên kết (khi lưu đồ
vượt quá trang) 127
Cấu trúc trình tự
• Thực thi tiến trình theo • Lưu đồ:
tuần tự.
• Các lệnh được thực thi Start
theo thứ tự từ trên xuống
dưới, hết lệnh này đến
Nhập a, b, c
lệnh khác.
• Ví dụ: Nhập vào 3 số
nguyên a, b, c và xuất ra a++; b++; c++
màn hình với giá trị của
mỗi số đã tăng lên 1. Xuất a, b, c

End 128
Cấu trúc lựa chọn if
• Chọn họat động theo điều kiện
• Ví dụ mã giả:
If student’s grade is greater than or equal to 6.0
Print “Passed” điều kiện
– Nếu điều kiện là true
• Lệnh Print được thực hiện, chương trình tiếp tục với lệnh kế
sau
– Nếu điều kiện là false
• Lệnh Print được bỏ qua, chương trình tiếp tục
– Thường các lệnh trong if được viết thụt vào để dễ đọc
• C++ bỏ qua các khoảng trắng (tabs, spaces,...)

129
Cấu trúc lựa chọn if
• Chuyển sang C++
if ( grade >= 6.0 )
cout << "Passed";

• Ký hiệu quyết định hình thoi (decision


symbol)
– Chỉ ra quyết định được thực hiện
– Chứa biểu thức có thể là true or false
• Kiểm tra điều kiện, theo đường dẫn
• Cấu trúc if
– Single-entry/single-exit
130
Cấu trúc lựa chọn if
• Lưu đồ và mã giả

Chú ý :
zero - false
nonzero - true
true
grade >= 6.0 print “Passed” Ví dụ:
3 - 4 is true
false

131
Viết chương trình
nhập vào 2 số, nếu số thứ nhất lơn hơn số thứ 2, in ra
“Cảm ơn”, ngược lại in ra “So thu 2 lớn hơn số thứ nhất”

Đáp án 132
Viết chương trình
nhập vào 2 số, nếu số thứ nhất lơn hơn số thứ 2, in ra
“Cảm ơn”, ngược lại in ra “So thu 2 lớn hơn”

133
Cấu trúc chọn lựa if/else
• if
– Thực hiện hoạt động điều kiện đúng
• if/else
– Các họat động khác nhau được thực hiện nếu điều kiện là true
hoặc false
• Mã giả
if student’s grade is greater than or equal to 6.0
print “Passed”
else
print “Failed”
• C++ code
if ( grade >= 6.0 )
cout << "Passed";
else
cout << "Failed";

134
Cấu trúc chọn lựa if/else
• Toán tử điều kiện tam phân (?:)
– 3 tham số (condition, giá trị if true, giá trị
if false)
• Mã có thể viết lại như sau:
cout << ( grade >= 6.0 ? “Passed” : “Failed” );

false true
grade >= 6.0

print “Failed” print “Passed”

135
Cấu trúc chọn lựa if/else
• Cấu trúc chọn lựa if/else lồng nhau
– Khi cấu trúc này lồng cấu trúc kia, kiểm tra các trường hợp.
– Khi điều kiện thỏa mãn, các câu lệnh khác được bỏ qua.
if ( grade < 5.0 )
cout << “Yeu";
else if ( grade <7 )
cout << “TB";
else if ( grade <8 )
cout << “Kha";
else if ( grade <9 )
cout << “Gioi";
else
cout << “Xuat sac";

136
Cấu trúc chọn lựa if/else
• Chú ý : trong trường hợp có nhiều cấu trúc if lồng nhau thì else sẽ
gắn liền với if gần nhất.
if(condition1) <statement1>;
else
if (condition2)
if(condition3) < statement2>;
else <statement3>; /* condition3 = 0 */
else <statement4>; /* condition2 = 0 */
• Ví dụ : Viết chương trình giải phương trình bậc nhất : Ax + B = 0 (A,
B : số thực).
• Giải : Xét các trường hợp xảy ra :
– Nếu A! =0 thì nghiệm x = -B/A
– Nếu A = 0
• Nếu B=0 : vô số nghiệm
• B != 0 : vô nghiệm.

137
Cấu trúc chọn lựa if/else
• /* Giải phương trình bậc nhất : Ax + B = 0 */
1. #include <iostream>
2. void main (void)
3. {
4. float a, b ;
5. //nhap du lieu ----------------------------------
------------------------
6. cout<<"Nhap 2 so a,b : "; cin >> a>>b;
7. //giai phuong trinh -----------------------------
-----------------------
8. if ( a== 0 )
9. if( b==0 ) cout << "\n Phuong trinh co vo so
nghiem ! \n " ;
10. else cout <<"\n Phuong trinh vo nghiem \n ";
11. else cout << "\n Phuong trinh co nghiem la : x=
"<< -b/a;
12.} 138
Bài tập
1. Viết mã giả, lưu đồ, viết code giải phương
trình bậc hai ax2 + bx + c = 0.
2. Nhập 3 số nguyên a, b, và c. Tìm số có giá
trị lớn nhất (max).

139
Cấu trúc lặp
while, do… while, for

140
Đặt vấn đề
• Ví dụ
– Viết chương trình xuất các số từ 1 đến 10
=> Sử dụng 10 câu lệnh cout
– Viết chương trình xuất các số từ 1 đến 1000
=> Sử dụng 1000 câu lệnh cout !
• Giải pháp
– Sử dụng cấu trúc lặp lại một hành động trong khi còn
thỏa một điều kiện nào đó.
– 3 lệnh lặp: while, do… while, for

141
Cấu trúc lặp while
• Dạng
while (expression)
statement
• lặp lại câu lệnh statement trong khi điều kiện
expression còn thoả mãn.
• Vòng lặp while lặp lại cho đến khi điều kiện là
false
• Ví dụ
int product = 2;
while ( product <= 10 )
product = 2 * product; 142
Cấu trúc lặp while
• Lưu đồ

product = 2

True
product<=10? product=2*product

False

143
Bài tập: In ra chữ “Lap lai” 5 lần và đếm số
lần hiển thị bằng vòng lặp while

144
Bài tập: In ra chữ “Lap lai” 5 lần và đếm số
lần hiển thị bằng vòng lặp while

145
Bài tập
• Vẽ lưu đồ và viết chương trình tính:
S=1+2+…+N với N được nhập từ bàn phím

• Viết chương trình tính:


S=12+22+…+N2 với N được nhập từ bàn
phím

146
Cấu trúc lặp while
• Ví dụ:
– Nhập điểm của 1 lớp 10 sinh viên, tính điểm trung
bình của lớp đó
• Mã giả
Set total to zero
Set grade counter to one
While grade counter is less than or equal to ten
Input the next grade
Add the grade into the total
Add one to the grade counter
Set the class average to the total divided by ten
Print the class average
• Chương trình C++ như sau:

147
1 // Class average program with counter-controlled repetition.
2 #include <iostream>
3
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 // function main begins program execution
9 int main()
10 {
11 int total; // sum of grades input by user
12 int gradeCounter; // number of grade to be entered next
13 int grade; // grade value
14 int average; // average of grades
15
16 // initialization phase
17 total = 0; // initialize total
18 gradeCounter = 1; // initialize loop counter
19
148
20
21 // processing phase
22 while ( gradeCounter <= 10 ) { // loop 10 times • Enter grade: 9.8
23 cout << "Enter grade: "; // prompt for input • Enter grade: 7.6
24 cin >> grade; // read grade from user • Enter grade: 7.1
25 total = total + grade; // add grade to total • Enter grade: 8.7
26 gradeCounter = gradeCounter + 1; // increment counter • Enter grade: 8.3
27 } • Enter grade: 9.0
28 • Enter grade: 5.7
29 // termination phase • Enter grade: 7.9
30 average = total / 10; // integer division • Enter grade: 8.2
31 • Enter grade: 9.4
32 // display result • Class average is
33 cout << "Class average is " << average << endl; 8.1
34
35 return 0; // indicate program ended successfully
36 Biến đếm tăng 1 sau mỗi vòng lặp.
Cuối cùng, biến đếm bằng 10 là
37 } // end function main
nguyên nhân kết thúc vòng lặp.
149
Cấu trúc lặp while
• Ví dụ: Chương trình yêu cầu xuất các số
từ 1 đến 100.
#include <iostream>
using namespace std;
int main()
{
int dem(1);
while (dem <= 100)
{
cout << dem << " ";
++dem; }
cout << “Done!" << endl;
return 0;

}
150
Cấu trúc lặp while
• Ví dụ: Tìm lỗi trong chương trình sau, giải thích.
#include <iostream>
using namespace std;
int main()
{
int dem(1);
while (dem < 5)
cout << dem << " ";

return 0;
}
151
Cấu trúc lặp while
• Ví dụ: Tìm lỗi trong chương trình sau, giải thích.
#include <iostream>
using namespace std;

int main () {
int i;
while(i=1) {
cout << "Vong lap while ????" << endl;
}
return 0;
}
152
Cấu trúc lặp do/while
• Dạng thức:
do statement
while (condition);
• Hoạt động lặp lại cho đến khi điều kiện sai.
• Vì vậy câu lệnh statement sẽ được thực hiện ít
nhất một lần ngay cả khi condition không bao
giờ được thoả mãn.
• Ví dụ
int product = 1;
do
product = 2 * product;
while ( product <= 10 );
153
Ví dụ

154
Cấu trúc lặp do/while
• Lưu đồ
product=1

product=2*product

product<=10? True

• Xét ví dụ như cấu trúc while False

155
Cấu trúc lặp do/while
• Mã giả
Set total to zero
Set grade counter to one
While grade counter is less than or equal to ten
Input the next grade
Add the grade into the total
Add one to the grade counter
Set the class average to the total divided by ten
Print the class average
• Chương trình C++ như sau:
156
1 // Class average program with counter-controlled repetition.
2 #include <iostream>
3
4 using std::cout;
5 using std::cin;
6 using std::endl;
7
8 // function main begins program execution
9 int main()
10 {
11 int total; // sum of grades input by user
12 int gradeCounter; // number of grade to be entered next
13 int grade; // grade value
14 int average; // average of grades
15
16 // initialization phase
17 total = 0; // initialize total
18 gradeCounter = 1; // initialize loop counter
19
157
20
21 // processing phase
22 do { • Enter grade: 9.8
23 cout << "Enter grade: "; // prompt for input • Enter grade: 7.6
24 cin >> grade; // read grade from user • Enter grade: 7.1
25 total = total + grade; // add grade to total • Enter grade: 8.7
26 gradeCounter = gradeCounter + 1; // increment counter • Enter grade: 8.3
27 } while ( gradeCounter <= 10 ) ; // loop 10 times • Enter grade: 9.0
28 • Enter grade: 5.7
29 // termination phase • Enter grade: 7.9
30 average = total / 10; // integer division • Enter grade: 8.2
31 • Enter grade: 9.4
32 // display result • Class average is
33 cout << "Class average is " << average << endl; 8.1
34
35 return 0; // indicate program ended successfully
36 Biến đếm tăng 1 sau mỗi vòng lặp.
Cuối cùng, biến đếm bằng 10 là
37 } // end function main
nguyên nhân kết thúc vòng lặp.
158
Cấu trúc lặp do/while
Ví dụ in từ 1-10 ra màn hình
#include <iostream>
using namespace std;

int main () {
int i = 1;
do {
cout << "Gia tri cua bien i: " << i << endl;
i = i + 1;
} while(i <= 10);
return 0; 159
}
Bài tập sử dụng vòng lặp
do/while
• Vẽ lưu đồ và viết chương trình tính:
S=1+2+…+N với N được nhập từ bàn phím

• Viết chương trình tính:


S=12+22+…+N2 với N được nhập từ bàn
phím

160
Cấu trúc lặp do/while
Tìm lỗi:

#include <iostream>
using namespace std;
int main() {
do{
cout << "Vong lap do while ????" << endl;
} while(true);
return 0;
}
161
Cấu trúc lặp for
• Dạng thức
for (initialization; condition; increase)
{
statement;
}
• Lặp lại statement chừng nào condition còn mang giá trị
đúng.
• Vòng for cung cấp chỗ dành cho lệnh khởi tạo và lệnh
tăng, do đó, vòng lặp này được thiết kế đặc biệt lặp lại
một hành động với một số lần xác định.
• Ví dụ
for( int counter = 1; counter <= 10; counter++ )
cout << counter << endl;
– In các số nguyên từ 1 đến 10 162
Cấu trúc lặp for
• Lưu đồ

int counter = 1

True cout << counter << endl;


Counter<=10?
counter++;

False

163
Cấu trúc lặp for
• Phần khởi tạo và lệnh tăng là không bắt buộc phải có.
Chúng có thể được bỏ qua nhưng vẫn phải có dấu chấm
phẩy ngăn cách giữa các phần. Vì vậy, chúng ta có thể
viết for (;n<10;) hoặc for (;n<10;n++).
• Sử dụng dấu phẩy, ta có thể dùng nhiều lệnh trong bất
kì trường nào trong vòng for. Ví dụ chúng ta có thể khởi
tạo một lúc nhiều biến trong vòng lặp:
for ( n=0, i=100 ; n!=i ; n++, i-- )
{
// thực hiện công việc gì đó ...
}
• Vòng lặp này sẽ thực hiện 50 lần nếu như n và i không
bị thay đổi trong thân vòng lặp:
164
Cấu trúc lặp for
• Ví dụ về vòng lặp for đơn giản nhất đó là
in các giá trị từ 1 đến mấy? ra màn hình.
include <iostream>
using namespace std;

int main () {

for( int i = 1; i < 10; i = i + 2 ) {


cout << "Gia tri cua bien i: " << i << endl;
}

return 0;
} 165
Bài tập
• Ví dụ tính tổng 10 số lẻ đầu tiên sử dụng
vòng lặp

166
Cấu trúc lặp for
• Ví dụ tính tổng 10 số lẻ đầu tiên.
include <iostream>
using namespace std;
int main () {
int i, k, sum;
sum=0;
k=1;
for( int i = 1; i <= 10; i ++) {
sum +=k;
k +=2;
}
cout << “Tổng 10 số lẻ đầu tiên: " << sum << endl;
return 0;
} 167
Cấu trúc lặp for
• Ví dụ tính tổng 10 số lẻ đầu tiên.

Đặt k là số lẻ đầu tiên

Điều kiện kết thúc vòng lặp


i là biến
điều khiển
vòng lặp
Cộng số lẻ thứ i vào tổng

k là số lẻ
tiếp theo

168
Bài tập sử dụng vòng lặp while,
do while và for
3.1 Viết chương trình tính:
S=12+22+…+N2 với N được nhập từ bàn
phím
3.2 Viết chương trình tính tổng số lẻ nguyên
dương nhỏ hơn n, n nhập từ bàn phím

169
Vòng lặp for lồng nhau trong
C++
• Một vòng lặp for nằm trong một vòng lặp
for khác, chúng ta gọi đó là lồng lặp for
lồng nhau.
#include <iostream>
using namespace std;
int main () {
for(int i=1;i<=2;i++) {
for(int j=1;j<=2;j++) {
cout << "Gia tri i = " << i << " , j = " << j<< "\n";
}
}
return 0; 170
} => in ra tất cả mấy giá trị?
Cấu trúc nhiều chọn lựa switch
• Ôn tập Cấu trúc lệnh if..else

Cấu trúc lệnh if..else. Cấu trúc lệnh switch().


if (biểu-thức-điều-kiện) { switch (biểu-thức) {
<Câu lệnh 1>; case H1: < câu lệnh 1>; break;
} case H2: < câu lệnh 2>; break;
else { . . . . . . . . . . . . . . . . . . . . . . . .;
<Câu lệnh 2>; case Hn: < câu lệnh n>; break;
} default: < câu lệnh n+1>; break;
}
Cấu trúc lệnh if..else khuyết.
if (biểu-thức-điều-kiện) {
<Câu lệnh 1>;
}

171
Cấu trúc nhiều chọn lựa switch
• Ví dụ else/if
if (x == 1) {
cout << "x is 1";
}
else if (x == 2) {
cout << "x is 2";
}
else {
cout << "value of x unknown";
}

172
Cấu trúc nhiều chọn lựa switch
• Kiểm tra một vài giá trị hằng cho một biểu thức
nguyên, dạng thức của nó như sau:
switch (expression) {
case constant1:
block of instructions 1
break;
case constant2:
block of instructions 2
break;
.
.
.
default:
default block of instructions
}
173
Cấu trúc nhiều chọn lựa switch
• Kiểm tra một vài giá trị hằng cho một biểu thức
nguyên, dạng thức của nó như sau:
switch (expression) {
case constant1:
block of instructions 1
break;
case constant2:
block of instructions 2
break;
.
.
.
default:
default block of instructions
}
174
Cấu trúc nhiều chọn lựa switch
• Nếu biểu thức (expression) bằng hằng 1 (constant1) thì
thực hiện khối lệnh 1 (block of instructions 1) cho đến khi
tìm thấy từ khoá break, thoát ra khỏi cấu trúc switch.
• Nếu không có từ khóa break, thực hiện khối kệnh 2
(block of instructions 2) cho đến khi tìm thấy từ khoá
break.
• …
• Cuối cùng, nếu giá trị biểu thức không bằng bất kì hằng
nào được chỉ định ở trên (bạn có thể chỉ định bao nhiêu
câu lệnh case tuỳ thích), chương trình sẽ thực hiện các
lệnh trong phần default (nếu nó tồn tại vì phần này
không bắt buộc phải có).

175
Cấu trúc nhiều chọn lựa switch
• Lưu đồ Case 1
T
Case 1 action

Y
F break?
N

T
Case 2 Case 2 action

F Y
break?
N

Default? Default action


Y
N

176
So sánh if-else vs switch case
• Điểm khác biệt duy nhất giữa hai cấu trúc lệnh
này là if..else lựa chọn một trong hai khả năng
đúng, sai của biểu thức điều kiện để thực
hiện, trái lại switch() lựa chọn khả năng đúng
của biểu thức điều kiện trong nhiều khả năng
để thực hiện.

177
So sánh if-else vs switch case
• Ví dụ switch • Ví dụ else/if
switch (x) if (x == 1) {
{ cout << "x is 1";
case 1: }
cout << "x is 1"; else if (x == 2) {
break; cout << "x is 2";
case 2: }
cout << "x is 2"; else {
break; cout << "value of x unknown
default: }
cout << "value of x unknown";
}
178
Bài tập
• Ví dụ : Dịch tên tháng sang tiếng anh sử dụng cấu
trúc switch case(). Ví dụ: nhập 1 in ra January,
nhập 2 thì in ra February.v.v.v

179
Các lệnh rẽ nhánh và lệnh nhảy
• Lệnh break.
– Sử dụng break chúng ta có thể thoát khỏi vòng lặp
while, for, do/while, switch ngay cả khi điều
kiện để nó kết thúc chưa được thoả mãn.
– Lệnh này có thể được dùng để kết thúc một vòng lặp
không xác định hay buộc nó phải kết thúc giữa chừng
thay vì kết thúc một cách bình thường.
– Thường sử dụng để bỏ qua phần còn lại của switch
hoặc thoát khỏi vòng lặp sớm.

180
1 #include <iostream>
2
3
4
using std::cout;
using std::endl; `
5
6 int main()
7 {
8
9 int x; // x declared here so it can be used after the loop
10
11 // loop 10 times
12 for ( x = 1; x <= 10; x++ ) {
Exits for structure when
13
break executed.
14 // if x is 5, terminate loop
15 if ( x == 5 ) • 1234
16 break; // break loop only if x is 5 • Broke out of loop when x became 5
17
18 cout << x << " "; // display value of x
19
20 } // end for
21
22 cout << "\nBroke out of loop when x became " << x << endl;
23
24 return 0; // indicate successful termination
25 181
26 } // end function main
Các lệnh rẽ nhánh và lệnh nhảy
• Lệnh continue.
– Lệnh continue làm cho chương trình bỏ qua phần còn
lại của vòng lặp và nhảy sang lần lặp tiếp theo.
– Ví dụ chúng ta sẽ bỏ qua số 5 trong phần đếm ngược
#include <iostream>
int main (){
for (int n=10; n>0; n--) {
if (n==5) continue;
cout << n << ", ";
}
cout << "FIRE!";
return 0;
}
10, 9, 8, 7, 6, 4, 3, 2, 1, FIRE!
182
Các lệnh rẽ nhánh và lệnh nhảy
• Hàm exit.
– Kết thúc chương trình và trả về một mã xác định. Dạng
thức
void exit (int exit code);
– exit code được dùng bởi một số hệ điều hành hoặc có
thể được dùng bởi các chương trình gọi. Mã trả về 0
có nghĩa là chương trình kết thúc bình thường còn các
giá trị khác 0 có nghĩa là có lỗi.

183
Các lệnh rẽ nhánh và lệnh nhảy
• Lệnh goto.
– Lệnh này cho phép nhảy vô điều kiện tới bất kì điểm
nào trong chương trình. Dạng thức
goto nhãn;
nhãn: statement
– Nhảy đến nhãn được khai báo trong chương trình.
– Không nên sử dụng trong chương trình C++.

184
Các lệnh rẽ nhánh và lệnh nhảy
• Lệnh return:
– Cho phép một hàm trả về một giá trị cho thành phần
gọi nó.
– Ví dụ:
#include <iostream>
int addition (int a, int b){
int r=a+b;
return (r);
}
int main (){
int z;
z = addition (5,3);
cout << "The result is " << z;
return 0;
}
185
Bài tập
1. Nhập một số nguyên dương n. Tính:
a. S = 1 + 2 + … + n
b. S = 12 + 22 + … + n2
2, Tính tổng các số nguyên tố nhỏ hơn n (0 < n
< 100)

186
KỸ THUẬT LẬP TRÌNH
Chương 4. Hàm

187
Khái niệm hàm
• Cú pháp
<kiểu trả về> <tên hàm>([danh sách tham số])
{
<các câu lệnh>
[return <giá trị>;]
}
– Trong đó
• <kiểu trả về> : kiểu bất kỳ của C/C++ (char, int,
long, float,…). Nếu không trả về thì là void.
• <tên hàm>: theo quy tắc đặt tên định danh.
• <danh sách tham số> : tham số hình thức đầu vào
giống khai báo biến, cách nhau bằng dấu ,
• <giá trị> : trả về cho hàm qua lệnh return.
188
Hàm
– Hàm là một khối lệnh được thực hiện khi nó
được gọi từ một điểm khác của chương trình.
– Một đoạn chương trình có tên, đầu vào và
đầu ra.
– Có chức năng giải quyết một số vấn đề
chuyên biệt cho chương trình chính.
– Được gọi nhiều lần với các tham số khác
nhau.
– Được sử dụng khi có nhu cầu:
• Tái sử dụng.
• Sửa lỗi và cải tiến. 189
Hàm

190
Khái niệm hàm
• Một phương thức để đóng gói quá trình
tính toán => dễ dàng sử dụng
• Định nghĩa hàm
• Khai báo hàm
 Tên hàm
 Các tham số của hàm
 Kiểu trả về của hàm
• Sử dụng hàm
Gọi hàm
 Tên hàm
 ()
191
 Các đối số
Ví dụ

Ví dụ 1
 Tên hàm: XuatTong
 Công việc: tính và xuất tổng 2 số nguyên
 Đầu vào: hai số nguyên x và y
 Đầu ra: kết quả tính toán

void XuatTong(int x, int y)


{
int s;
s = x + y;
cout<<x<<“+”<<y<<“=“<<s;
} 192
Ví dụ
 Ví dụ 2
 Tên hàm: TinhTong
 Công việc: tính và trả về tổng 2 số nguyên
 Đầu vào: hai số nguyên x và y
 Đầu ra: một số nguyên có giá trị x + y

int TinhTong(int x, int y)


{
int s;
s = x + y;
return s;
}
193
Định nghĩa và sử dụng hàm
Ví dụ: Định nghĩa của một hàm đơn giản để tính lũy thừa của một số nguyên

Tên hàm Các tham số hình thức

1 int Power(int base, unsigned int exponent) Khai báo hàm


2 {
3 int result = 1;
4 for (int i = 0; i < exponent; ++i)
5 result *= base; Định nghĩa hàm
6 return result;
7 }

8 main (void)
Tham số thực/đối số
9 {
10 cout << "2 ^ 8 = "<< Power(2,8)<< '\n';
11
12 }

Gọi hàm
194
#include <iostream>
int tinhtong (int a, int b){ // int a, int b là tham số hình
thức
int r=a+b;
return (r);
}
int main (){
int z;
z = tinhtong (5,3); // truyền 5 và 3 là đối số (tham số
thực)
cout << "The result is " << z;
return 0;
}

195
#include <iostream>
Phạm Vi Toàn Cục – Cục Bộ #include <stdio.h>
using namespace std;
Nhap gia tri cua x: 5 int xyz = 1; // xyz là toàn c?c
Xyz cua chuong trinh chinh: 1 void abc (int xyz) // xyz là c?c b? cho thân c?a
Tham so xyz co gia tri -4 abc
Xyz trong chuong trinh chinh: 1 {
• Phạm vi toàn cục if (xyz > 0) {
double xyz =2; // xyz là c?c b? cho kh?i
– Được định nghĩa ở này
cout <<"\nXyz co gia tri "<<xyz;
phạm vi chương }
trình else {
cout <<"\nTham so xyz co gia tri:"<<xyz;
• Phạm vi cục bộ cout <<"\nXyz trong chuong trinh chinh:
"<< ::xyz;
– Được định nghĩa ở }
}
phạm vi khối hay main (void){
hàm int x;
cout <<"Nhap gia tri cua x: ";
• Toán tử phạm vi cin >>x;
cout <<"Xyz cua chuong trinh chinh:
– :: "<<xyz<<endl;
abc(xyz-x); 196
}
Ví dụ
#include <iostream>
using namespace std;
// Definition of function sHello()
void sHello() // sHello() is the called function in this example
{
cout << "Hello !" << endl;
}

// Definition of function main()


int main()
{
cout << "Starting()" << endl;
// Interrupt main() by making a function call to sHello(). main() is the
caller.
sHello();
cout << "Ending in()" << endl;
return 0;
}
197
Các phương pháp truyền tham số
Có hai loại tham số:
• Tham số thực (actual parameter) hay còn gọi là đối số: là tham
số trong lời gọi hàm.
• Tham số hình thức (formal parameter): là tham số trong phần
khai báo và định nghĩa. Tham số hình thức chỉ là tên đại diện
cho tham số thực tương ứng. Kiểu của tham số hình thức sẽ
qui định kiểu của tham số thực.

198
Các phương pháp truyền tham số

• Ví dụ:
int min(int a, int b) //a,b là
tham số hình thức
{
if(a<b) return a;
else return b;
}
int main()
{ int minAB =min(15,
7)//Gọi hàm
// a = 15, b=7
} // Lúc này a,b là
tham số thực
199
Các phương pháp truyền tham số
Truyền tham trị (call by value):
• Chương trình dịch cấp phát vùng nhớ riêng cho từng
tham số hình thức, sau đó sao chép giá trị của tham số
thực tương ứng vào các tham số hình thức.
• Khi kết thúc thực hiện hàm, chương trình dịch sẽ thu hồi
các vùng nhớ đã cấp phát cho các tham số hình thức, và
các biến cục bộ khai báo bên trong hàm.
• Như vậy, mọi sự thay đổi trị của các tham số hình thức
đều không ảnh hưởng đến các tham số thực bên ngoài
hàm.
• Cách truyền:
void F(int, int ); // truyền bằng trị
hay
void F(int a, int b); // truyền bằng trị
200
Các phCác phương pháp truyền tham sốương

• Truyền Giá trị (Call by Value)


– Truyền đối số cho hàm ở dạng giá trị.
– Có thể truyền hằng, biến, biểu thức nhưng hàm
chỉ sẽ nhận giá trị.
– Được sử dụng khi không có nhu cầu thay đổi giá
trị của tham số sau khi thực hiện hàm.

void TruyenGiaTri(int x)
{

x++;
}
201
Hàm nguyên mẫu (function prototype)

• Hàm nguyên mẫu:


– Được dùng để cung cấp thông tin cho chương trình dịch
về tên hàm, kiểu giá trị trả về, số lượng, thứ tự và kiểu của
các tham số của hàm.
– Chương trình dịch căn cứ vào các thông tin này để kiểm
tra các lời gọi hàm trong chương trình.
– Hàm nguyên mẫu được đặt sau phần khai báo toàn cục và
ngay trước hàm main() hoặc có thể đặt trong tập tin khác.
• Khai báo:
[<kiểu giá trị trả về>] <tên hàm>([<danh sách các tham số>]) ;
• Ví dụ: Khai báo hàm nguyên mẫu có chức năng xác định trị
min giữa 2 số nguyên.
int Min(int, int) ;
int Min(int a, int b) ; // nên dùng cách khai báo này
202
Tổ chức một chương trình “C/C++”

• Cách 1: chương trình gồm 3 phần


PHẦN KHAI BÁO TOÀN CỤC
PHẦN KHAI BÁO VÀ ĐỊNH NGHĨA HÀM
HÀM main()
• Cách 2: chương trình gồm 4 phần (nên dùng cách này)
PHẦN KHAI BÁO TOÀN CỤC
PHẦN KHAI BÁO HÀM NGUYÊN MẪU
HÀM main()
PHẦN ĐỊNH NGHĨA HÀM

203
Tổ chức một chương trình “C/C++”
 Ví dụ: Cách 2
• Ví dụ: cách 1
#include <iostream.h>
#include <iostream.h>
int min(int a, int b);
int min(int a, int b) //prototype
{ if (a<b) return a; void main()
else return b; { int a=40, b=30;
} int min1 = min(a,b);
void main() cout << “Min = “ <<
{ int a=40, b=30; min1;
int min1 = min(a,b); }
cout << “Min = “ << min1; int min(int a, int b)
} { if (a<b) return a;
else return b;
} 204
#include <iostream>
Phạm Vi Toàn Cục – Cục Bộ #include <stdio.h>
using namespace std;
Nhap gia tri cua x: 5
int xyz = 1; // xyz là toàn c?c
Xyz cua chuong trinh chinh: 1 void abc (int xyz) // xyz là c?c b? cho thân c?a
Tham so xyz co gia tri -4 abc
Xyz trong chuong trinh chinh: 1 {
• Phạm vi toàn cục if (xyz > 0) {
double xyz =2; // xyz là c?c b? cho kh?i
– Được định nghĩa ở này
cout <<"\nXyz co gia tri "<<xyz;
phạm vi chương }
trình else {
cout <<"\nTham so xyz co gia tri:"<<xyz;
• Phạm vi cục bộ cout <<"\nXyz trong chuong trinh chinh:
"<< ::xyz;
– Được định nghĩa ở }
}
phạm vi khối hay main (void){
hàm int x;
cout <<"Nhap gia tri cua x: ";
• Toán tử phạm vi cin >>x;
cout <<"Xyz cua chuong trinh chinh:
– :: "<<xyz<<endl;
abc(xyz-x); 205
}
Cấp phát bộ nhớ
• Biến toàn cục được cấp phát tĩnh
• Biến địa phương (biến cục bộ):
– Cấp phát động: biến được giải phóng
khi kết thúc hàm, không lưu kết quả cho
lần sau
– Cấp phát tĩnh: biến không được giải
phóng khi kết thúc hàm, lưu kết quả cho
lần sau
– Mặc định biến địa phương là cấp phát
động. Để cấp phát tĩnh biến địa phương,
ta dùng từ khoá static trước khai báo
của biến 206
Cấp phát bộ nhớ
• Biến địa phương (biến cục bộ):
– Biến cục bộ thanh ghi:
• khai báo : register int x;
• yêu cầu trình biên dịch đặt biến đó
vào thanh ghi (nếu có thể)
• không dùng từ khóa register với biến
tĩnh.
• không định nghĩa con trỏ đến biến
thanh ghi
• Ví dụ: for(register int i =0;i <n;++i)
sum+=i;
207
Đệ quy
• Một hàm gọi lại chính nó
1. long int giaithua(int n)
2. { if (n==0) return(1);
3. else return(giaithua(n-1)*n);
4. }
• Nên hạn chế dùng đệ quy, nếu có thể thì dùng
vòng lặp để thay thế
1. long int giaithua(int n)
2. { long int i=1,k=1;
3. while (i<=n) { k=k*i ; i++ ; }
4. return(k);
5. } 208
Bài tập
1. Viết chương trình tính:S=1+2+…+N với N
được nhập từ bàn phím, sử dụng vòng
lặp while
2. Viết chương trình tính:S=1+2+…+N với N
được nhập từ bàn phím, sử dụng vòng
lặp do/while
3. S=12+22+…+N2 với N được nhập từ bàn
phím, sử dụng vòng lặp for
4. Viết chương trình tính tổng các số lẻ từ 1-
10
209
Chương trình học
• Chương 1. Tổng quan
• Chương 2. Biến, biểu thức và toán tử
• Chương 3. Cấu trúc điều khiển
• Chương 4. Hàm
• Chương 5. Mảng và con trỏ
• Chương 6. Lập trình hướng đối tượng
• Chương 7. Lớp
• Chương 8. Quá tải
• Chương 9: Thừa kế 210
KỸ THUẬT LẬP TRÌNH
Chương 5. Mảng và con trỏ

211
Đặt vấn đề
• Ví dụ
– Chương trình cần lưu trữ 4 số nguyên?
=> Khai báo 4 biến int a1, a2, a3, a4;
– Chương trình cần lưu trữ 100 số nguyên?
=> Khai báo 100 biến kiểu số nguyên!
– Người dùng muốn nhập n số nguyên?
=> Không thực hiện được!
• Giải pháp
– Kiểu dữ liệu mới cho phép lưu trữ một dãy
các số nguyên và dễ dàng truy xuất. 212
Mảng
• Mảng (array)
– Gồm một tập các đối tượng cùng kiểu và được sắp
xếp liên tiếp trong bộ nhớ
– Mỗi phần tử mảng được xác định bởi một chỉ số biểu
thị vị trí của phần tử trong mảng
• Phần tử đầu tiên của mảng luôn có chỉ số 0
– Số lượng phần tử trong mảng được gọi là kích thước
của mảng (cố định; xác định trước)
– Gồm mảng một chiều và mảng đa chiều

int A[4]; 54 43 40 102


A[0] A[1] A[2] A[3]
Kích thước 213
Chỉ số Phần tử mảng
Biến Mảng

• Biến mảng được định nghĩa bằng cách


đặc tả kích thước mảng và kiểu các phần
tử của nó
– Ví dụ: int heights[10];
• Truy xuất 1 phần tử qua chỉ số mảng
– Ví dụ: heights[0]= 210; cout<< heights[3];
• Truy xuất phần tử không tồn tại  lỗi
vượt ngoài biên
– Ví dụ: cout<<heights[-1]; cout<<heights[10];
214
Khai báo mảng
• Cú pháp tổng quát:
– <tên kiểu> <tên mảng> [<số phần tử>];
• Ví dụ:
int mang[20]; //Mảng số nguyên tên “mang”, có tối
đa 20 phần tử

//Mảng ký tự tên “chuoi”, có tối đa 30 phần


• char chuoi[30]; tử

• float daySo[40]; //Mảng số thực tên “daySo”, có tối đa 40


phần tử

215
Khai báo biến mảng (tường minh)
• Ví dụ
int Mang1Chieu[10];
0 1 2 3 4 5 6 7 8 9

Mang1Chieu

int Mang2Chieu[3][4];
0 1 2 3 4 5 6 7 8 9 10 11

Mang2Chieu 0
1

216
Truy xuất phần tử mảng
• Dùng tên mảng và số thứ tự của phần tử
tương ứng (gọi là chỉ số).
• Trong C/C++, chỉ số của các phần tử
mảng bắt đầu từ 0.
• Truy xuất đến các phần tử có chỉ số âm sẽ
có báo lỗi.
• Không truy xuất phần tử vượt quá kích
thước mảng.
217
Truy xuất phần tử mảng
• mang[2] = 100;
// Gán phần tử có chỉ số 2 trong “mang” giá trị 100
• chuoi[1] = ‘A’;
//Gán phần tử có chỉ số 1 trong “chuoi” ký tự A.
• float x = daySo[3];
//Gán giá trị của phần tử có chỉ số 3 trong “daySo”
cho biến thực x;

218
Bộ Khởi Tạo Mảng
• Mỗi mảng có một bộ khởi tạo mảng
• Ví dụ
int numx[3] = {5, 10, 15}; Bộ khởi tạo mảng

numx[2] = ? Kích
numy[2] = ? thước
int numz[ ] = {5, 10, 15}; mảng ?

int numy[3] = {5, 10};

219
Khởi tạo giá trị cho mảng lúc
khai báo
• Gồm các cách sau
– Khởi tạo giá trị cho mọi phần tử của mảng
int numx[4] = {55, 44, 245, 235};
0 1 2 3
numx 55 44 245 235

– Khởi tạo giá trị cho một số phần tử đầu mảng


int numy[4] = {456, 789};

0 1 2 3
numy 456 789 0 0
220
Nhập mảng số nguyên từ bàn phím
• void main (){
int mang[20];
for (int i = 0; i < 20; i++)
{
cout << “mang[“<<i<<“]=“;
cin >> mang[i];
}
}

221
Xuất mảng số nguyên ra màn hình
• int mang[20];
void main () {
for (int i = 0; i < 20; i++)
{
cout << “mang[“<<i<<“]=“;
cout << mang[i] << endl;
}
}

222
Ví dụ mảng
#include <iostream>
#define n 5
void main()
{
int i, mang[n];
cout <<"Nhap cac phan tu cua mang: "<<endl;
for(i=0 ; i<n ; i++) {
cout <<"mang["<< i <<"]"<<": ";
cin>> mang[i]; }
cout<<"\n Mang gom cac phan tu :\n";
for(i=0 ; i<n ; i++) {
cout <<"mang["<< i <<"]"<<"= ";
cout<<mang[i]<<endl; }
getchar(); 223
}
Truyền mảng cho hàm
• Truyền mảng cho hàm
– Tham số kiểu mảng trong khai báo hàm giống
như khai báo biến mảng
void HamTang(int a[100]);

– Tham số kiểu mảng truyền cho hàm chính là địa


chỉ của phần tử đầu tiên của mảng
• Có thể bỏ số lượng phần tử hoặc sử dụng con trỏ.
• Mảng có thể thay đổi nội dung sau khi thực hiện hàm.
void HampTang(int a[]);
void HamTang(int *a);
224
Truyền mảng cho hàm
• Truyền mảng cho hàm
– Số lượng phần tử thực sự truyền qua biến khác
void HamTang(int a[100], int n);
void HamTang(int a[], int n);
void HamTang(int *a, int n);

• Lời gọi hàm


void NhapMang(int a[], int &n);
void XuatMang(int a[], int n);
void main()
{
int a[100], n;
NhapMang(a, n);
XuatMang(a, n);
225
}
Bài tập

226
Chuỗi ký tự (string)
• Trong C/C++ không có kiểu chuỗi. Chuỗi
được xem như một mảng ký tự kết thúc
bằng ký tự null (mã ASCII = 0).
• Khai báo mảng ký tự:
char chuoi[20];
Chú ý: việc thêm ký tự null vào cuối mảng
do trình biên dịch thực hiện

227
Khởi tạo giá trị ban đầu cho
mảng ký tự
• char chuoi[20] = “I am a string”;
• char chuoi[] = “You are a string too”;
• char chuoi[] = {‘H’, ‘E’, ‘ ‘, ‘I’, ‘S’};
Chuỗi là một mảng ký tự
Ví dụ: so sánh sự khác nhau của
char str[] = "HELLO"; và char str[] = {'H', 'E',
'L', 'L', 'O'};

228
Xử lý chuỗi trong C/C++
• Dùng các thư viện khai báo trong string.h
Phép gán: strcpy(s1, s2);
Phép nối: strcat(s1, s2);
Phép so sánh: strcmp(s1, s2);

229
Con trỏ
• Địa chỉ của biến
– Bộ nhớ trong của máy tính bao gồm các ô
nhớ liên tiếp được đánh số từ 0 đến giới hạn
của bộ nhớ.

230
Con trỏ
• Toán tử &
– Trả về địa chỉ của biến (hàm, mảng)
– Thường là số 32 bit
– Biễu diễn ở dạng hexadecima
– Ta có thể xác định địa chỉ của biến hoặc địa chỉ của
hàm hoặc địa chỉ của mảng nhờ vào toán tử & như
sau: &<biến>
&<hàm>
&<array>
231
Toán tử &
• int andy = 25; // giả sử địa chỉ của andy = 1776
• int * ted;
• ted = &andy;

232
Con Trỏ

• Con trỏ đơn giản chỉ là địa chỉ của một vị


trí bộ nhớ và cung cấp cách gián tiếp để
truy xuất dữ liệu trong bộ nhớ
• Ví dụ Biến giá trị địa chỉ

int num = 10; num 10 1000


int *ptr1 = &num;
ptr1 1000 2000

cout << *ptr1;

Thành phần của biến = kiểu biến + tên biến +giá trị + địa chỉ
233
Khai báo con trỏ
• Con trỏ cũng có kiểu. Kiểu con trỏ là kiểu
của dữ liệu mà nó trỏ đến.
• Khai báo con trỏ nguyên:
int * intPtr;
• Khai báo con trỏ ký tự:
char * charPtr;

234
Ví dụ kiểm định giá trị con trỏ
#include <iostream>
void main() 1. Gia tri cua number: 10
2. Dia chi cua number: 0012FF88
{ 3. Gia tri cua con tro pointer: 0012FF88
int number=10; 4. Gia tri duoc tro toi boi pointer: 10
int *pointer=&number; 5. Dia chi cua con tro pointer: 0012FF84
cout << "\n 1. Gia tri cua number : "<<number;
cout <<"\n 2. Dia chi cua number : "<<&number;
cout <<"\n 3. Gia tri cua con tro pointer : "<<pointer;
cout <<"\n 4. Gia tri được trỏ tới bởi pointer : "<<*pointer;
cout <<"\n 5. Dia chi cua con tro pointer : "<<&pointer;
getchar();
}
235
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ỏ>;
Trong phép toán này, phải có dấu con trỏ “*”.
Nếu không có dấu con trỏ, sẽ trở thành phép
lấy địa chỉ của biến do con trỏ trỏ tới.
Ví dụ:
int x = 10, y, *px;
px = &y;
*px = x;

236
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ỏ A> = <Tên con trỏ B>;
• Ví dụ:
• int x = 10, *px, *py;
• px = &x;
• py = px;

237
Ví dụ về con trỏ
• int i, j, *p,*q ; // khai báo int
• // khai báo
• p = &i; // cho p
• q = &j; // cho q j
• cout << &i ; // hỏi địa
• cout << q ; // hỏi địa chỉ)
• i = 2; // gán i
• *q = 5; // gán j
• i++ ; cout << i ; // tăng i và
• (*q)++ ; cout << j ; // tăng j (
• (*p) = (*q) *2 + 1; // gán lại i (
• cout << i ; //

238
Ví dụ về con trỏ
• int i, j ; // khai báo 2 biến nguyên i, j
• int *p, *q ; // khai báo 2 con trỏ nguyên p, q
• p = &i; // cho p trỏ tới i
• q = &j; // cho q trỏ tới j
• cout << &i ; // in địa chỉ biến i
• cout << q ; // in địa chỉ biến j (thông qua q)
• i = 2; // gán i bằng 2
• *q = 5; // gán j bằng 5 (thông qua q)
• i++ ; cout << i ; // tăng i và hỏi i, i = 3
• (*q)++ ; cout << j ; // tăng j (thông qua q) và hỏi j, j = 6
• (*p) = (*q) * 2 + 1; // gán lại i (thông qua p)
• cout << i ; // I = bao nhiêu?
// 13

239
Con trỏ và mảng một chiều
• Các phần tử của mảng được cấp phát liên tiếp
nhau, phần tử đầu tiên có địa chỉ thấp nhất.
• Tên mảng là hằng con trỏ, luôn trỏ đến phần tử
đầu tiên của mảng.
• Số học con trỏ: phép tăng / giảm con trỏ làm cho
con trỏ trỏ đến phần tử liền sau / liền trước của
phần tử ban đầu .
Ví dụ khai báo:
int array [5];

thì địa chỉ của mảng array (cũng viết là array) sẽ trùng với địa chỉ phần tử
đầu tiên của mảng array (là & array[0]) nghĩa là:
array = & array[0];
240
Địa chỉ của mảng một chiều
• Địa chỉ của mảng một chiều và các phần tử
trong mảng một chiều
• int arr[] = { 2, 3, 4, 1, 3 };
• // hiển thị địa chỉ của mảng
– cout << &arr << endl; //show
• // hiển thị địa chỉ phần từ đầu
tiên trong mảng
– cout << &arr[0] << endl;
241
Con trỏ và mảng một chiều
• Vì mỗi phần tử bên trong mảng đều có
kiểu int (mảng ở slide trước), do đó, chúng ta
có thể sử dụng 1 con trỏ có kiểu dữ liệu tương
ứng (int *) để trỏ đến từng phần tử của
mảng arr.
• int *ptr = &arr[4]; //ptr point to
the 5th element

242
Con trỏ và mảng một chiều
• Ví dụ khai báo:
• int A[5] = {5, 10, 15, 20, 25};
• int *pa = A;

So sánh pa vs. A; và pa vs. &A[0]; ???

*pa là bao nhiêu?


243
Phép toán trên con trỏ và mảng
• Khi một con trỏ trỏ đến mảng, thì các phép toán tăng hay
giảm trên con trỏ sẽ tương ứng với phép dịch chuyển trên
mảng.
• Ví dụ khai báo:
– int A[5] = {2,4,6,8,10};
– 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] = 6.
• 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] 244
Phép toán trên con trỏ và mảng
• Khi một con trỏ trỏ đến mảng, thì các phép toán tăng hay
giảm trên con trỏ sẽ tương ứng với phép dịch chuyển trên
mảng.
• Ví dụ khai báo:
– int A[5] = {5, 10, 15, 20, 25};
– 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] 245
1. cout <<“Nhap cac phan tu cua mang:”<<endl;
2. int a[10], i ;
3. for(i=0 ; i<10 ; i++)
4. { cout<< “Nhap vao phan tu a[“<<i<<“]: ”;
5. cin >>a[i];
6. }
7. int *p;
8. p=a; //p=&a[0]
9. cout <<“Xuat cac phan tu cua mang:”<<endl;
10. for(i=0 ; i<10 ; i++)
11. { cout <<“a[“<<i<<“]=“<<*p<<endl;
12. p++;
13. }

246
Truyền mảng một chiều cho hàm

int max( int x[ ]) int max( int *x)


{int j,m; {int j,m;
m= x[0]; m=*x;
for(j=0 ; j<10 ; j++) for(j=0;j<10;j++)
if( x [ j ] > m ) if( *(x+j) > m )
m= x [ j ] ; m=*(x+j);
return m; return m; }
}

247
Truyền mảng một chiều cho hàm
void nhap( int x[] )
{ int i;
for(i=0; i<N; i++)
{ cout <<i <<“ “;
cin >> x[i];
}
}
void xuat(const int x[]) // chú ý từ khoá const
{ int i;
for(i=0 ; i<N ; i++)
cout << x[i];
}
Khi thêm từ khóa const vào trước khai báo mảng hình thức thì trong thân hàm
không thể thay đổi giá trị của mảng đối số thực truyền cho hàm. Nó tương tự
như truyền đối số theo giá trị 248
Mảng Đa Chiều
Mùa xuân Mùa hè Mùa thu Mùa đông Hàng
Hà nội 26 34 22 17

Đà nẵng 24 32 19 13 int seasonTemp[3][4];


Tp HCM 28 38 25 20

Cột

... 26 34 22 17 24 32 19 13 28 38 25 20 ...

First
hàngrow
đầu Second row
hàng hai Third
hàng row
ba

Cách tổ chức trong bộ nhớ


249
Con trỏ và mảng nhiều chiều
1. #include <iostream>
2. void main()
3. { int a[4][6];
4. int i,j;
5. for (i=0;i<4;i++)
6. for (j=0;j<6;j++)
7. { cout <<“a[“<<i<<“][”<<j<<“] =”;
8. cin >> a[i][j];
9. }
10. }
250
Mảng 2 chiều
• Mảng có thể được khởi tạo bằng cách sử dụng một bộ khởi tạo
lồng nhau:
int seasonTemp[3][4]={
{26, 34, 22, 17},
{24, 32, 19, 13},
{28, 38, 25, 20}
};
• Điều này ánh xạ tới mảng một chiều gồm 12 phần tử trong bộ nhớ nên
nó tương đương với:
int seasonTemp[3][4]={
26, 34, 22, 17, 24, 32, 19, 13, 28, 38, 25, 20
};
251
Mảng 2 chiều
const int rows = 3;
const int columns = 4;
int seasonTemp[rows][columns]= {
{26, 34, 22, 17},
{24, 32, 19, 13},
{28, 38, 25, 20}
};
int HighestTemp(int temp[rows][columns])
{
int highest=0;
for (register i=0; i < rows;++i)
for (register j=0; j <columns;++j)
if(temp[i][j]>highest)
highest= temp[i][j];
return highest;
}
252
Con trỏ và mảng nhiều chiều
• Các phần tử của mảng 2 chiều được sắp xếp:
các phần tử của hàng đầu tiên, sau đó đến các
phần tử của hàng kế tiếp …

int *pa, a[4][6];


pa=(int *)a; // ép kiểu
Khi đó pa là địa chỉ của a[0][0]
pa + 1 là địa chỉ của a[0][1]
pa + 2 là địa chỉ của a[0][2]
........
pa + i*6 + j là địa chỉ của a[i][j] 253
1. #include <iostream>
2. void main()
3. { int i,j;
4. int *pa, a[4][6];
5. pa=(int *)a;
6. cout <<"\nNhap vao cac phan tu cua mang:";
7. for (i=0 ; i<4 ; i++)
8. for (j=0 ; j<6 ; j++)
9. {
10. cout <<"a["<<i<<"]["<<j<<"]=";
11. cin >>*(pa+i*6+j);
12. }
13. cout <<"\n\n";
14. for (i=0 ; i<4 ; i++)
15. {
16. for (j=0 ; j<6 ; j++)
17. cout<<" a["<<i<<"]["<<j<<"]="<<*(pa+i*6+j);
18. cout <<"\n";
19. }
20. getchar();
21. }
254
1. #include <iostream>
2. using namespace std;
3. int main()
4. { int i,j;
5. int *pa, a[2][3];
6. pa=(int *)a;
7. cout <<"\nNhap vao cac phan tu cua mang:";
8. for (i=0 ; i<2 ; i++)
9. for (j=0 ; j<3 ; j++)
10. {
11. cout <<"a["<<i<<"]["<<j<<"]=";
12. cin >> *(pa+i*3+j);
13. }
14. cout <<"\n\n";
15. for (i=0 ; i<2 ; i++)
16. {
17. for (j=0 ; j<3 ; j++)
18. cout<<" a["<<i<<"]["<<j<<"]="<<*(pa+i*3+j);
19. cout <<"\n";
20. }
21. return 0;
22. } => Kết quả gì??? 255
Truyền mảng nhiều chiều cho hàm
• Giả sử ta cần truyền mảng int a[4] [6] cho hàm
• Cách 1: khai báo
int (*pa)[6]; hoặc int pa[ ][6];
Trong thân hàm, để truy cập phần tử a[i][j] ta
dùng pa[i][j].
• Cách 2: khai báo
int *pa; và int n; // số cột
Trong thân hàm, để truy cập phần tử a[i][j] ta
dùng *(pa+i*n+j)
256
1. //chương trình nhập xuất mảng 2 chiều theo cách 1
2. #include <stdio.h>
3. void nhap(int a[][6],int m,int n)
4. { int i,j, x; // x là biến trung gian
5. printf("\nNhap vao cac phan tu cua mang:");
6. for ( i=0 ; i<m ; i++)
7. for ( j=0 ; j<n ; j++)
8. { gotoxy(10*j+3,i+3);
9. printf("a[%d][%d]=",i,j);
10. scanf("%d",&x);
11. a[i][j]=x;
12. }
13. }
257
15. void hienthi(int a[][6],int m,int n)
16. { int i,j;
17. printf("\nCac phan tu cua mang:\n");
18. for ( i=0 ; i<m ; i++)
19. { for (j=0 ; j<n ; j++)
20. printf(" a[%d][%d]=%4d", i, j,a[i][j]);
21. printf("\n");
22. }
23. }
24. void main()
25. { int m,n;
26. int a[10][6];
27. printf("\nNhap m,n:"); scanf("%d%d",&m,&n);
28. nhap(a,m,n);
29. printf("\n\n");
30. hienthi(a,m,n); 258
31. }
1. // nhập xuất mảng 2 chiều theo cách 2
2. #include <stdio.h>
3. #include <conio.h>
4. void nhap(int *p,int m,int n)
5. { int i,j;
6. clrscr();
7. printf("\nNhap vao cac phan tu cua mang:");
8. for (i=0;i<m;i++)
9. for (j=0;j<n;j++)
10. { gotoxy(10*j+3,i+3);
11. printf("a[%d][%d]=",i,j);
12. scanf("%d",p+i*n+j);
13. }
14. }
259
16. void hienthi(int *p, int m, int n)
17. { int i, j;
18. printf("\nCac phan tu cua mang:\n");
19. for ( i=0 ; i<m ; i++)
20. { for ( j=0 ; j<n ; j++)
21. printf(" a[%d][%d]=%4d", i, j, *(p+i*n+j));
22. printf("\n");
23. }
24. }
25. void main()
26. { int m,n;
27. int *pa, a[10][10];
28. pa=(int *)a; // chú ý lệnh này
29. printf("\nNhap m,n:"); scanf("%d%d",&m,&n);
30. nhap(pa, m, n);
31. printf("\n\n");
32. hienthi(pa, m, n); 260
33. }
Bộ Nhớ Động - Tĩnh
• Bộ nhớ động (heap)
– Vùng nhớ được cấp phát động trong thời gian thực thi
• Bộ nhớ tĩnh (stack)
– Vùng nhớ được sử dụng để lưu trữ các biến toàn cục
và lời gọi hàm

261
Bộ Nhớ Động - Tĩnh
• Hai toán tử được sử dụng
– new: cấp phát
– delete: thu hồi

void Foo (void)


{
int *ptr = new int;
char *str = new char[10];
//...
delete ptr;
delete [ ]str;
}

262
Bộ Nhớ Động - Tĩnh
• Hai toán tử được sử dụng
– new: cấp phát
– delete: thu hồi

void Foo (void)


{
int *ptr = new int;
char *str = new char[10];
//...
delete ptr;
delete [ ]str;
}

263
Tham Chiếu
• Một tham chiếu (reference) là một biệt hiệu
(alias) cho một vùng nhớ được cấp phát cho
một biến
• Các tác động lên tham chiếu sẽ giống như các
tác động trực tiếp lên biến đó
• Ví dụ int num1=123;
int &num2=num1;
cout <<"\n num1: “ <<num1 <<"\n num2: “<<num2;
• Chú ý num2=345; cout << “\n num1: ”<< num1;
– Một tham chiếu phải luôn được khởi tạo khi nó được
định nghĩa
– Có thể khởi tạo tham chiếu tới một hằng.
– Khi khai báo tham chiếu, phải gắn nó với một biến
nào đó và không thể gắn lại với biến khác
264
Truyền trị (Passing by Value)
• Khi truyền trị, hàm sẽ tạo ra một biến cục bộ để
lưu giá trị của biến được truyền.
• Giá trị của biến được truyền không thay đổi.
• Ví dụ:
void ftang(int val) {
val++;
cout << val<<endl;
}
int main (void) {
int x=4;
ftang(x);
cout << x; // x = 4
}
265
Cho biến a kiểu số nguyên được nhập từ bàn phím, viết
hàm tăng giá trị biến a lên 1 và hiển thị ra màn hình

#include<iostream>
using namespace std;
void tang(int a) {
Kết quả khi chạy chương trình:
a++;
Gia tri cua a: 5
}
int main() {
int a = 5;
tang(a);
cout << "Gia tri cua a: " << a;
return 0;
}

266
Truyền tham chiếu (Passing by Reference)
• Tham chiếu cung cấp bí danh (alias) cho biến.
• Khi truyền tham số theo kiểu tham chiếu thì biến cục bộ là bí danh
của biến được truyền.
• Địa chỉ của biến được truyền cho hàm.
• Hàm có thể truy cập trực tiếp trên biến được truyền.
• Ví dụ:
void swap (int & a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
cout<< a << b<< end;
}
Int main (void)
{
int x=3; int y=5;
swap(x,y); // a,b là bí danh của x,y
return 0; 267
}
Cho biến a kiểu số nguyên được nhập từ bàn phím, viết
hàm tăng giá trị biến a lên 1 và hiển thị ra màn hình
#include<iostream> Kết quả khi chạy chương trình:
Address of a in ham tang : 010FFAEC
using namespace std; Address of a in ham main : 010FFAEC
void tang(int& a) { Gia tri cua a: 6
a++;
cout << “1. Address of a in tang function: " << &a << endl;
}
int main() {
int a = 5;
tang(a);
cout << “2. Address of a in main function: " << &a << endl;
cout << "Gia tri cua a: " << a;
return 0;
}
268
Cho biến a kiểu số nguyên được nhập từ bàn phím, viết
hàm tăng giá trị biến a lên 1 và hiển thị ra màn hình
#include<iostream>
using namespace std;
void tang(int* p) {
(*p)++; Kết quả khi chạy chương trình:
} Gia tri cua a: 6
int main() {
int a = 5;
tang(&a);
cout << "Gia tri cua a: " << a;
return 0;
}

269
Tham số hằng
• Tham chiếu hằng không cho phép hàm
thay đổi giá trị của biến được truyền.
• Ví dụ:
void f( int& a, const int& b ) // b là tham số hằng
{
a=5; // ok
b=7; // fail
}

270
Truyền Bằng Trị - Con Trỏ - Tham
Chiếu
// Truyền bằng trị (đối tượng) int main (void)
1 void Swap1 (int x, int y) {
2 { int i = 10, j = 20;
3 int temp = x; Swap1(i, j); cout << i << ", " << j << '\n';
4 x = y; Swap2(&i, &j); cout << i << ", " << j << '\n';
5 y = temp; Swap3(i, j); cout << i << ", " << j << '\n';
6 } }
// Truyền bằng địa chỉ (con trỏ)
7 void Swap2 (int *x, int *y)
8 {
9 int temp = *x; 10, 20 Hàm 1 tạo 2 bản sao cho hai biến x, y
10 *x = *y;
20, 10 Hàm 2 làm việc trực tiếp tại địa chỉ của i,j
11 *y = temp;
10, 20 Hàm 3 không tạo bản sao nào cả.
}
12 // Truyền bằng tham chiếu
13 void Swap3 (int &x, int &y)
14 {
15 int temp = x;
16 x = y;
17 y = temp;
18 }
271
Bài tập
1. Viết chương trình đổi số thành chữ, ví dụ:
12345 tức là mười hai ngàn ba trăm bốn mươi
lăm.
2. Viết chương trình nhập điểm sinh viên với các
trường: họ tên, điểm môn học 1, 2, 3, 4, 5,
điểm trung bình. Xuất kết quả là các điểm nói
trên và phân loại sinh viên.
3. Viết chương trình thi trắc nghiệm với hàm rand
để tạo ngẫu nhiên cho câu hỏi và xáo các đáp
án.
4. Viết chương trình nhập chuỗi, in ra chiều dài
chuỗi và chuỗi đảo.
272
Chương trình
• Chương 1. Tổng quan
• Chương 2. Biến, biểu thức và toán tử
• Chương 3. Cấu trúc điều khiển
• Chương 4. Hàm
• Chương 5. Mảng và con trỏ
• Chương 6. Lập trình hướng đối tượng
• Chương 7. Lớp
• Chương 8. Quá tải
• Chương 9: Thừa kế 273
KỸ THUẬT LẬP TRÌNH
Chương 6. Lập trình hướng
đối tượng

274
Lập trình hướng đối tượng
C++

275
Tổng quan

276
Lập trình cấu trúc
• Lập trình cấu trúc (1960s)
– Phân chia vấn đề lớn thành các vấn đề con độc lập, từ
đó tạo thành thủ tục và hàm.
– Trọng tâm của lập trình truyền thống: dựa trên các chi
tiết của việc thực hiện.
– Phương pháp viết chương trình chặt chẽ
– Rõ ràng, dễ thử nghiệm và sửa lỗi, dễ thay đổi
– Khi chương trình lớn hơn  khó quản lý
– Dữ liệu đóng vai trò quan trọng
• Khi thêm 1 dữ liệu kiểu mới, phải thay đổi tất cả các công việc
và các hàm liên quan đến dữ liệu đó
• Không thể phân chia các phần cần che dấu thông tin trong
chương trình. 277
Các phương pháp lập trình

Lập trình tuyến tính Thời gian đầu

Lập trình có cấu trúc 1960 – 1970

Lập trình hướng đối


tượng 1980
278
(1) Lập trình tuyến tính

• Chương trình là một


dãy các lệnh
• Lập trình là viết các
lệnh trong dãy lệnh

• Không mang tính thiết kế

• Tiêu biểu là ngôn ngữ Basic, Fortran


279
(1) Lập trình tuyến tính: Đặc điểm

• Chương trình đơn giản, số dòng lệnh ít

• Thực hiện trình tự từ đầu đến cuối

• Không có cấu trúc

• Dùng các lệnh “goto/ gosub” để nhảy đến một


vị trí nào đó trong chương trình

280
(2) Lập trình có cấu trúc

• Chương trình là một hệ thống các thủ tục/


hàm. Mỗi thủ tục/ hàm là một dãy các lệnh

• Lập trình là xây dựng các thủ tục/ hàm

• Kết quả là hệ thống cấu trúc và mối quan hệ


giữa các hàm/ thủ tục

• Tiêu biểu là ngôn ngữ Pascal, C

281
(2) Lập trình có cấu trúc

• Khái niệm trừu tượng hóa (abstraction) theo


chức năng: Làm được gì, không quan tâm chi
tiết bên trong

• Phân chia công việc thành các chức năng đơn


giản hơn gọi là lập trình đơn thể (module)

282
(2) Lập trình có cấu trúc: Đặc điểm
Ưu điểm

• Đơn giản hóa quá trình thiết kế và cài đặt

• Dễ đọc chương trình, hiểu chi tiết bài toán

• Các chương trình con tương đối độc lập, dễ


phân công cho từng nhóm

283
(2) Lập trình có cấu trúc: Đặc điểm
Khuyết điểm

Khi thay đổi dữ liệu thì phải thực hiện các thay
đổi ở mọi đơn thể có tác động trên chúng

Hàm và dữ liệu không có quan hệ với nhau

Hàm không hạn chế truy nhập tới các biến toàn
cục

284
(2) Lập trình có cấu trúc: Đặc điểm
Khuyết điểm

• Sai sót trong việc trao đổi thông tin giữa các
thành viên trong nhóm có thể mất nhiều thời
gian để sửa chữa chương trình

• Phần xử lý nằm rải rác và phải hiểu rõ cấu


trúc dữ liệu

• Khó bảo trì chương trình


285
(3) Lập trình Hướng đối tượng
(Object-oriented Programming)
• Tư duy mới, tiếp cận theo hướng đối
tượng để giải các bài toán trên máy tính

• Thiết kế và phát triển phần mềm dựa trên


kiến trúc lớp và đối tượng

286
(3) Lập trình Hướng đối tượng
(Object-oriented Programming)
Mục tiêu

• Loại bỏ những thiếu sót của tiếp cận theo thủ tục

• Tiếp cận theo hướng trừu tượng hoá (abstraction)

• Dữ liệu được xem là phần trung tâm và được bảo


vệ

• Hàm gắn kết với dữ liệu

287
(3) Lập trình Hướng đối tượng
(Object-oriented Programming)

Mục tiêu

• Phân tách bài toán thành nhiều đối tượng và


yêu cầu chúng thực hiện hành động của mình

• Tăng cường khả năng sử dụng lại

288
(3) Lập trình Hướng đối tượng
(Object-oriented Programming)

Ưu điểm

• Cung cấp một cấu trúc module rõ ràng: Giao


diện định nghĩa rõ ràng và chi tiết cài đặt ẩn

• Duy trì và sửa đổi mã nguồn dễ dàng

• Cung cấp framework tốt với các thư viện mã


nguồn

289
Lập trình hướng đối tượng
• Object oriented programming (OOP)
– Chia bài toán thành các nhóm nhỏ có liên hệ với
nhau gọi là đối tượng.

290
Đối Tượng (Object)
• Đối tượng là
chìa khóa để hiểu
được kỹ thuật
hướng đối tượng
• Trong hệ thống
hướng đối tượng,
mọi thứ đều là đối
tượng
Viết một chương trình hướng đối tượng nghĩa là đang tạo
một mô hình của một vài bộ phận trong thế giới thực
291
Đối tượng (Object)
What is the – Hộp đen chứa các
salary of Jack?
lệnh (code) và dữ
Sales
liệu.
– Thông tin truyền
giữa các phần khác
nhau gọi là các
Jack's salary thông điệp giữa các
is $2000 đối tượng.
Accounts – Các thông điệp này
có thể được chuyển
thành lời gọi hàm
trong chương trình.
292
Đối Tượng

Đối tượng phần mềm Đối tượng phần mềm Xe Đạp

Đối tượng (object) là một Thuộc tính được xác định


thực thể phần mềm bao bởi giá trị cụ thể gọi là
bọc các thuộc tính và thuộc tính thể hiện.
các phương thức liên Một đối tượng cụ thể
quan. được gọi là một thể hiện.
293
Đối Tượng Thế Giới Thực
(Real Object)

• Một đối tượng thế giới thực là một thực


thể cụ thể mà thông thường bạn có thể sờ,
nhìn thấy hay cảm nhận được.
Dữ liệu thành viên Hàm thành viên

• Tất cả có
trạng thái
(state) và
hành động
(behaviour)
294
Đối Tượng Phần Mềm
(Software Object)
• Các đối tượng phần mềm có thể được
dùng để biểu diễn các đối tượng thế giới
thực.
• Cũng có trạng thái và
hành động
– Trạng thái: thuộc tính
(attribute; property)
– Hành động: phương thức
(method) 295
Lớp (Class)
• Nhóm các đối tượng có cùng thuộc tính (hay còn gọi là
trạng thái, hoặc Dữ liệu thành viên), hành vi (Hàm thành
viên) và mối quan hệ chung.
• Lớp là viết tắt của “lớp của đối tượng”.
• Một lớp là một thiết kế (blueprint) hay mẫu (prototype)
cho các đối tượng cùng kiểu
– Ví dụ: lớp XeDap là một thiết kế chung cho nhiều đối tượng xe
đạp được tạo ra
• Một đối tượng là một thể hiện (instance) cụ thể của một
lớp.
– Ví dụ: mỗi đối tượng xe đạp là một thể hiện của lớp XeDap
• Mỗi thể hiện có thể có những thuộc tính thể hiện khác
nhau
– Ví dụ: một cây mai có hoa 6 cánh, trong khi một cây khác có hoa
296
đến 12 cánh.
Thuộc Tính Lớp
& Phương Thức Lớp

Đối tượng đa giác Lớp đa giác

Thuộc tính
(properties):
Số cạnh
Trừu tượng hóa
Màu viền
Màu nền

Phương thức
(methode):
Vẽ
Xóa
Di chuyển

297
Thuộc Tính Lớp
& Phương Thức Lớp
• Thuộc tính lớp (class attribute): đặc trưng yêu cầu của
đối tượng hoặc thực thể được biểu diễn trong một lớp.
• Thuộc tính lớp được định nghĩa bên trong định nghĩa lớp
và được chia sẻ bởi tất cả các thể hiện của lớp.
– Ví dụ: Lớp đa giác gồm các thuộc tính như: số cạnh, màu viền,
màu chữ…
• Phương thức lớp (class method): phương thức (hành
động) yêu cầu của đối tượng hoặc thực thể được biểu
diễn trong một lớp.
• Tất cả các phương thức lớp ảnh hưởng đến toàn bộ lớp
chứ không ảnh hưởng đến một lớp riêng rẽ nào.
– Ví dụ: Lớp đa giác gồm các phương thức như: vẽ, xóa, di
chuyển…

298
Thuộc tính (Attribute) và Phương
thức (Method)
• Thuộc tính (attribute) là dữ liệu trình bày các đặc điểm
về một đối tượng.
• Phương thức (method) có liên quan tới những thứ mà
đối tượng có thể làm. Một phương thức đáp ứng một
chức năng tác động lên dữ liệu của đối tượng (thuộc
tính).

299
Thông Điệp
& Truyền Thông Điệp
• Thông điệp (message) là một lời yêu cầu
một hoạt động. Gồm có:
– Đối tượng nhận thông điệp
– Tên của phương thức thực hiện
– Các tham số mà phương thức cần
• Truyền thông điệp: một đối tượng triệu
gọi một hay nhiều phương thức của đối
tượng khác để yêu cầu thông tin.

300
Đặc Điểm Quan Trọng
• Nhấn mạnh trên dữ liệu hơn là thủ tục
• Các chương trình được chia thành các đối
tượng
• Dữ liệu được che giấu và không thể được
truy xuất từ các hàm bên ngoài
• Các đối tượng có thể giao tiếp với nhau
thông qua các hàm
• Dữ liệu hay các hàm mới có thể được
thêm vào khi cần
• Theo tiếp cận từ dưới lên 301
Thuận Lợi
• So với các tiếp cận cổ điển thì OOP có
những thuận lợi sau:
– OOP cung cấp một cấu trúc module rõ ràng
• Giao diện được định nghĩa tốt
• Những chi tiết cài đặt được ẩn
– OOP giúp lập trình viên duy trì mã và sửa đổi mã tồn
tại dễ dàng (các đối tượng được tạo ra với những
khác nhau nhỏ so với những đối tượng tồn tại).
– OOP cung cấp một cơ chế tốt với các thư viện mã mà
các thành phần có thể được chọn và sửa đổi bởi lập
trình viên.
302
Trừu Tượng Hóa
(Abstraction)
• Trừu tượng hóa
– Tiến trình xem xét các khía cạnh nào đó của
bài toán.
– Biểu diễn những đặc tính, bỏ qua những chi
tiết vụn vặt hoặc những giải thích.
• Các kỹ thuật trừu tượng
– Đóng gói (encapsulation)
– Ẩn thông tin (information hiding)
– Thừa kế (inheritance)
– Đa hình (polymorphism)
303
Tính đóng gói
(Encapsulation)
• Đóng gói (encapsulation):
– Cho phép truy cập đối tượng chỉ qua thông
điệp của nó trong khi giữ kín các chi tiết riêng
tư gọi là ẩn thông tin.
– Là tiến trình che giấu việc thực thi chi tiết của
một đối tượng.

304
Ẩn thông tin
(Information Hiding)
• Đóng gói  Thuộc tính được lưu trữ
hay phương thức được cài đặt như thế
nào  được che giấu đi từ các đối
tượng khác

Việc che giấu những chi


tiết thiết kế và cài đặt từ
những đối tượng khác
được gọi là ẩn thông tin

305
Tính Thừa Kế
(Inheritance)

• Hệ thống hướng đối tượng cho phép các


lớp được định nghĩa kế thừa từ các lớp
khác
– Ví dụ, lớp xe đạp leo núi và xe đạp đua là
những lớp con (subclass) của lớp xe đạp.
• Thừa kế nghĩa là các phương thức và các
thuộc tính được định nghĩa trong một lớp
có thể được thừa kế hoặc được sử dụng
lại bởi lớp khác.
306
Tính Đa Hình
(Polymorphism)
• Đa hình: “nhiều hình thức”, hành động
cùng tên có thể được thực hiện khác nhau
đối với các đối tượng/các lớp khác nhau.
• Ngữ cảnh khác  kết quả khác
Điểm Đường Thẳng Hình Tròn Hình Vuông

Vẽ

307
Chương trình học
• Chương 1. Tổng quan
• Chương 2. Biến, biểu thức và toán tử
• Chương 3. Cấu trúc điều khiển
• Chương 4. Hàm
• Chương 5. Mảng và con trỏ
• Chương 6. Lập trình hướng đối tượng
• Chương 7. Lớp
• Chương 8. Quá tải
• Chương 9: Thừa kế 308
KỸ THUẬT LẬP TRÌNH
Chương 7. Lớp

309
Khái niệm lớp
• Lớp: khái niệm trung tâm của OOP
• Định nghĩa: Lớp là nhóm của những đối
tượng (objects) có cùng chung thuộc tính
(properties) và có những mối quan hệ
chung
• Đối tượng: thể hiện một thực thể trong thế
giới thực.

310
Cú pháp khai báo lớp
class <Ten lop>
{ private: <Khai bao cac
thanh phan private>
public: <Khai bao cac class Xedap{
thanh phan public> };
protected: <Khai bao
cac thanh phan protected>
};

311
Sử dụng lớp đối tượng
• <Tên lớp> <Tên biến lớp>;
• Tên lớp: là tên lớp đối tượng đã được định nghĩa trước khi
khai báo biến.
• Tên biến lớp: là tên đối tượng cụ thể. Tên biến lớp sẽ được
sử dụng như các biến thông thường trong C++, ngoại trừ
việc nó có kiểu lớp đối tượng.

Xedap xedap; // đúng,


nhưng khai báo:
class Xedap xedap; ; // Lỗi cú pháp là
sai cú pháp.

312
Các thành phần của lớp
• Thuộc tính của lớp (Dữ liệu thành viên)
<Kiểu dữ liệu> <Tên thuộc tính>;
Các thành phần chỉ dữ liệu của lớp, được gọi
là thuộc tính của lớp
class Xedap
{ private: int tocdo;

public: string nhanhieu;

};

313
Các thành phần của lớp
• Phương thức của lớp (Hàm thành viên)
<Kiểu trả về> <Tên phương thức>([<Các
tham số>]);
Một phương thức là một thao tác thực hiện
một số hành động đặc trưng của lớp đối
tượng. class Xedap{
private:
int tocdo;
string nhanhieu;
public:
void show();
};
Code 314
Khái niệm lớp
private
• Lớp: kiểu dữ liệu trừu tượng. protected
public
Đặc tả class TÊNLỚP
TÊN LỚP
đối [: <Quyền truy xuất> LỚPCHA ]
tượng
{ <Quyền truy xuất > :
Dữ liệu DataType1 memberdata1;
thành viên DataType2 memberdata2;
…………….
< Quyền truy xuất > :
Tập các Hàm memberFunction1();
thao tác thành viên
memberFunction2();
…………..
};

315
Quyền truy xuất
• Các thành viên lớp được liệt kê vào một trong ba loại quyền truy
xuất khác nhau:
• Các thành viên (hàm thành viên và dữ liệu thành viên) có quyền
truy cập là chung: (public) có thể được truy xuất bởi tất cả các
thành phần sử dụng lớp.
• Các thành viên riêng (hàm thành viên và dữ liệu thành viên) có
quyền truy cập là riêng: (private) chỉ có thể được truy xuất bởi các
thành viên lớp.
• Các thành viên hàm thành viên và dữ liệu thành viên) có quyền
truy cập là chung: được bảo vệ (protected) chỉ có thể được truy
xuất bởi các thành viên lớp và các thành viên của một lớp dẫn xuất.

316
Lớp đơn giản
• Ví dụ: Tạo ra
đối tượng
class Point { thuộc lớp
int xVal, yVal; void main() { Point
public: Point pt;
Khai báo void SetPt (int, int);
void OffsetPt (int, int); pt.SetPt(10,20);
Lớp pt.OffsetPt(2,2);
}; Gọi hàm
……..
void Point::SetPt (int x, int y) { trên
xVal = x; đối tượng
Định nghĩa yVal = y;
} pt.xVal = 10; // Đúng hay sai?
các hàm
thành viên void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
……….
yVal += y;
}
}
317
Quyền Lớp đơn giản
truy cập Đối tượng
1
Tên lớp
4 1 5
• Ví dụ: Dữ liệu thành viên Tạo ra
2 đối tượng
class Point { thuộc lớp
int xVal, yVal; void main() { Point
public: 3 Point pt;
void SetPt (int, int);
Khai báo
void OffsetPt (int, int);
Hàm thành viên
pt.SetPt(10,20);
Lớp pt.OffsetPt(2,2);
}; Gọi hàm
……..
void Point::SetPt (int x, int y) { trên
xVal = x; đối tượng
Định nghĩa yVal = y;
} pt.xVal = 10; // Đúng hay sai?
các hàm
thành viên void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
……….
yVal += y;
}
}
318
Lớp đơn giản
Đối tượng
• Ví dụ: 1
Tạo ra
đối tượng
class Point { thuộc lớp
int xVal, yVal; void main() { Point
public: Point pt;
Khai báo void SetPt (int, int);
void OffsetPt (int, int); pt.SetPt(10,20);
Lớp pt.OffsetPt(2,2);
}; Gọi hàm
……..
void Point::SetPt (int x, int y) { trên
xVal = x; đối tượng
Định nghĩa yVal = y;
} pt.xVal = 10; // Đúng hay sai?
các hàm
thành viên void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
……….
yVal += y;
}
}
319
Lớp đơn giản
Quyền truy cập của xVal là gì?
• Ví dụ: 1
Tạo ra
đối tượng
class Point { thuộc lớp
int xVal, yVal; void main() { Point
public: Point pt;
Khai báo void SetPt (int, int);
void OffsetPt (int, int); pt.SetPt(10,20);
Lớp pt.OffsetPt(2,2);
}; Gọi hàm
……..
void Point::SetPt (int x, int y) { trên
xVal = x; đối tượng
Định nghĩa yVal = y;
} pt.xVal = 10; // Đúng hay sai?
các hàm
thành viên void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
……….
yVal += y;
}
}
320
Lớp đơn giản
• Ví dụ: Tạo ra
đối tượng
class Point { thuộc lớp
int xVal, yVal; void main() { Point
public: Point pt;
Khai báo void SetPt (int, int);
void OffsetPt (int, int); pt.SetPt(10,20);
Lớp pt.OffsetPt(2,2);
}; Gọi hàm
……..
void Point::SetPt (int x, int y) { trên
xVal = x; đối tượng
Định nghĩa yVal = y;
} pt.xVal = 10; // Đúng hay sai?
các hàm
thành viên void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
……….
yVal += y;
}
}
321
Cơ bản về lớp
• Lớp gồm các thành viên:
– Dữ liệu thành viên (Member Data):
• Có cú pháp của định nghĩa biến
• Một đặc trưng của đối tượng
• Có thể là kiểu đã được định nghĩa hoặc tự định nghĩa
• Dữ liệu thành viên của lớp không thể có kiểu của lớp đó,
nhưng có thể là con trỏ kiểu lớp đó.
– Hàm thành viên (Member Function) hay phương thức
(Method)
• Có cú pháp của khai báo hàm
• Chỉ định các thao tác của lớp
• Một hoạt động, hành vi của đối tượng
• Đối tượng là “hộp đen” nhận và gửi thông điệp (message) 322
Cơ bản về lớp
• Đối tượng của lớp
– Đối tượng là một thể hiện cụ thể của một lớp
– Đối tượng được khai báo sau định nghĩa lớp
• Khai báo đối tượng hoặc mảng, con trỏ, tham chiếu đến đối
tượng
• Ví dụ:
Point pt;
Point polygon[9];
Point *pt1, pt2, pt3;
• Chỉ dữ liệu của đối tượng mới được cấp phát vùng nhớ,
các hàm thành viên không có vùng nhớ riêng.
• Sử dụng toán tử “.” để truy nhập vào các thành phần của
đối tượng
– Chú ý: phạm vi truy nhập (public/private)

323
Cơ bản về lớp

324
Cơ bản về lớp
• Dữ liệu thành viên:
– Nên được khai báo với từ khoá private.
– Không được khởi tạo giá trị của dữ liệu thành phần trong định
nghĩa lớp.
class Employee // khai báo tên lớp
{
private: // từ khóa cho biết không thể truy nhập từ ngoài lớp
unsigned int EmpID =1; // sai
char EmpName[30];
float EmpSalary;
public: // từ khóa cho biết có thể truy nhập từ ngoài lớp
void AddEmployee();
};

325
Cơ bản về lớp
• Các hàm thành viên định nghĩa ở ngoài lớp
– Sử dụng toán tử ::
• “Liên kết” tên hàm thành viên với tên lớp
• Các lớp khác nhau có thể có các hàm trùng tên
– Định dạng để định nghĩa các hàm thành viên
Kiểutrảvề Tênlớp::Tênhàmthànhviên( )
{ …
}
– Không phụ thuộc vào hàm public hay private
• Các hàm thành viên định nghĩa bên trong lớp
– Không cần toán tử :: và tên lớp
– Trình biên dịch sẽ cố thực hiện inline
• Ở ngoài lớp, ta chỉ định inline với từ khoá inline

326
Ví dụ
• Viết chương trình nhập từ bàn phím chiều
dài và chiều rộng của một hình chữ nhật
và hiển thị diện tích và chu vi ra màn hình.
• Phương pháp lập trình hướng đối tượng là
phương pháp giải quyết các bài toán lập
trình thông qua các đối tượng, do đó bài
này bạn cần tạo ra một đối tượng thuộc
lớp Rectangle (hình chữ nhật) giống như
sau:

327
Ví dụ
Trong đó:
•length và width là hai thuộc tính (Dữ liệu thành
viên) chỉ chiều dài và chiều rộng của hình
chữ nhật.
•getInformation() là phương thức nhập dữ liệu
(Hàm thành viên) cho thuộc
tính length và width từ bàn phím.
•display() là phương thức dùng để hiển thị thông
tin về chu vi và diện tích của hình chữ nhật ra
màn hình.
•getArea() và getPerimeter() là hai phương thức trả
về diện tích và chu vi của hình chữ nhật, hai
phương thức này được dùng trong phương
thức display().
328
Ví dụ
#include <iostream> void display()
using namespace std; { cout << "Area: " << getArea()
class Rectangle { << endl;
public: double width; cout << "Perimeter: " <<
double length; getPerimeter() << endl; } };
void getInformation() int main()
{ cin >> width; {
cin >> length; } Rectangle r1;
double getArea() r1.getInformation();
{ return width * length; r1.display();
} return 0; }
double getPerimeter()
{ return (width + length)
* 2; }
329
Điền chỗ thiếu vào các ô trống

330
Bài tập 2

Hãy viết chương trình sử dụng class gồm các thuộc tính a,
b, c.
a) Viết hàm tạo gán a=5,b=10,c=15
b) Tính trung bình cộng của 3 số
c) Cập nhật 3 số a, b và c từ bàn phím

331
Hàm inline
• Hàm không phải thành viên:
– Dùng từ khóa inline trong khai báo hàm và dòng đầu định nghĩa
hàm
• Hàm thành viên:
– Đặt mã định nghĩa hàm BÊN TRONG định nghĩa lớp  tự động
inline
• Chỉ dùng cho những hàm rất ngắn
• Mã thực sự sẽ được chèn vào nơi gọi
– Bớt “chi phí” gọi hàm
– Hiệu quả hơn, nhưng chỉ với hàm ngắn!

332
Hàm thành viên nội tuyến
• Hàm inline:
– Cải thiện tốc độ thực thi
– Tốn bộ nhớ (dành cho mã lệnh) khi thực thi.
Cách 1:
class Point { class Point {
thêm int xVal, yVal; int xVal, yVal;
Từ public: Cách 2: public:
khóa void SetPt (int, int); void SetPt (int x, int y) {
inline void OffsetPt (int, int); Định xVal = x;
}; nghĩa yVal = y;
bên }
inline void Point::SetPt (int x, int y) {
trong void OffsetPt (int x, int y) {
xVal = x;
lớp xVal += x;
yVal = y;
yVal += y;
} }
…………… };
333
Truy cập các thành phần
(dữ liệu và hàm)
• Các chế độ truy cập
– private
• Chế độ truy cập mặc định
• Chỉ có thể truy xuất được bởi các thành viên của lớp
• Các hàm thành phần và các hàm bạn, lớp bạn có thể truy
cập được
– public
• Bất kỳ hàm nào trong chương trình xử lý đối tượng đó cũng
có thể truy cập được (có thể được truy xuất bởi tất cả các
thành phần sử dụng lớp)
– protected
• Sẽ được học sau. Hiện tại giống với private
• Dữ liệu mặc định là private để đảm bảo giấu kín
• Hàm mặc định là public để có thể dùng mọi nơi
334
Các hàm truy cập và
các hàm hữu dụng
• Các hàm truy cập
– public
– Đọc/xuất dữ liệu
– Các hàm xác định tính chất
• Kiểm tra các điều kiện
• Các hàm hữu dụng (các hàm giúp đỡ)
– private
– Hỗ trợ hoạt động của các hàm thành phần public
– Không dành cho người dùng trực tiếp

335
Sự đóng gói (encapsulation)
• Sự truy xuất đến đối tượng thông qua các thông điệp
(message) mà vẫn giữ các thuộc tính private dưới
dạng thông tin ẩn
• Làm cho việc truy xuất đến dữ liệu của các lớp từ bên
ngoài lớp bị giới hạn, trở nên không cần thiết hoặc
không thể thực hiện được
• Người dùng không cần quan tâm đến cấu trúc và hoạt
động bên trong của lớp, chỉ quan tâm giải quyết vấn đề
lớn hơn
• Mục tiêu:
– tạo bức tường không thể thâm nhập được để bảo vệ, tránh
những hư hại vô tình hoặc cố ý do những lỗi chúng ta mắc phải
– Dễ dàng cô lập lỗi  dễ dàng tìm kiếm và sữa chữa

336
Hàm tạo (Constructor)
• Dùng để định nghĩa và khởi tạo đối tượng cùng 1 lúc.
• (1) Có tên trùng với tên lớp,
• (2) Không cần khai báo kiểu cho hàm tạo
• (3) Không có kiểu trả về.
• Không gọi trực tiếp, sẽ được tự động gọi khi khởi tạo đt.
• Gán giá trị, cấp vùng nhớ cho các dữ liệu thành viên.
class Point { void main() {
int xVal, yVal; Point pt1(10,20);
public: pt1.OffsetPt(2,2);
Point (int x, int y) { ……..
xVal = x; yVal = y; // Khai báo nào là sai ?
} Point pt2;
Point pt3(); ???
void OffsetPt (int x, int y) {
Point pt4 = Point(5,5);
xVal += x; yVal += y;
Point *pt5 = new Point(5,5);
}
……….
}; }
337
Vì cách khai báo này ứng với hàm tạo không tham số
Định nghĩa lớp Set

338
Định nghĩa lớp Set

339
Hàm tạo
class Set { Mềm
class Point { dẻo
private:
int xVal, yVal;
int *elems; hơn
public:
int maxCard;
Point () // Hàm tạo mặc định int card;
{ xVal = 0; yVal = 0; } public:
Point (int x, int y) { Set(const int size) {
xVal = x; yVal = y; elems = new int[size];
} maxCard = size;
Point (float len, float angle) { card = 0;
xVal = (int) (len * cos(angle)); }
yVal = (int) (len * sin(angle)); ……………
} };
void OffsetPt (int , int ); … Không cần
void main() {
}; Set s1(100);
phải nhớ
void main() { Set s2(20); gọi hàm
Point p1; Set s3(1000); … EmptySet()
Point p2(10,20); } khi khởi tạo
Point p3(60.3, 3.14);
}
340
Hàm tạo
class Set { Mềm
class Point { dẻo
private:
int xVal, yVal;
int *elems; hơn
public:
int maxCard;
Point () // Hàm tạo mặc định int card;
{ xVal = 0; yVal = 0; } public:
Point (int x, int y) { Set(const int size) {
xVal = x; yVal = y; elems = new int[size];
} maxCard = size;
Point (float len, float angle) { card = 0;
xVal = (int) (len * cos(angle)); }
yVal = (int) (len * sin(angle)); ……………
} };
void OffsetPt (int , int ); … Không cần
void main() {
}; Set s1(100);
phải nhớ
void main() { Set s2(20); gọi hàm
Point p1; Set s3(1000); … EmptySet()
Point p2(10,20); } khi khởi tạo
Point p3(60.3, 3.14);
}
341
Hàm tạo
class Set { Mềm
class Point { dẻo
private:
int xVal, yVal;
int *elems; hơn
public:
int maxCard;
Point () // Hàm tạo mặc định int card;
{ xVal = 0; yVal = 0; } public:
Point (int x, int y) { Set(const int size) {
xVal = x; yVal = y; elems = new int[size];
} maxCard = size;
Point (float len, float angle) { card = 0;
xVal = (int) (len * cos(angle)); }
yVal = (int) (len * sin(angle)); ……………
} };
void OffsetPt (int , int ); … Không cần
void main() {
}; Set s1(100);
phải nhớ
void main() { Set s2(20); gọi hàm
Point p1; Set s3(1000); … EmptySet()
Point p2(10,20); } khi khởi tạo
Point p3(60.3, 3.14);
}
342
Hàm tạo
• Hàm tạo sao chép khởi tạo đối tượng dựa trên một đối
tượng khác thuộc cùng lớp.
• Mỗi lớp có một hàm tạo sao chép mặc định – hàm này
có một tham số là đối tượng cùng lớp.
• Ta có thể định nghĩa lại hàm tạo sao chép.
Date(Date& d)
• Ví dụ
void main()
{
Date d1(12,4,1997);
Date d2(d1); // hàm tạo sao chép mặc định
Date d3=d1; // hàm tạo sao chép mặc định
}

343
Hàm hủy
• Giải phóng 1 đối tượng trước khi nó được thu hồi.
• Cú pháp: Có tên trùng với tên lớp, có dấu ~ phía trước
• ~TenLop() { ……... }
• Không gọi trực tiếp, sẽ được tự động gọi khi hủy bỏ đối
tượng.
• Thu hồi vùng nhớ cho các dữ liệu thành viên là con trỏ.
class Set { Set TestFunct1(Set s1) {
private: Set *s = new Set(50);
int *elems; return *s;
Tổng cộng
int maxCard; }
có bao
int card; nhiêu lần
void main() {
public:
Set s1(40), s2(50); hàm hủy
Set(const int size) { …… }
~Set() { delete[] elems; }
s2 = TestFunct1(s1); được gọi ?
}
….
}; 4

344
Hàm hủy
• Giải phóng 1 đối tượng trước khi nó được thu hồi.
• Cú pháp: ~TenLop() { ……... }
• Không gọi trực tiếp, sẽ được tự động gọi khi hủy bỏ đối
tượng.
• Thu hồi vùng nhớ cho các dữ liệu thành viên là con trỏ.

class Set { Set TestFunct1(Set s1) {


private: Set *s = new Set(50);
int *elems; return *s;
Tổng cộng
int maxCard; }
có bao
int card; nhiêu lần
void main() {
public:
Set s1(40), s2(50); hàm hủy
Set(const int size) { …… }
~Set() { delete[] elems; }
s2 = TestFunct1(s1); được gọi ?
}
….
}; 4

345
Hàm tạo và hàm hủy mặc định
• Hàm tạo mặc định:
– Thường tạo hàm tạo và hàm hủy khi lớp có
thành phần cấp phát động
– Nếu lớp không có hàm tạo thì trình biên dịch
sẽ cung cấp 1 hàm tạo mặc định không đối.
Hàm này chỉ cấp phát bộ nhớ, không khởi tạo
dữ liệu.
– Nếu lớp có hàm tạo thì sẽ không có hàm tạo
mặc định mới được phát sinh.
• Hàm hủy mặc định tương tự như trên.
346
Đối số mặc định

class Point { class Point {


int xVal, yVal; int xVal, yVal;
public: public:
Point (){}; Point (int x = 0, int y = 0);
Point (int x = 0, int y = 0); Point (float x=0, float y=0, int i=0);
//... //...
}; }; Tối nghĩa
Mơ hồ
void main() { void main() {
Point p1; // như là ??? Point p2(1.6, 5.0); // như là ???
Point p2(10); // như là ??? Point p3(10,20, 10); // như là ???
Point p3(10,20); Point p4; // ?????
Point p4(, 20); // ????? …..
….. }
}
347
Cho biết kết quả?
class Point {
int xVal, yVal;
public:
Point(int x = 0, int y = 0){
xVal = x ; yVal = y ;
}
void Print(){
cout<<" ("<<xVal <<","<<yVal<<" )";
}
};
void main() {
Point pt(5);
pt.Print();
}

Đáp án (5,0)
348
Đối số mặc định

class Point { class Point {


int xVal, yVal; int xVal, yVal;
public: public:
Point (){}; Point (int x = 0, int y = 0);
Point (int x = 0, int y = 0); Point (float x=0, float y=0, int i=0);
//... //...
}; }; Tối nghĩa
Mơ hồ
void main() { void main() {
Point p1; // như là ??? Point p2(1.6, 5.0); // như là ???
Point p2(10); // như là ??? Point p3(10,20, 10); // như là ???
Point p3(10,20); Point p4; // ?????
Point p4(, 20); // ????? …..
….. }
}
349
Đối số thành viên ẩn
• Con trỏ *this:
– Là 1 thành viên ẩn, có thuộc tính là private.
– Trỏ tới chính bản thân đối tượng.
void Point::OffsetPt (int x, int y) { void Point::OffsetPt (int x, int y) {
xVal += x; this->xVal += x;
yVal += y; this->yVal += y;
} }

• Có những trường hợp sử dụng *this là dư thừa (Ví dụ trên)


• Tuy nhiên, có những trường hợp phải sử dụng con trỏ *this

350
Đối số thành viên ẩn
• Con trỏ *this:
– Là 1 thành viên ẩn, có thuộc tính là private.
– Trỏ tới chính bản thân đối tượng.
void Point::OffsetPt (int x, int y) { void Point::OffsetPt (int x, int y) {
xVal += x; this->xVal += x;
yVal += y; this->yVal += y;
} }

• Có những trường hợp sử dụng *this là dư thừa (Ví dụ trên)


• Tuy nhiên, có những trường hợp phải sử dụng con trỏ *this

351
Sử dụng con trỏ this
• Các lời gọi hàm thành phần liên tiếp
– Nhiều hàm được gọi trong cùng 1 lệnh
– Hàm trả về 1 tham chiếu đến cùng đối tượng
đó
{ return *this; }
– Các hàm khác hoạt động trên con trỏ này
– Hàm không trả về tham chiếu phải được gọi
sau cùng

352
Sử dụng con trỏ this
1. #include <iostream>
2. class Time
3. { int hour, minute, second ;
4. public:
5. Time( int = 0, int = 0, int = 0 ); // default constructor
6. Time &setTime(int,int,int); // set hour,minute,second
7. Time &setHour( int ); // set hour
8. Time &setMinute( int ); // set minute
9. Time &setSecond( int ); // set second
10. int getHour() const; // return hour
11. int getMinute() const; // return minute
12. int getSecond() const; // return second
13. void printUniversal() const; // print universal time
14. void printStandard() const; // print standard time
15. }; // end class Time
353
16. Time::Time( int hr, int min, int sec )
17. { setTime( hr, min, sec );
18. } // end Time constructor
19. Time &Time::setTime( int h, int m, int s )
20. { setHour( h ); setMinute( m ); setSecond( s );
21. return *this; // enables cascading
22. } // end function setTime
23. Time &Time::setHour( int h )
24. { hour = ( h >= 0 && h < 24 ) ? h : 0;
25. return *this; // enables cascading
26. } // end function setHour
27. Time &Time::setMinute( int m )
28. { minute = ( m >= 0 && m < 60 ) ? m : 0;
29. return *this; // enables cascading
30. } // end function setMinute
354
31. Time &Time::setSecond( int s )
32. { second = ( s >= 0 && s < 60 ) ? s : 0;
33. return *this; // enables cascading
34. } // end function setSecond
35. int Time::getHour() const
36. { return hour;
37. } // end function getHour
38. int Time::getMinute() const
39. { return minute;
40. } // end function getMinute
41. int Time::getSecond() const
42. { return second;
43. } // end function getSecond
44. void Time::printUniversal() const
45. { cout << hour << ":“ << minute << ":“ << second;
46. } // end function printUniversal 355
47. void Time::printStandard() const
48. { cout << ((hour == 0 || hour == 12 ) ? 12 : hour % 12 )
49. << ":" << minute << ":" << second
50. << ( hour < 12 ? " AM" : " PM" );
51. } // end function printStandard
52. void main()
53. { Time t;
54. t.setHour( 18 ).setMinute( 30 ).setSecond( 22 );
55. cout << "Universal time: ";
56. t.printUniversal();
57. cout << "\nStandard time: ";
58. t.printStandard();
59. cout << "\n\nNew standard time: ";
60. t.setTime( 20, 20, 20 ).printStandard();
61. cout << endl;
62. } // end main 356
Bạn (Friend) – Đặt vấn đề
• Đôi khi chúng ta cần cấp quyền truy xuất cho một hàm
tới các thành viên không là các thành viên chung của
một lớp. Một truy xuất như thế được thực hiện bằng
cách khai báo hàm như là bạn của lớp.
• - Nếu một thành viên của lớp được quy định là private
hoặc protected thì chỉ có các hàm thành viên của lớp
mới có quyền truy cập đến nó.
• - Nếu một phương thức không phải là thành viên của lớp
muốn truy cập đến, thì nó phải là hàm bạn của lớp đó.
• - Phương thức bạn có thể được khai báo nhờ từ khóa
friend

357
Bạn (Friend) – Đặt vấn đề
Tập Các Hàm SetToReal
Số Nguyên dùng để chuyển
tập số nguyên
thành tập số thực
class IntSet {
public: void IntSet::SetToReal (RealSet &set) {
//... set.card = card;
private: for (register i = 0; i < card; ++i)
int elems[maxCard]; set.elems[i] = (float) elems[i];
int card; }
};
class RealSet {
public: Làm thế nào
//... để thực hiện
Tập Các private: được việc truy
Số Thực float elems[maxCard]; xuất
int card; đến thành viên
}; Private ?

358
Bạn (Friend) – Đặt vấn đề
Tập Các Hàm SetToReal
Số Nguyên dùng để chuyển
tập số nguyên
thành tập số thực
class IntSet {
public: void IntSet::SetToReal (RealSet &set) {
//... set.card = card;
private: for (register i = 0; i < card; ++i)
int elems[maxCard]; set.elems[i] = (float) elems[i];
int card; }
};
class RealSet {
public: Làm thế nào
//... để thực hiện
Tập Các private: được việc truy
Số Thực float elems[maxCard]; xuất
int card; đến thành viên
}; Private ?

359
Bạn (Friend)
• Hàm bạn:
– Có quyền truy xuất đến tất cả các dữ liệu và
hàm thành viên (protected + private) của 1 lớp.
– Lý do:
• Cách định nghĩa hàm chính xác.
• Hàm cài đặt không hiệu quả.
• Lớp bạn:
– Tất cả các hàm trong lớp bạn: là hàm bạn.
class A; class IntSet { ……….. }
class B { // ………. class RealSet { // ……….
friend class A; friend class IntSet;
}; };
360
Các hàm thành viên của lớp A đều là bạn của lớp B
Hàm bạn và lớp bạn
• Tính chất của tình bạn
– Tình bạn được cấp, không tự dưng có được
• Lớp B là friend của lớp A
– Lớp A phải khai báo rõ ràng lớp B là friend
– Không đối xứng
• Lớp B là friend của lớp A
• Lớp A không nhất thiết là friend của lớp B
– Không bắt cầu
• Lớp A là friend của lớp B
• Lớp B là friend của lớp C
• Lớp A không nhất thiết là friend của lớp C

361
Hàm bạn (Friend)
• Cách 1 - Dùng từ khoá friend để xây dựng hàm trong
định nghĩa lớp.
• Cú pháp class A
{
private:
// Khai báo các thuộc tính
public: ...
// Xây dựng các hàm bạn của lớp A
friend void f1(...) { ... }
friend double f2(...) { ... }
friend A f3(...) { ... } ... } ;
362
Hàm bạn (Friend)
• Cách 1: Khai báo hàm thành viên của lớp
IntSet là bạn (friend) của lớp RealSet.
class IntSet {
Giữ nguyên định public:
nghĩa của lớp IntSet //...
private:
int elems[maxCard];
int card;
void SetToReal (RealSet &);
};
class RealSet {
Thêm dòng khai báo public:
Friend cho //...
hàm thành viên friend void IntSet::SetToReal (RealSet &);
SetToReal private:
float elems[maxCard];
int card;
}; 363
HÀM BẠN (Friend Function)
• Dùng từ khoá friend để khai báo hàm trong lớp và xây
dựng hàm bên ngoài như các hàm thông thường (không
dùng từ khoá friend).
• Ví dụ một lớp A có ba hàm bạn:
class A { // Xây dựng các hàm f1, f2, f3
void f1(...) { ... }
private: double f2(...) { ... }
// Khai báo các thuộc tính A f3(...) { ... }
public: ...
// Khai báo các hàm bạn của lớp A
friend void f1(...);
friend double f2(...);
friend A f3(...) ; ... } ;
364
HÀM BẠN (Friend Function)
• Hàm bạn của nhiều lớp
• Một hàm là bạn của nhiều lớp, thì nó có quyền truy
nhập tới tất cả các thuộc tính của các đối tượng trong
các lớp này.
// Định nghĩa lớp B
class A; // Khai báo trước lớp A
class B{
class B; // Khai báo trước lớp B
// Khai báo f là bạn của B
class C; // Khai báo trước lớp C
friend void f(...) ; } ; /
// Định nghĩa lớp A
/ Định nghĩa lớp C
class A {
class C{
// Khai báo f là bạn của A
// Khai báo f là bạn của C
friend void f(...) ;
friend void f(...) ; };
};
// Xây dựng hàm f
void f(...) { ... }

365
Hàm bạn (Friend)
• Cách 2:
– Chuyển hàm SetToReal ra ngoài (độc lập).
– Khai báo hàm đó là bạn của cả 2 lớp.
class IntSet {
public: void SetToReal (IntSet& iSet,
//... RealSet& rSet )
friend void SetToReal (IntSet &, RealSet&);
private: {
int elems[maxCard]; rSet.card = iSet.card;
int card; for (int i = 0; i < iSet.card; ++i)
}; rSet.elems[i] =
class RealSet { (float) iSet.elems[i];
public:
//... }
friend void SetToReal (IntSet &, RealSet&);
private: Hàm độc lập
float elems[maxCard]; là bạn(friend)
int card;
}; của cả 2 lớp.
366
Hàm bạn (Friend)
• Cách 2:
– Chuyển hàm SetToReal ra ngoài (độc lập).
– Khai báo hàm đó là bạn của cả 2 lớp.
class IntSet {
public: void SetToReal (IntSet& iSet,
//... RealSet& rSet )
friend void SetToReal (IntSet &, RealSet&);
private: {
int elems[maxCard]; rSet.card = iSet.card;
int card; for (int i = 0; i < iSet.card; ++i)
}; rSet.elems[i] =
class RealSet { (float) iSet.elems[i];
public:
//... }
friend void SetToReal (IntSet &, RealSet&);
private: Hàm độc lập
float elems[maxCard]; là bạn(friend)
int card;
}; của cả 2 lớp.
367
HÀM BẠN (Friend Function)
• Tính chất của hàm bạn
• - Trong thân hàm bạn của một lớp có thể truy nhập tới
các thuộc tính (kể cả thuộc tính riêng) của các đối tượng
thuộc lớp này. Đây là sự khác nhau duy nhất giữa hàm
bạn và hàm thông thường.
• - Chú ý rằng hàm bạn không phải là phương thức của
lớp. Phương thức của lớp có một đối ẩn (ứng với con trỏ
this) và lời gọi của phương thức phải gắn với một đối
tượng nào đó (địa chỉ đối tượng này được truyền cho
con trỏ this). Lời gọi của hàm bạn giống như lời gọi của
hàm thông thường.
•  Lý do dùng hàm bạn
 Cách định nghĩa hàm chính xác.
 Hàm cài đặt không hiệu quả.
368
Hàm bạn
class <Tên lớp>{
// Khai báo các thành phần lớp như thông thường
// Khai báo hàm bạn
friend <Kiểu trả về> <Tên hàm bạn>([<Các tham số>]);
};

Khi đó, định nghĩa chi tiết hàm bạn được thực hiện như
định nghĩa một hàm tự do thông thường:
<Kiểu trả về> <Tên hàm bạn>([<Các tham số>]){
// Có thể truy nhập trực tiếp các thành phần private
// của lớp đã khai báo
}
369
Hàm bạn
• Một hàm tự do là hàm bạn của một lớp
– Mặc dù hàm bạn được khai báo khuôn mẫu hàm trong phạm vi
lớp, nhưng hàm bạn tự do lại không phải là một phương thức
của lớp. Nó là hàm tự do, việc định nghĩa và sử dụng hàm này
hoàn toàn tương tự như các hàm tự do khác.
– Việc khai báo khuôn mẫu hàm bạn trong phạm vi lớp ở vị trí
nào cũng được: hàm bạn không bị ảnh hưởng bởi các từ khóa
private, protected hay public trong lớp.
– Trong hàm bạn, có thể truy nhập trực tiếp đến các thành phần
private và protected của đối tượng có kiểu lớp mà nó làm bạn
(truy nhập thông qua đối tượng cụ thể).

370
Hàm bạn
#include <iostream> int HienThiChieuDai(HinhChuNhat hcn) {
using namespace std; hcn.chieuDai += 10;
class HinhChuNhat { return hcn.chieuDai;
private: }
int chieuDai;
int chieuRong; int main() {
public: HinhChuNhat hcn = HinhChuNhat(10,
HinhChuNhat(int chieuDai, int 20);
chieuRong) { cout << "Chieu dai cua hinh chu nhat la:
this->chieuDai = chieuDai; " << HienThiChieuDai(hcn)<<endl;
this->chieuRong = chieuRong; return 0;
} }
HinhChuNhat(): chieuDai(0) { }
friend int
HienThiChieuDai(HinhChuNhat); //friend
function
};

Đáp án: 20
371
Ví dụ về lớp bạn

372
Hàm thành viên và hàm độc lập
class SP class SP
{ double r; { double r;
double i; double i;
public: public:
SP cong(SP s2) friend SP cong(SP s1,SP s2)
{ SP temp; { SP temp;
temp.r = r+ s2.r ; temp.r = s1.r+ s2.r ;
temp.i = i+ s2.i ; temp.i = s1.i+ s2.i ;
return temp; return temp;
} }
}; };
Cách dùng: Cách dùng:
SP s, s1, s2; SP s, s1, s2;
s=s1.cong(s2); s=cong(s1,s2);
373
1. // Use a friend function.
2. #include <iostream>
3. const int IDLE=0;
4. const int INUSE=1;
5. class C2; // note : forward declaration
6. class C1
7. { int status; // IDLE if off, INUSE if on screen
8. // ...
9. public:
10. void set_status(int state);
11. friend int idle(C1 a, C2 b);
12. };
13.class C2
14. { int status; // IDLE if off, INUSE if on screen
15. // ...
16. public:
17. void set_status(int state);
18. friend int idle(C1 a, C2 b); 374

19. };
20.void C1::set_status(int state) { status = state; }
21.void C2::set_status(int state) { status = state; }
22.// idle( ) is a friend of C1 and C2.
23.int idle(C1 a, C2 b)
24. { if(a.status || b.status) return 0; else return 1; }
25.void main()
26. { C1 x;
27. C2 y;
28. x.set_status(IDLE);
29. y.set_status(IDLE);

30. if(idle(x, y)) cout << "Screen Can Be Used.\n";


31. else cout << "Pop-up In Use.\n";

32. x.set_status(INUSE);

33. if(idle(x, y)) cout << "Screen Can Be Used.\n";


34. else cout << "Pop-up In Use.\n"; 375

35. }
Hàm bạn và lớp bạn
• Chú ý: Hàm bạn không có con trỏ this
• Lợi ích của hàm bạn và lớp bạn:
– Quá tải toán tử
• Sẽ được học sau
– Đơn giản một vài hàm I/O
• Sẽ được học sau
– Các thành phần của 2 lớp (hoặc nhiều hơn)
có liên quan đến phần khác của chương trình
• Ví dụ: Hàm print cần xuất thông tin của 2 đối
tượng thuộc 2 lớp khác nhau
376
Hàm bạn và lớp bạn
• Thuận lợi:
– Cung cấp một sự tự do trong việc thiết kế giao diện
• Khó khăn:
– Đi ngược lại nguyên lý OOP
• Phá vỡ encapsulation
• Có thể hạn chế sự vi phạm tính toàn vẹn trong một
chừng mực nào đó
– Khi 1 hàm là hàm bạn của 1 lớp, nó có thể truy cập
dữ liệu của lớp đó. Người lập trình cần biết mã nguồn
 hạn chế thay đổi lớp đó đến mức có thể.
– Không lạm dụng friend

377
Toán tử new
• Xem xét lệnh sau
Time *timePtr;
timePtr = new Time;
• Toán tử new
• Tạo đối tượng với kích thước vùng nhớ kiểu Time
– Thành công : trả về con trỏ trỏ đến vùng nhớ đó
– Lỗi (nếu không đủ bộ nhớ, …) : trả về 0
• Gọi hàm tạo mặc định cho đối tượng
• Trả về con trỏ kiểu Time
• Cung cấp bộ khởi tạo
double *ptr = new double( 3.14159 );
Time *timePtr = new Time( 12, 0, 0 );
• Cấp phát mảng
int *gradesArray = new int[ 10 ];
378
Toán tử delete
• Giải phóng vùng nhớ được cấp phát động cho đối tượng
– Gọi hàm hủy cho đối tượng
– Giải phóng vùng nhớ dành cho đối tượng
• Vùng nhớ đó có thể dùng để cấp phát lại cho các
đối tượng khác
– Giải phóng mảng
• delete [] gradesArray;
• Giải phóng mảng gradesArray đang trỏ tới
• Nếu là mảng đối tượng
• Đầu tiên gọi hàm hủy cho từng đối tượng
• Sau đó giải phóng vùng nhớ
• Ví dụ
delete timePtr;
379
Toán tử phạm vi
• Toán tử :: dùng để xác định chính xác hàm
(thuộc tính) được truy xuất thuộc lớp nào.
• Câu lệnh: pt.OffsetPt(2,2);
<=> pt.Point::OffsetPt(2,2);
• Cần thiết trong một số trường hợp:
– Cách gọi hàm trong thừa kế.
– Tên thành viên bị che bởi biến cục bộ.
Ví dụ: Point(int xVal, int yVal) {
Point::xVal = xVal;
Point::yVal = yVal;
}
380
Mảng các đối tượng
• Sử dụng hàm tạo không đối số (hàm tạo mặc
nhiên - default constructor).
VD: Point pentagon[5];
• Sử dụng bộ khởi tạo mảng:
VD: Point triangle[3] =
{ Point(4,8), Point(10,20), Point(35,15) };
Ngắn gọn:
Set s[4] = { 10, 20, 30, 40 };
tương đương với:
Set s[4] = { Set(10), Set(20), Set(30), Set(40) };

381
Mảng các đối tượng
• Sử dụng dạng con trỏ:
– Cấp vùng nhớ:
VD: Point *pentagon = new Point[5];
– Thu hồi vùng nhớ:
delete[] pentagon;
delete pentagon; // Thu hồi vùng nhớ đầu
class Polygon {
public: Không cần biết kích
//... thước mảng.
private:
Point *vertices; // các đỉnh
int nVertices; // số các đỉnh
};

382
Mảng các đối tượng
• Sử dụng dạng con trỏ:
– Cấp vùng nhớ:
VD: Point *pentagon = new Point[5];
– Thu hồi vùng nhớ:
delete[] pentagon;
delete pentagon; // Thu hồi vùng nhớ đầu
class Polygon {
public: Không cần biết kích
//... thước mảng.
private:
Point *vertices; // các đỉnh
int nVertices; // số các đỉnh
};

383
Mảng các đối tượng
• Sử dụng dạng con trỏ:
– Cấp vùng nhớ:
VD: Point *pentagon = new Point[5];
– Thu hồi vùng nhớ:
delete[] pentagon;
delete pentagon; // Thu hồi vùng nhớ đầu
class Polygon {
public: Không cần biết kích
//... thước mảng.
private:
Point *vertices; // các đỉnh
int nVertices; // số các đỉnh
};

384
Ví dụ mảng các đối tượng
• Nhập điểm số lượng sinh viên từ bàn phím
• Nhập mã sv, điểm số từng sinh viên
• Tính điểm trung bình
• Gợi ý:
Tạo class Sinhvien
void getinfo ()
void display ()
stulist[i].getinfo(); // ten-mang-doi-tuong[chi-
so].hamthanhvien()
total = total + stulist[i].getmarks();
// hàm main
Student stulist[100];
385
float total, average;
Ví dụ mảng các đối tượng
int main()
#include <iostream> {
using namespace std; Student stulist[100];
class Student float total, average;
{ int no, i;
private: int rollno;
int marks; total = 0.0;
public: cout << "\n Nhap vao so luong sinh vien: ";
void getinfo () cin >> no ;
{cout << "Nhap vao ma so sinh vien:
";
for (i=0; i< no ; i++)
cin >> rollno ;
{
cout << "Nhap vao diem: ";
stulist[i].getinfo();
cin >> marks ;
total = total + stulist[i].getmarks();
} //ket thuc ham getinfo
}
void display ()
cout << "Ma so Diem"<< endl;
{cout<<rollno<<'\t'<<marks<< endl;
for (i=0; i< no; i++)
} // ket thuc ham display
{
stulist[i].display();
int getmarks()
}
{return marks;
average = total / no;
} // ket thuc ham getmarks
cout << "Diem trung binh cac sinh vien = " <<
average;
};// ket thuc lop Student
}// ket thuc ham main
386
Phạm vi lớp
• Thành viên trong 1 lớp:
– Che các thực thể trùng tên trong phạm vi.
// ………
int fork (void); // fork hệ thống
class Process {
int fork (void); // fork thành viên fork thành viên
//... che đi fork toàn cục
}; trong phạm vi lớp
Process
// ………
int Process::func1 (void)
{ int x = fork(); // gọi fork cục bộ
int pid = ::fork(); // gọi hàm fork hệ thống
//...
}

387
Phạm vi lớp
• Thành viên trong 1 lớp:
– Che các thực thể trùng tên trong phạm vi.
// ………
int fork (void); // fork hệ thống
class Process {
int fork (void); // fork thành viên fork thành viên
//... che đi fork toàn cục
}; trong phạm vi lớp
Process
// ………
int Process::func1 (void)
{ int x = fork(); // gọi fork cục bộ
int pid = ::fork(); // gọi hàm fork hệ thống
//...
}

388
Phạm vi lớp
• Lớp toàn cục: đại đa số lớp trong C++.
• Lớp lồng nhau: lớp chứa đựng lớp.
• Lớp cục bộ: trong 1 hàm hoặc 1 khối.
class Rectangle { // Lớp lồng nhau void Render (Image &i)
public: {
Rectangle (int, int, int, int); class ColorTable {
//.. public:
private: ColorTable () { /* ... */ }
class Point { AddEntry (int r, int g, int b)
public: { /* ... */ }
Point(int a, int b) { … } //...
private:
int x, y; };
}; ColorTable colors;
Point topLeft, botRight; //...
}; }
Rectangle::Point pt(1,1); // sd ở ngoài ColorTable ct; // SAI
389
Cấu trúc và hợp
• Cấu trúc (structure):
– Bắt nguồn từ ngôn ngữ C.
– Tương đương với class với các thuộc tính là public.
– Sử dụng như class.

struct Point { class Point {


Point (int, int); public:
Point(int, int);
void OffsetPt(int, int);
void OffsetPt(int, int);
int x, y; int x, y;
}; };

Point p = { 10, 20 }; Có thể khởi tạo dạng này


nếu không có định nghĩa
hàm xây dựng
390
Cấu trúc và hợp
• Hợp (union):
– Tất cả thành viên ánh xạ đến cùng 1 địa chỉ bên trong đối tượng
chính nó (không liên tiếp).
– Kích thước = kích thước của dữ liệu lớn nhất.
union Value { class Object {
long integer; private:
double real; enum ObjType {intObj, realObj,
char *string; strObj, listObj};
Pair list; ObjType type; // kiểu đối tượng
//... Value val; // giá trị của đối tượng
}; //...
};
class Pair {
Value *head;
Value *tail;
//... Kích thước của Value là
}; 8 bytes = sizeof(double)
391
Các trường bit
• Điều khiển đối tượng ở mức bit.
VD: Truy xuất các bit trong header của gói tin.
typedef unsigned int Bit;
class Packet {
Bit type : 2; // rộng 2 bit
Bit acknowledge : 1; // rộng 1 bit
Bit channel : 4; // rộng 4 bit
Bit sequenceNo : 4; // rộng 4 bit
Bit moreData : 1; // rộng 1 bit
//...
}; // …………
enum PacketType { dataPacket, controlPacket, Packet p;
supervisoryPacket }; p.type = controlPacket;
enum Bool { false, true }; p.acknowledge = true;

392
Danh sách khởi tạo thành viên
Member Initialization List

• Tương đương việc gán giá trị dữ liệu thành viên.


class Point { class Image {
int xVal, yVal; public:
Image(const int w, const int h);
public: private:
Point (int x, int y) { int width;
xVal = x; int height;
yVal = y; //...
} };
Image::Image(const int w, const int h) {
// ……………………
width = w;
}; height = h;
//.....................
}
Point::Point (int x, int y)
: xVal(x), yVal(y)
Image::Image (const int w, const int h)
{ } : width(w), height(h)
{ //............... }
393
Danh sách khởi tạo thành viên
• Tương đương việc gán giá trị dữ liệu thành viên.
class Point { class Image {
int xVal, yVal; public:
Image(const int w, const int h);
public: private:
Point (int x, int y) { int width;
xVal = x; int height;
yVal = y; //...
} };
Image::Image(const int w, const int h) {
// ……………………
width = w;
}; height = h;
//.....................
}
Point::Point (int x, int y)
: xVal(x), yVal(y)
Image::Image (const int w, const int h)
{ } : width(w), height(h)
{ //............... }
394
Thành viên hằng (Constant
Members)
• Hằng dữ liệu thành viên:
class Image {
public:
Image(const int w, const int h); Khai báo bình thường
private: như dữ liệu thành viên
const int width;
const int height;
//...
};

class Image {
const int width = 256;
Khởi tạo const int height = 168;
SAI //...
};

Image::Image (const int w, const int h) Khởi tạo ĐÚNG


: width(w), height(h) thông qua danh sách
{ //................ } khởi tạo thành viên 395
Thành viên hằng
• Hằng đối tượng: không được thay đổi giá trị.
• Hàm thành viên hằng:
– Được phép gọi trên hằng đối tượng.
– Không được thay đổi giá trị dữ liệu thành viên.
class Set { void main() {
public: const Set s;
Set(void){ card = 0; } s.AddElem(10); // SAI
bool Member(const int) const;
s.Member(10); // ok
void AddElem(const int);
//...
}; }
bool Set::Member (const int elem) const
{ //...
}

396
Thành viên tĩnh (Static
Members)
• Dữ liệu thành viên tĩnh:
– Dùng chung 1 bản sao chép (1 vùng nhớ) chia sẻ
cho tất cả đối tượng của lớp đó.
– Sử dụng: <TênLớp>::<TênDữLiệuThànhViên>
– Thường dùng để đếm số lượng đối tượng.
class Window {
// danh sách liên kết tất cả Window
static Window *first; Khai báo
// con trỏ tới window kế tiếp
Window *next;
//...
}; Khởi tạo
dữ liệu
Window *Window::first = &myWindow; thành viên
// ……………. tĩnh
397
Thành viên tĩnh
• Dữ liệu thành viên tĩnh
– Dữ liệu “cấp độ lớp”
• Thuộc tính của lớp, không phải của 1 đối tượng
nào đó của lớp.
– Hiệu quả khi chỉ cần 1 biến cho lớp là đủ
– Có vẻ như biến toàn cục, nhưng chỉ có phạm vi lớp
• Chỉ các đối tượng thuộc lớp đó mới có thể truy cập
– Cần khởi tạo 1 lần duy nhất
– Tồn tại ngay cả khi không có đối tượng nào của lớp
đó tồn tại, không phụ thuộc vào bất kỳ đối tượng nào
– Có thể là public, private hoặc protected

398
Thành viên tĩnh
• Hàm thành viên tĩnh:
– Tương đương với hàm toàn cục.
– Gọi thông qua: <TênLớp>::<TênHàm>
class Window {
Khai báo
// ……….
Định nghĩa
static void PaintProc (Event *event) { ….. }
hàm thành
// ………
viên tĩnh
};

void main() {
// …………….
Window::PainProc(); Truy xuất
hàm thành
} viên tĩnh

399
Thành viên tĩnh
• Hàm thành viên tĩnh
– Các hàm static không có con trỏ this
• Các dữ liệu và hàm thành viên static tồn
tại mà không phụ thuộc vào các đối tượng
– Có thể gọi hàm static ngay cả khi không
có đối tượng nào của lớp đó tồn tại
– Có thể sử dụng một hàm thành viên
static để truy vấn một đối tượng để tìm ra
nó là đối tượng nào.
– Có ích trong việc gỡ rối một chương trình
giữa những tình huống khác nhau
400
Ví dụ về thành viên tĩnh
#include <iostream>
using namespace std;
class Teacher {
public:
static int n;
public :
Teacher (){ cout<<" "<<n++ ; }
};
int Teacher::n = 0;
int main() {
Teacher t1;
Teacher t2;
Teacher t3;
cout<<" "<<t1.n ;
return 0;
} 401
Truy xuất các dữ liệu static
• Ví dụ:
class NGUOI
{ float tn; //thu nhập từng người
static int tongsn; //tổng số người có trong class
static float tongtn;//tổng thu nhập của các object
public:

} nguoi1, nguoi2;
• Truy xuất tongtn:
– nguoi1.tongtn
– nguoi2.tongtn
– NGUOI::tongtn // có thể dùng cả khi chưa có đối tượng nào
• nguoi1.tn, nguoi2.tn sẽ chiếm 2 vùng nhớ khác nhau
• nguoi1.tongtn, nguoi2. tongtn chiếm cùng 1 vùng nhớ
402
Thành phần static của lớp
1. #include <iostream>
2. #include <new.h> // C++ standard new operator
3. #include <string.h>
4. class Employee
5. { char *firstName;
6. char *lastName;
7. // static data member
8. static int count; // number of objects instantiated
9. public:
10. Employee( const char *, const char * ); // constructor
11. ~Employee(); // destructor
12. const char *getFirstName() const; // return first name
13. const char *getLastName() const; // return last name
14. // static member function
15. static int getCount(); // return # objects instantiated
16. }; // end class Employee 403
17. int Employee::count = 0;
18. int Employee::getCount()
19. { return count;
20. }
21. Employee::Employee( const char *first, const char *last )
22. { firstName = new char[ strlen( first ) + 1 ];
23. strcpy( firstName, first );
24. lastName = new char[ strlen( last ) + 1 ];
25. strcpy( lastName, last );
26. ++count; // increment static count of employees
27. cout << "Employee constructor for " << firstName
28. << ' ' << lastName << " called." << endl;
29. } // end Employee constructor
30. Employee::~Employee()
31. { cout << "~Employee() called for " << firstName
32. << ' ' << lastName << endl;
33. delete [] firstName; delete [] lastName;
34. --count; // decrement static count of employees
35. } 404
36. const char *Employee::getFirstName() const
37. { // const before return type prevents client from
// modifying private data; client should copy returned
// string before destructor deletes storage to prevent
// undefined pointer
38. return firstName;
39. } // end function getFirstName
40. const char *Employee::getLastName() const
41. { // const before return type prevents client from
// modifying private data; client should copy returned
// string before destructor deletes storage to prevent
// undefined pointer
42. return lastName;
43. } // end function getLastName

405
void main()
{ cout << "Number of employees before instantiation is "
<< Employee::getCount() << endl; // use class name
Employee *e1Ptr = new Employee( "Susan", "Baker" );
Employee *e2Ptr = new Employee( "Robert", "Jones" );
cout << "Number of employees after instantiation is "
<< e1Ptr->getCount();
cout << "\n\nEmployee 1: “ << e1Ptr->getFirstName()
<< " " << e1Ptr->getLastName()
<< "\nEmployee 2: “ << e2Ptr->getFirstName()
<< " " << e2Ptr->getLastName() << "\n\n";
delete e1Ptr; // recapture memory
e1Ptr = 0; // disconnect pointer from free-store space
delete e2Ptr; // recapture memory
e2Ptr = 0; // disconnect pointer from free-store space
cout << "Number of employees after deletion is "
<< Employee::getCount() << endl;
406
} // end main
• Number of employees before instantiation is 0
• Employee constructor for Susan Baker called.
• Employee constructor for Robert Jones called.
• Number of employees after instantiation is 2

• Employee 1: Susan Baker
• Employee 2: Robert Jones

• ~Employee() called for Susan Baker
• ~Employee() called for Robert Jones
• Number of employees after deletion is 0

407
Thành viên tham chiếu
• Tham chiếu dữ liệu thành viên:
class Image {
int width; Khai báo bình thường
int height; như dữ liệu thành viên
int &widthRef;
//...
};

class Image {
int width;
int height;
Khởi tạo
int &widthRef = width;
SAI
//...
};

Image::Image (const int w, const int h) Khởi tạo ĐÚNG


: widthRef(width) thông qua danh sách
{ //……………... } khởi tạo thành viên
408
Thành viên tham chiếu
• Tham chiếu dữ liệu thành viên:
class Image {
int width; Khai báo bình thường
int height; như dữ liệu thành viên
int &widthRef;
//...
};

class Image {
int width;
int height;
Khởi tạo
int &widthRef = width;
SAI
//...
};

Image::Image (const int w, const int h) Khởi tạo ĐÚNG


: widthRef(width) thông qua danh sách
{ //……………... } khởi tạo thành viên
409
Thành viên là đối tượng của 1 lớp
• Dữ liệu thành viên có thể có kiểu:
– Dữ liệu (lớp) chuẩn của ngôn ngữ.
– Lớp do người dùng định nghĩa (có thể là chính lớp đó).
class Point { ……. };
class Rectangle {
public:
Rectangle (int left, int top, int right, int bottom);
//... Khởi tạo cho các
private: dữ liệu thành viên
Point topLeft; qua danh sách khởi
Point botRight; tạo thành viên
};
Rectangle::Rectangle (int left, int top, int right, int bottom)
: topLeft(left,top), botRight(right,bottom)
{}

410
Thành viên là đối tượng của 1 lớp
• Dữ liệu thành viên có thể có kiểu:
– Dữ liệu (lớp) chuẩn của ngôn ngữ.
– Lớp do người dùng định nghĩa (có thể là chính lớp đó).
class Point { ……. };
class Rectangle {
public:
Rectangle (int left, int top, int right, int bottom);
//... Khởi tạo cho các
private: dữ liệu thành viên
Point topLeft; qua danh sách khởi
Point botRight; tạo thành viên
};
Rectangle::Rectangle (int left, int top, int right, int bottom)
: topLeft(left,top), botRight(right,bottom)
{}

411
Bài tập
1. tạo lớp điểm, nhập 3 điểm bất kỳ trong mặt phẳng,
tính chu vi, diện tích tam giác tạo ra từ 3 điểm đó.
2. Nhập dãy các điểm. Tìm:
– 3 điểm tạo tam giác có diện tích lớn nhất / nhỏ nhất
– 2 điểm xa nhau nhất / gần nhau nhất
3. Nhập mảng hình chữ nhật có diện tích / chu vi lớn
nhình chữ nhật, tìm hất.
4. tạo lớp danh sách : mã số, họ tên, thu nhập. Nhập
danh sách cán bộ, sắp xếp danh sách theo thu
nhập, xuất danh sách và tổng thu nhập
5. tạo lớp : họ tên, điểm toán, lý, hoá. Nhập thông tin
N thí sinh, sắp xếp theo tổng số điểm, xuất các thí
sinh trúng tuyển
6. Tìm số lớn nhất / nhỏ nhất trong mảng 1 chiều các
số nguyên
412
7. tạo lớp ngày gồm ngày, tháng và năm và các hàm:
– Hàm tạo với 3 tham số mặc định
– Hàm xuất thông tin ở dạng dd-mm-yy
– Hàm tăng / giảm từng ngày một
– Hàm tính khoảng cách giữa 2 ngày trong 1 năm
8. tạo lớp phân số với các phương thức nhập, xuất, rút gọn,
cộng trừ nhân chia, đảo dấu
9. tạo lớp đa thức với các phương thức nhập, xuất, cộng trừ
nhân, đảo dấu
10. Viết chương trình cộng trừ nhân các số nguyên rất lớn
11. tạo lớp số phức với các phương thức nhập, xuất, cộng trừ
nhân chia, đảo dấu, liên hiệp phức, luỹ thừa, khai căn,
xuất tọa độ cực
12. tạo lớp vector và ma trận.
13. Tìm phần tử lớn nhất / nhỏ nhất của từng hàng / cột của
ma trận
14. Tìm phần tử lớn nhất trong số n phần tử nhỏ nhất của n
cột của ma trận m * n
413
15. tạo lớp lý lịch
Chương 8. Quá tải
(Overloading)
Quá tải hàm
Quá tải toán tử

414
Qúa tải hàm (Function
Overloading)
• Định nghĩa các hàm cùng tên
• Đối số phải khác nhau:
– Số lượng – Kiểu
– Thứ tự
class Time { void main() {
//... int h, m, s;
long GetTime (void); // số giây tính từ nửa đêm long t = GetTime(); // Gọi hàm ???
void GetTime (int &hours, GetTime(h, m, s); // Gọi hàm ???
int &minutes, }
int &seconds);
};

• Có thể dùng đối số mặc định.


415
Qúa tải hàm
• Định nghĩa các hàm cùng tên
• Đối số phải khác nhau:
– Số lượng – Kiểu
– Thứ tự
class Time { void main() {
//... int h, m, s;
long GetTime (void); // số giây tính từ nửa đêm long t = GetTime(); // Gọi hàm ???
void GetTime (int &hours, GetTime(h, m, s); // Gọi hàm ???
int &minutes, }
int &seconds);
};

• Có thể dùng đối số mặc định.


416
Toán tử 1 ngôi
• Toán tử một ngôi (unary operator) hay còn gọi là toán tử
đơn, có thể được dùng làm toán tử trước (prefix operator)
và toán tử sau (postfix operator). Ví dụ phép tăng (++) hay
phép giảm (–)
• Ví dụ:
• prefix operator: ++A;
• postfix operator: A++;

417
Toán tử 2 ngôi

• Toán tử 2 ngôi hay còn gọi là toán tử


đôi (binary operator).
• Ví dụ: như A+B, A*B, hay toán tử
chỉ mục “[…]” cũng là toán tử đôi.

418
Quá tải toán tử
• Định nghĩa lại các phép toán trên đối tượng.
• Các phép toán có thể tái định nghĩa (quá tải):

+ - * ! ~ & ++ -- () -> ->*


Một ngôi
new delete

+ - * / % & | ^ << >>

Hai ngôi = += -= /= %= &= |= ^= <<= >>=

== != < > <= >= && || [] () ,

• Các phép toán không thể tái định nghĩa (quá tải):
. .* :: ?: sizeof
419
Quá tải toán tử
• Bằng hàm thành viên:
class Point {
public:
Point (int x, int y) { Point::x = x; Point::y = y; }
Point operator + (Point &p) { return Point(x + p.x,y + p.y); }
Point operator - (Point &p) { return Point(x - p.x, y - p.y); }
private:
int x, y;
};
Có 1 tham số
(Nếu là toán tử hai ngôi)
void main() {
Point p1(10,20), p2(30,40);
Point p3 = p1 + p2; Point p4 = p1 - p2;
Point p5 = p3.operator + (p4); Point p6 = p3.operator – (p4);
};

420
Quá tải toán tử
• Bằng hàm độc lập: thường khai báo friend
class Point {
public:
Point (int x, int y) { Point::x = x; Point::y = y; }
friend Point operator + (Point &p, Point &q)
{return Point(p.x + q.x,p.y + q.y); }
friend Point operator - (Point &p, Point &q)
{return Point(p.x - q.x,p.y - q.y); }
private:
int x, y; Có 2 tham số
}; (Nếu là toán tử hai ngôi)

void main() {
Point p1(10,20), p2(10,20);
Point p3 = p1 + p2; Point p4 = p1 - p2;
Point p5 =operator + (p3, p4); Point p6 = operator – (p3, p4);
};
421
Chuyển kiểu
• Muốn thực hiện các phép cộng:
void main() {
Point p1(10,20), p2(30,40), p3, p4, p5;
p3 = p1 + p2;
p4 = p1 + 5; p5 = 5 + p1;
};

Có thể định nghĩa thêm 2 toán tử:


class Point {
//...
friend Point operator + (Point, Point);
friend Point operator + (int, Point);
friend Point operator + (Point, int);
};

422
Chuyển kiểu (tt)
• Chuyển đổi kiểu: ngôn ngữ định nghĩa sẵn.
void main() {
Point p1(10,20), p2(30,40), p3, p4, p5;
p3 = p1 + p2;
p4 = p1 + 5; // tương đương p1 + Point(5)
p5 = 5 + p1; // tương đương Point(5) + p1
}

Định nghĩa phép chuyển đổi kiểu


class Point {
//... Chuyển kiểu
Point (int x) { Point::x = Point::y = x; } 5  Point(5)
friend Point operator + (Point, Point);
};

423
Quá tải toán tử xuất <<
• Định nghĩa hàm toàn cục:
ostream& operator << (ostream&, Class&);
class Point { void main() {
public: Point p1(10,20), p2;
Point (int x=0, int y=0) cout<<“Diem P1: “<< p1 << endl;
{ Point::x = x; Point::y = y; } cout<<“Diem P2: “<< p2 << endl;
friend ostream& operator << }
(ostream& os, Point& p)
{ os<< “(“ << p.x << “,” << p.y << “)”; }
// …..
private: Kết quả
int x, y; trên
}; màn hình ?

424
Quá tải toán tử nhập >>
• Định nghĩa hàm toàn cục:
istream& operator >> (istream&, Class&);
class Point { void main() {
public: Point p1, p2;
Point (int x=0, int y=0) cout<<“Nhap thong tin cho P1: \n“;
{ Point::x = x; Point::y = y; } cin>>p1;
friend istream& operator >> cout<<“Nhap thong tin cho P2: \n“;
(istream& is, Point& p) cin>>p2;
{ cout<<“Nhap x: “; is>>p.x; }
cout<<“Nhap y: “; is>>p.y;
}
// …..
private:
int x, y;
};
425
Quá tải toán tử [ ]
• Thông thường để xuất ra giá trị của 1 phần tử tại
vị trí cho trước trong đối tượng.
• Định nghĩa là hàm thành viên.
class StringVec { char* StringVec::operator [] (int i) {
public: if ( i>=0 && i<used) return elems[i];
StringVec (const int dim); return “”;
~StringVec (); }
char* operator [] (int);
int add(char* ); void main() {
// ……….. StringVec sv(100);
private: sv.add(“PTPhi”);sv.add(“BQThai”);
char **elems; // cac phan tu sv.add(“LVLam”); sv.add(“NCHuy”);
int dim; // kich thuoc cua vecto cout<< sv[2]<<endl;
int used; // vi tri hien tai cout<<sv[0];
}; }

426
Quá tải toán tử ()
• Định nghĩa là hàm thành viên.
class Matrix { double& Matrix::operator ()
public: (const short row, const short col)
Matrix (const short rows, const short cols); {
~Matrix (void) {delete []elems;} static double dummy = 0.0;
double& operator () (const short row, return (row >= 1 && row <= rows
const short col); && col >= 1 && col <= cols)
friend ostream& operator << (ostream&, Matrix&); ? elems[(row - 1)*cols
+ (col - 1)]
friend Matrix& operator + (Matrix&, Matrix&);
: dummy;
friend Matrix& operator - (Matrix&, Matrix&); }
friend Matrix& operator * (Matrix&, Matrix&); void main() {
private: Matrix m(3,2);
const short rows; // số hàng m(1,1) = 10; m(1,2) = 20;
const short cols; // số cột m(2,1) = 30; m(2,2) = 40;
double *elems; // các phần tử m(3,1) = 50; m(3,2) = 60;
}; cout<<m<<endl;
} 427
Quá tải toán tử ++ & --
• Toán tử ++ (hoặc toán tử --) có 2 loại:
– Tiền tố: ++n
– Hậu tố: n++
class PhanSo { void main() {
int tuso, mauso; PhanSo p1(3,4), p2;
public:
cout<< p1++;
// …..
PhanSo(int x=0 , int y=1) cout<<++p2;
{tuso=x; mauso=y;} cout<<++(p1++) + (++p2)++;
friend PhanSo operator ++ (PhanSo&); }
friend PhanSo operator ++ (PhanSo&, int);
};
PhanSo operator ++ (PhanSo& p) {
return (p = PhanSo(p.tuso+p.mauso, p.mauso));
}
PhanSo operator ++ (PhanSo& p, int notused) {
PhanSo p2 = PhanSo(p.tuso+p.mauso, p.mauso);
return p2; 428
}
Quá tải new & delete
• Hàm new và delete mặc định của ngôn ngữ:
– Nếu đối tượng kích thước nhỏ, có thể sẽ gây ra quá
nhiều khối nhỏ => chậm.
– Không đáng kể khi đối tượng có kích thước lớn.
=> Toán tử new và delete ít được quá tải.
• Định nghĩa theo dạng hàm thành viên:
class Point { void main() {
public: Point *p = new Point(10,20);
//... Point *ds = new Point[30];
void* operator new (size_t bytes); //………………
void operator delete (void *ptr, size_t bytes); delete p;
private: delete []ds;
int xVal, yVal; }
}; 429
Các toán tử được quá tải
• Tương tự như tái định nghĩa hàm thành viên:
– Che giấu đi toán tử của lớp cơ sở.
– Hàm tạo bản sao:
Y::Y (const Y&)
– Phép gán:
Y& Y::operator = (const Y&)
• Nếu không định nghĩa, sẽ tự động có hàm tạo
bản sao và phép gán do ngôn ngữ tạo ra.
=> SAI khi có con trỏ thành viên.
• Cẩn thận với toán tử new và delete.
430
Chương 9
Thừa kế
(Inheritance)

431
Khái niệm
• Kế thừa từ các lớp có từ trước.
• Ích lợi: có thể tận dụng lại
– Các thuộc tính chung
– Các hàm có thao tác tương tự
Lớp cơ sở LỚP CHA
STUDENT
(Base class) (Super class)

Lớp dẫn xuất LỚP CON


ENT_STUDENT
(Derived class) (Sub class)
432
Thuật ngữ
• inheritance: tính thừa kế
• derive: dẫn xuất / thừa kế
• base/parent class: lớp cơ sở / lớp cha
• derived/child class: lớp dẫn xuất / lớp con
• multiple inheritance: đa thừa kế

433
Khái niệm
• Kế thừa từ các lớp có từ trước.
• Ích lợi: có thể tận dụng lại
– Các thuộc tính chung
– Các hàm có thao tác tương tự
Lớp cơ sở LỚP CHA
STUDENT
(Base class) (Super class)

Lớp dẫn xuất LỚP CON


ENT_STUDENT
(Derived class) (Sub class)
434
Ưu điểm của việc kế thừa
– Tiết kiệm thời gian và công sức
– Tái sử dụng lại những lớp có sẵn
– Giảm lượng code phải thiết kế, viết, kiểm tra
– Tránh trùng lắp code
– Rút ngắn thời gian giúp LTV tập trung vào
mục tiêu
– Giúp phân loại và thiết kế lớp dễ dàng, dễ
quản lý

435
Cú pháp kế thừa
class TenLopCoSo
{
//Cac thanh vien cua lop co so
};
class TenLopDanXuat : public TenLopCoSo
{
//Cac thanh vien cua lop dan xuat
};
436
Cú pháp kế thừa
class TenLopCoSo
{
//Cac thanh vien cua lop co so
};
class TenLopDanXuat :
public/private/protected TenLopCoSo
{
//Cac thanh vien cua lop dan xuat
}; 437
Ví dụ minh họa
Ký hiệu n
composition ContactDir Contact

#include <iostream> class ContactDir {


#include <string.h> private:
class Contact { int Lookup(const char *name);
private:
Contact **contacts; // ds cac doi tac
char *name; // ten doi tac
int dirSize; // kich thuoc thu muc hien tai
char *address; // dia chi doi tac
char *tel; // so dien thoai int maxSize; // kich thuoc thu muc toi da
public: public:
Contact (const char *name, ContactDir (const int maxSize);
const char *address, const char *tel); ~ContactDir();
~Contact (); void Insert(const Contact&);
const char* Name () const { return name;} void Delete(const char *name);
const char* Address() const { return address;} Contact* Find(const char *name);
const char* Tel() const { return tel;} friend ostream& operator <<
friend ostream& operator << (ostream&, ContactDir&);
( ostream&, Contact& ); // …………
}; };
438
Ví dụ minh họa (tt)
n
ContactDir Contact
Ký hiệu
Thừa kế
SmartDir

class SmartDir : public ContactDir { Contact* SmartDir::Recent (void) {


private: return recent == 0 ? 0 :
char *recent; // ten duoc tim gan nhat ContactDir::Find(recent);
public: }
SmartDir(const int max) : ContactDir(max) Contact* SmartDir::Find (const char *name) {
{ recent = 0; } Contact *c = ContactDir::Find(name);
Contact* Recent (void); if (c != 0)
Contact* Find (const char *name); recent = (char*) c->Name();
// ……………. return c;
}; }
439
Ví dụ minh họa (tt)
n
ContactDir Contact
Ký hiệu
Thừa kế
SmartDir

class SmartDir : public ContactDir { Contact* SmartDir::Recent (void) {


private: return recent == 0 ? 0 :
char *recent; // ten duoc tim gan nhat ContactDir::Find(recent);
public: }
SmartDir(const int max) : ContactDir(max) Contact* SmartDir::Find (const char *name) {
{ recent = 0; } Contact *c = ContactDir::Find(name);
Contact* Recent (void); if (c != 0)
Contact* Find (const char *name); recent = (char*) c->Name();
// ……………. return c;
}; }
440
Ví dụ minh họa (tt)
n
ContactDir Contact
Ký hiệu
Thừa kế
SmartDir

class SmartDir : public ContactDir { Contact* SmartDir::Recent (void) {


private: return recent == 0 ? 0 :
char *recent; // ten duoc tim gan nhat ContactDir::Find(recent);
public: }
SmartDir(const int max) : ContactDir(max) Contact* SmartDir::Find (const char *name) {
{ recent = 0; } Contact *c = ContactDir::Find(name);
Contact* Recent (void); if (c != 0)
Contact* Find (const char *name); recent = (char*) c->Name();
// ……………. return c;
}; }
441
Ví dụ về kế thừa
#include <iostream> int main() {
//#include <bits/stdc++.h> lopCon obj1;
using namespace std; obj1.id_con = 10;
obj1.id_cha = 30;
//Lop Cha cout << "lopCon id is " << obj1.id_con
<< endl;
class lopCha {
cout << "lopCha id is " << obj1.id_cha
public:
<< endl;
int id_cha;
};
return 0;
}
// Lop con ke thua tu lop cha
class lopCon : public lopCha {
public:
int id_con;
}; 442
Phân loại
• Phân loại
– Đơn kế thừa (single inheritance): chỉ có một
lớp cha
– Đa kế thừa (multiple inheritance): có nhiều
lớp cha

443
Từ khóa chỉ định quyền truy cập: protected

• Phân loại mới cho thành viên của lớp


• Cho phép lớp dẫn xuất truy cập trực tiếp thành viên
của lớp cơ sở “bằng tên”
– Nhưng với những chỗ khác, chúng giống như private
• Trong lớp cơ sở  coi chúng là private
• Chúng được coi là "protected" trong lớp dẫn xuất
– Để cho phép các dẫn xuất cháu chắt
• Nhiều người cho rằng cơ chế này vi phạm nguyên lý
che giấu thông

444
Các phạm vi kế thừa
Có 3 loại chính đó là:
• public: Nếu kế thừa ở dạng public, sau khi kế thừa,
tất cả các thành viên dạng public lớp cha
sẽ public ở lớp con, dạng protected ở lớp cha vẫn
sẽ là protected ở lớp con.
• protected: Nếu dùng protected thì sau khi kế thừa,
tất cả các thành viên dạng public lớp cha sẽ trở
thành protected tại lớp con.
• private: Trường hợp ta sử dụng private, thì sau khi
kế thừa, tất cả các thành viên
dạng public và protected ở lớp cha sẽ
thành private tại lớp con. 445
Đơn kế thừa
• Đơn kế thừa (Single
Inheritance): nghĩa là một
lớp chỉ được kế thừa từ
đúng một lớp khác. Hay
nói cách khác, lớp con chỉ
có duy nhất một lớp cha.

446
Ví dụ
#include <iostream> // Lop con ke thua tu lop cha
using namespace std; class Lopxedap : public Lopxe
{
// Lop cha };
class Lopxe
{ // main function
public: int main()
Lopxe() {
{ Lopxedap xe1;
cout << "This is a bycicle" << endl; return 0;
} }
};

447
Ví dụ (tt) - mô tả trong bộ nhớ
**contacts **contacts
dirSize dirSize
maxSize maxSize
ContactDir *recent
SmartDir
*contacts 1 *contacts 1
Contact 1 *contacts 2 Contact 1 *contacts 2
*contacts 3 *contacts 3
… …
Contact 2 Contact 2
*contacts i *contacts i
… …
… …
*contacts n *contacts n

Contact i Contact i

448
Cây kế thừa
• Các quan hệ kế thừa luôn được biểu
diễn với các lớp dẫn xuất đặt dưới lớp
cơ sở để nhấn mạnh bản chất phả hệ
của quan hệ

449
Cây kế thừa
Kế thừa phần lớn các thành viên dữ liệu và phương thức
của lớp cơ sở (ngoại trừ constructor, destructor)

Lớp dẫn xuất

Có thể bổ sung thêm các thành viên dữ liệu mới và các


phương thức mới
Lớp cơ sở trực tiếp Class A

Lớp cơ sở Class B

Lớp cơ sở gián tiếp 450


Class C
Hàm tạo và hàm hủy
• Trong thừa kế, khi khởi tạo đối tượng:
– Hàm tạo của lớp cha sẽ được gọi trước
– Sau đó mới là hàm tạo của lớp con.
• Trong thừa kế, khi hủy bỏ đối tượng:
– Hàm hủy của lớp con sẽ được gọi trước
– Sau đó mới là hàm hủy của lớp cha.
A

C 451
Hàm tạo và hàm hủy
• Trong thừa kế, khi khởi tạo đối tượng:
– Hàm tạo của lớp cha sẽ được gọi trước
– Sau đó mới là hàm tạo của lớp con.
• Trong thừa kế, khi hủy bỏ đối tượng:
– Hàm hủy của lớp con sẽ được gọi trước
– Sau đó mới là hàm hủy của lớp cha.
A

C 452
Hàm tạo và hàm hủy (tt)
class SmartDir : public ContactDir {
private:
char *recent; // ten duoc tim gan nhat
Gọi hàm
public: tạo của
SmartDir(const int max) : ContactDir(max) lớp cha

{ recent = 0; }
SmartDir(const SmartDir& sd): ContactDir(sd)
{ recent = 0; }
~SmartDir() { Thu hồi vùng nhớ
của con trỏ thành viên
delete recent; của lớp con nếu đã
} cấp vùng nhớ trong
hàm tạo.
// …………….
};
453
Thành viên lớp được bảo vệ
• Thừa kế:
– Có tất cả các dữ liệu và hàm thành viên.
– Không được truy xuất đến thành viên private.
• Thuộc tính truy cập protected:
– Cho phép lớp con truy xuất.
class Foo {
class ContactDir { public:
//... // cac thanh vien chung...
private:
protected: // cac thanh vien rieng...
int Lookup (const char *name); protected:
// cac thanh vien duoc bao ve...
Contact **contacts; // ds cac doi tac public:
int dirSize; // kich thuoc hien tai // cac thanh vien chung nua...
protected:
int maxSize; // kich thuoc toi da
// cac thanh vien duoc bao ve nua...
}; }; 454
Thành viên lớp được bảo vệ
• Thừa kế:
– Có tất cả các dữ liệu và hàm thành viên.
– Không được truy xuất đến thành viên private.
• Thuộc tính truy cập protected:
– Cho phép lớp con truy xuất.
class Foo {
class ContactDir { public:
//... // cac thanh vien chung...
private:
protected: // cac thanh vien rieng...
int Lookup (const char *name); protected:
// cac thanh vien duoc bao ve...
Contact **contacts; // ds cac doi tac public:
int dirSize; // kich thuoc hien tai // cac thanh vien chung nua...
protected:
int maxSize; // kich thuoc toi da
// cac thanh vien duoc bao ve nua...
}; }; 455
Lớp private, publish và
protected
Thuộc tính truy xuất Kiểu kế thừa
thành viên của lớp
public protected private
cơ sở
public trong lớp protected trong private trong
public
dẫn xuất lớp dẫn xuất lớp dẫn xuất
protected trong protected trong private trong
protected
lớp dẫn xuất lớp dẫn xuất lớp dẫn xuất
Dấu trong lớp Dấu trong lớp dẫn Dấu trong lớp
private dẫn xuất xuất dẫn xuất

protecte
public private
d 456
Lớp private, publish và protected

class A { class B : A { // Thừa kế dạng private


private: …….
int x; };
class C : private A { // A là lớp cơ sở riêng của B
void Fx (void);
………
public: };
int y; class D : public A { // A là lớp cơ sở chung của C
void Fy (void); ………
protected: };
int z; class E : protected A { // A: lớp cơ sở được bảo vệ
void Fz (void); ……….
}; };
457
Lớp private, publish và protected

class A { class B : A { // Thừa kế dạng private


private: …….
int x; };
class C : private A { // A là lớp cơ sở riêng của B
void Fx (void);
………
public: };
int y; class D : public A { // A là lớp cơ sở chung của C
void Fy (void); ………
protected: };
int z; class E : protected A { // A: lớp cơ sở được bảo vệ
void Fz (void); ……….
}; };
458
Đa kế thừa
• Đa kế thừa (Multiple
Inheritance): là một tính
năng của ngôn ngữ C++.
Trong đó một lớp có thể kế
thừa từ nhiều hơn một lớp
khác. Nghĩa là một lớp con
được kế thừa từ nhiều hơn
một lớp cơ sở.

459
Đa thừa kế
OptionList Window class OptionList { class Window {
public: public:
OptionList (int n); Window (Rect &);
~OptionList (); ~Window (void);
Menu //... //...
}; };

class Menu
OptionList object Window object Menu object : public OptionList, public Window {
OptionList public:
Window OptionList
data members
data members data members Menu (int n, Rect &bounds);
~Menu (void);
Window
//...
data members };
Menu Menu::Menu (int n, Rect &bounds) :
data members
OptionList(n), Window(bounds)
{ /* ... */ }
460
Sự mơ hồ trong đa thừa kế
class OptionList { class Window {
public: public:
// …… // ……
void Highlight (int part); void Highlight (int part);
}; };

class Menu : public OptionList,


Hàm cùng tên Chỉ rõ hàm
public Window của lớp nào
{ ……. };

void main() {
void main() {
xử lý Menu m1(….);
Menu m1(….);
m1.OptionList::Highlight(10);
m1.Highlight(10);
m1.Window::Highlight(20);
….
….
}
}
461
Hàm ảo
• Liên kết tĩnh (static binding):
– Xác định khi biên dịch chương trình.
– Dùng hàm thành viên.
– Gọi hàm của lớp cơ sở (lớp cha).
class ContactDir { class SortedDir : public ContactDir {
//... public:
public:
int Lookup (const char *name); SortedDir(const int max) : ContactDir(max) {}
//... int Lookup(const char *name);
}; };

void
voidmain()
main(){ { cout<<p->Lookup(“ABC”);
ContactDir Gọi
ContactDir c1(10);
*p; ….
SortedDir hàm
SortedDir *p; p = &c1;
c1(10); p = &c1; } 462
nào ?
Hàm ảo (tt)
• Liên kết động (dynamic binding)
– Xác định khi thực thi chương trình.
– Dùng hàm ảo (virtual function).
– Gọi hàm của lớp dẫn xuất (lớp con).
– Thể hiện tính đa hình của OOP.
class ContactDir { void main() {
//... ContactDir c1(10);
public:
SortedDir *p1; p1 = &c1;
virtual int Lookup (const char *name);
}; p1->Lookup(“ABC”); Kết quả
trên
class SortedDir : public ContactDir { SortedDir c2(20); màn hình
//…. Bỏ từ khóa là gì?
ContactDir *p2; p2 = &c2;
public: virtual thì kết
int Lookup(const char *name);
quả là gi? p2->Lookup(“ABC”);
}; } 463
Hàm ảo (tt)
• Khi khai báo hàm ảo trong lớp cơ sở thì hàm đó sẽ được
định nghĩa lại trong lớp dẫn xuất.
• Sử dụng hàm ảo?
– Lớp Parent và Child cùng có phương thức f
– Khai báo một con trỏ thuộc kiểu của lớp Parent
• Parent* p;
– Con trỏ này trỏ đến đối tượng của lớp Child
• p = new Child;
– Sau đó, thực hiện lời gọi
• p->f;
– Kết quả: f của lớp Parent sẽ được gọi
– Nếu f được khai báo là hàm ảo trong lớp Parent thì f của lớp
Child sẽ được gọi.

464
#include <iostream>
#include <iostream>
using namespace std;
using namespace std;
class Hinh
class Hinh
{
{
public:
public:
virtual void ve()
void ve()
{
{
cout << "ve hinh" << endl;
cout << "ve hinh" << endl;
}
}
};
};
class Hinhtron : public Hinh
class Hinhtron : public Hinh
{
{
public:
public:
void ve()
void ve()
{
{
cout << "ve hinh tron" << endl;
cout << "ve hinh tron" << endl;
}
}
};
};
int main ()
int main ()
{
{
Hinh *h = new Hinhtron;
Hinh *h = new Hinhtron;
h->ve();
h->ve();
}
}

465
#include <iostream>
class base{
public:
int i;
base(int x) {i=x;}
virtual void func() {
cout <<"Ham func() cua lop co so, i= "<<i<<endl;
}
};
class derived1:public base{
public:
derived1(int x):base(x) {}
virtual void func() {
cout <<"Ham func() cua lop dan suat 1, i= "<<2*i<<endl;
}
};
class derived2:public base{
public:
derived2(int x):base(x) {}
};
main() { Kết quả là
base *b; base ob(10); gi?
derived1 d_ob1(10); derived2 d_ob2(10);
b=&ob; b->func();
b=&d_ob1; b->func();
b=&d_ob2; b->func();
getchar(); 466
}
Hàm thuần ảo
• Lớp cơ sở trừu tượng
– chỉ cung cấp bộ khung gồm có danh sách các biến và
các hàm cho các lớp khác kế thừa.
Thông báo
– không có đối tượng nào, không tồn tại độc lậpcho trình
– các lớp dẫn xuất định nghĩa phần còn lại. biên dịch
biết thân
• Hàm thuần ảo được khai báo trong lớp sẽ hàm làm
không
liên quan
cho lớp đó trở thành lớp cơ sở trừu tượng. đến lớp cơ
virtual kiểu_trả_về tên_hàm(danh sách tham số) = 0; sở

• Tất cả các lớp dẫn xuất đều phải định nghĩa


hàm thuần ảo.
467
Hàm thuần ảo
• Lớp cơ sở trừu tượng
– chỉ cung cấp bộ khung gồm có danh sách các biến và
các hàm cho các lớp khác kế thừa.
– không có đối tượng nào, không tồn tại độc lập
– các lớp dẫn xuất định nghĩa phần còn lại.
• Hàm thuần ảo được khai báo trong lớp sẽ làm
cho lớp đó trở thành lớp cơ sở trừu tượng.
virtual kiểu_trả_về tên_hàm(danh sách tham số) = 0;
• Tất cả các lớp dẫn xuất đều phải định nghĩa hàm
thuần ảo.
468
Hàm thuần ảo
#include <iostream>
using namespace std;
class Square : public Shape
// lớp trừu tượng {
class Shape public:
{ float calculateArea()
{ return l*l; }
protected:
};
float l;
public: class Circle : public Shape
void getData() {
{ public:
float calculateArea()
cin >> l;
{ return 3.14*l*l; }
} };

// hàm thuần ảo
virtual float calculateArea() = 0;
469
};
int main()
{
Square s;
Circle c;

cout << "Enter length to calculate the area of a square: ";


s.getData();
cout<<"Area of square: " << s.calculateArea();
cout<<"\nEnter radius to calculate the area of a circle: ";
c.getData();
cout << "Area of circle: " << c.calculateArea();

return 0;
}
470
Ví dụ: hàm thuần ảo

class Mammal
{
public:
virtual void Move() = 0;
};
class Dog : public Mammal
{
public:
void Move() {cout << "Dog moves 1 step";}
};
void main()
{
Dog p;
p.Move(); // “Dog moves 1 step”
Mammal m; // ”Lỗi”
m.Move();
}
471
Lớp cơ sở ảo
• Sự mơ hồ - dư thừa dữ liệu

class OptionList
: public Widget, List
{ /*...*/ };
class Window
: public Widget, Port
{ /*...*/ };
class Menu
: public OptionList,
public Window
{ /*...*/ };

Đối tượng Menu


472
Lớp cơ sở ảo (tt)
• Cách xử lý: dùng lớp cơ sở ảo.
class OptionList
: virtual public Widget,
public List
{ /*...*/ };
class Window
: virtual public Widget,
Chỉ có 1
public Port đối tượng Widget
{ /*...*/ };
class Menu
: public OptionList,
Menu::Menu (int n, Rect &bounds) :
public Window Widget(bounds), OptionList(n), Window(bounds)
{ /*...*/ }; { //... }
473
#include <iostream> #include <iostream>
class base{ class base{
public: public:
int i; int i;
}; };
class derived1:public base{ class derived1: virtual public base{
public: public:
int j; int j;
}; };
class derived2:public base{ class derived2: virtual public base{
public: public:
int k; int k;
}; };
class derived3:public derived1, public class derived3:public derived1, public
derived2{ derived2{
public: public:
int tich() { return i*j*k; } int tich() { return i*j*k; }
}; };
main() { main() {
derived3 ob; derived3 ob;
Mơ hồ, ? i của
ob.i=10; ob.i=10;
ob.j=20; lớp nào? ob.j=20;
ob.k=30; ob.k=30;
cout <<"tich so la : "<<ob.tich ()<<endl; cout <<"tich so la : "<<ob.tich ()<<endl;
getchar(); getchar();
} } 474
Khuôn hình

475
Định nghĩa khuôn hình
• Template cho phép định nghĩa một hàm có đặc điểm
chung.
• C++ sử dụng template để tạo ra những thể hiện cụ thể
của hàm khi cần thiết.
• Ví dụ: định nghĩa hàm max
template <class kind>
kind max(kind d1, kind d2)
{
if (d1 > d2)
return (d1);
return (d2);
}
– Cấu trúc <class kind> báo cho C++ biết rằng kind có thể được
thay thế bởi bất kỳ kiểu nào.
– Khai báo hàm phải nằm ngay sau khai báo template
476
Định nghĩa khuôn hình…
• Ví dụ: Sử dụng template
main()
{
float f = max(3.5, 8.7);
int i = max(100, 800);
char ch = max('A', 'Q');
int i2 = max(600, 200);
}
– Khi C++ phát hiện câu lệnh: float f = max(3.5, 8.7); nó
sẽ kiểm tra để xác định xem hàm max(float, float) đã
có mã lệnh hay chưa và nó sẽ tự động tạo ra mã lệnh
nếu chưa có.
– Chú ý rằng khi gặp câu lệnh int i2 = max(600, 200);
thì nó sẽ không tạo ra mã lệnh cho hàm max(int,int)
nữa vì nó đã tạo ra từ trước.
477
Chuyên biệt hoá hàm
• Khi sử dụng hàm max để so sánh hai string như
sau:
main()
{
char *namel = "Able";
char *name2 = "Baker";
cout << max(namel, name2) << '\n';
}
– Vì string được biểu diễn bởi một con trỏ (char *), nên
câu lệnh if (dl > d2) sẽ so sánh giá trị của con trỏ chứ
không phải nội dung của con trỏ.
– Nếu muốn C++ vẫn sử dụng cách so sánh với những
dữ liệu thông thường, riêng đối với string thì phải sử
dụng strcmp. 478
Chuyên biệt hoá hàm …
• Thực hiện quá trình chuyên biệt hoá (specialization).
• Định nghĩa lại hàm max chỉ dùng cho kiểu string.
char *max(char *dl, char *d2)
{
if (strcmp(dl, d2) < 0)
return (dl);
return (d2);
}
– Khi C++ phát hiện ra câu lệnh cout << max(namel, name2) << '\n'; thì
nó sẽ tìm những hàm thông thường có dạng max(char *, char *) ,
sau đó nó mới tìm đến template max(kind d1, kind d2).

479
Khuôn hình lớp
#include <stdlib.h>
#include <iostream>
const int STACK_SIZE = 100; // số lượng phần tử lớn nhất trong Stack
template<class kind>
class stack {
private:
int count; // Số lượng phần tử của Stack
kind data[STACK_SIZE]; // Mảng chứa các phần tử của Stack
public:
stack (void) {count = 0; }// Stack ban đầu rỗng
void push(const kind item) {
data[count] = item;
++count;
}
kind pop(void) {
--count;
return (data[count]);
}
};
480
Khuôn hình lớp …
• Nếu ta khai báo:
stack a_stack; // câu lệnh không hợp lệ
– Stack là một template chung.
– Khi C++ nhìn thấy lệnh khai báo này, nó sẽ hỏi “stack nào?”.
– Phải xác định một kiểu dữ liệu cụ thể được lưu trong stack.
– Phải khai báo:
stack<int> a_stack; // Các phần tử trong stack là kiểu số nguyên
• Sau đó, ta có thể sử dụng stack như sau:
a_stack.push(I);
x = a_stack.pop();

481
Khuôn hình lớp …
• Định nghĩa các hàm thành viên bên ngoài
định nghĩa lớp như sau:
template<class kind>
inline void stack<kind>::push(const kind item)
{
data[count] = item;
++count;
}

482
Chuyên biệt hoá lớp
template <class kind>stack { ...}
– Giới thiệu cho C++ biết cách tạo ra một tập các lớp có tên là stack<int>,
stack<double>, stack<float>,…
– C++ sẽ tự động tạo ra các hàm thành viên như: stack<int>: :push,
stack<double>: :push, và stack<float>::push.
• Nếu khai báo một hàm thành viên một cách tường minh thì C++ sẽ
sử dụng định nghĩa này trước.
inline void stack<char *>::push(const char * item)
{
data[count] = strdup(item);
++count;
}
– Từ khoá template nói cho C++ biết ”Đây là class chung, hãy tạo ra một
phiên bản tương ứng với nó”.
– Nếu không có từ khoá template, C++ sẽ hiểu đây là hàm chính thức và
phải sử dụng nó trước.

483

You might also like