Professional Documents
Culture Documents
Ham Contro
Ham Contro
MỞ RỘNG CỦA
C++ SO VỚI C
TS. LÊ THỊ MỸ HẠNH
Bộ môn Công nghệ Phần mềm
Khoa Công Nghệ Thông Tin n Ngôn ngữ C ra đời năm 1972.
Đại học Bách khoa – Đại học Đà Nẵng
n Phát triển thành C++ vào năm 1983.
Biến Biến
n Biến: vùng bộ nhớ được dành riêng để lưu n Trong C tất cả các câu lệnh khai báo biến,
giá trị. mảng cục bộ phải đặt tại đầu khối.
n 3 loại: ¨ vị trí khai báo và vị trí sử dụng của biến có thể
¨ Biến giá trị; ở cách khá xa nhau, điều này gây khó khăn
¨ Biến tham chiếu; trong việc kiểm soát chương trình.
¨ Biến con trỏ. n C++ đã khắc phục nhược điểm này bằng
n Khai báo & Khởi tạo: cách cho phép các lệnh khai báo biến có
int x; <Kiểu Dữ Liệu> <Tên biến>;
thể đặt bất kỳ chỗ nào trong chương trình
x = 5; trước khi các biến được sử dụng.
Hoặc:
→ int x = 5; ¨ Phạm vi hoạt động của các biến kiểu này là
<Kiểu Dữ Liệu> <Tên biến> = <Giá trị>; khối trong đó biến được khai báo.
Biến Chuyển kiểu (CASTING)
n Phân loại theo phạm vi: n C:
¨ Biến cục bộ ¨ (new_type) expression;
¨ Biến toàn cục n C++:
n Toán tử định phạm vi (::) ¨ vẫn sử dụng như trên.
¨ Phép chuyển kiểu mới:
new_type (expression);
→ Phép chuyển kiểu này có dạng như một
hàm số chuyển kiểu đang được gọi.
Hàm
n Khai báo nguyên mẫu hàm & Định nghĩa
CHƯƠNG 1:
hàm;
HÀM
TS. LÊ THỊ MỸ HẠNH
Bộ môn Công nghệ Phần mềm
Khoa Công Nghệ Thông Tin
Đại học Bách khoa – Đại học Đà Nẵng
Hàm Hàm
n Truyền tham số: tham trị & tham chiếu (biến tham n Đối số
chiếu hoặc biến con trỏ):
Hàm Hàm
n Đối số hằng: sử dụng khi không muốn n Đối số hằng tham chiếu:
thay đổi giá trị đối số truyền vào:
Hàm Hàm
n Đối số mặc định
¨ Định nghĩa các giá trị tham số mặc định cho các hàm; n Đối số mặc định:
¨ Các đối số mặc định cần là các đối số cuối cùng tính từ trái
qua phải;
¨ Nếu chương trình sử dụng khai báo nguyên mẫu hàm thì các
đối số mặc định cần được khởi gán trong nguyên mẫu hàm,
không được khởi gán lại cho các đối số mặc định trong dòng
đầu của định nghĩa hàm;
¨ Khi xây dựng hàm, nếu không khai báo nguyên mẫu hàm thì
các đối số mặc định được khởi gán trong dòng đầu của định
nghĩa hàm;
¨ Đối với các hàm có đối số mặc định thì lời gọi hàm cần viết
theo quy định: các tham số vắng mặt trong lời gọi hàm tương
ứng với các đối số mặc định cuối cùng (tính từ trái sang
phải).
Hàm Hàm
n Hàm trả là tham chiếu
n Hàm trả về là tham chiếu Kiểu &Tên_hàm(...) {
¨ Biểu thức được trả lại trong //thân hàm
câu lệnh return phải là biến return <biếntoàncục>;
toàn cục, khi đó mới có thể }
sử dụng được giá trị của hàm;
¨ Nếu trả về tham chiếu đến một biến cục bộ thì biến
cục bộ này sẽ bị mất đi khi kết thúc thực hiện hàm.
Do vậy tham chiếu của hàm sẽ không còn ý nghĩa
nữa. Vì vậy, nếu hàm trả về là tham chiếu đến biến
cục bộ thì biến cục bộ này NÊN khai báo static;
¨ Khi giá trị trả về của hàm là tham chiếu, ta có thể gặp
các câu lệnh gán hơi khác thường, trong đó vế trái là
một lời gọi hàm chứ không phải là tên của một biến.
Các toán tử được đa năng hóa sẽ được lựa chọn bởi trình Chúng ta không thể thay đổi ý nghĩa của các toán tử khi áp dụng cho các kiểu có sẵn.
Đa năng hóa các toán tử không thể có các tham số có giá trị mặc định.
biên dịch: Các toán tử có thể đa năng hoá:
khi gặp một toán tử làm việc trên các kiểu không phải là kiểu có sẵn, + - * / % ^ ! = < > += -=
trình biên dịch sẽ tìm một hàm định nghĩa của toán tử nào đó có các ^= &= |= << >> <<= <= >= && || ++ --
tham số đối sánh với các toán hạng để dùng. () [] new delete & | ~ *= /= %= >>= == != , -> ->*
Bài tập Bài tập
n Bài tập 1.1: n Bài tập 1.2:
Viết chương trình cho phép thực hiện các thao tác trên kiểu struct Viết chương trình cho phép thực hiện các thao tác trên kiểu struct
phân số: số phức:
n Hàm toán tử Nhập, xuất phân số (>>, <<) n Hàm toán tử Nhập, xuất số phức (>>, <<).
n Nghịch đảo, rút gọn phân số. n Hàm tính module số phức.
n Hàm toán tử +, -, *, /, <, >, ==, != giữa hai phân số. n Hàm toán tử +, -, *, /, <, >, ==, != giữa hai số phức.
37
n Tính giá trị, đạo hàm, nguyên hàm đơn thức. n Tính xác định thứ trong tuần.
n Hàm toán tử +, -, *, /, <, >, ==, != giữa hai đơn thức cùng n Hàm toán tử ++, -- để tang, giảm 01 ngày; toán tử <, >, ==, !=
bậc. giữa hai ngày.
Bài tập
n Bài tập 1.5:
Thông tin một học sinh bao gồm:
CHƯƠNG 2:
n Họ tên. CON TRỎ
n Điểm văn, toán.
Thực hiện các thao tác trên kiểu struct học sinh: &
n Hàm toán tử >>, << để nhập, xuất thông tin học sinh.
n Toán tử >, != để soa sánh 02 học sinh dựa vào điểm trung CẤP PHÁT ĐỘNG
bình
n Tính điểm trung bình của mỗi học sinh.
TS. LÊ THỊ MỸ HẠNH
Bộ môn Công nghệ Phần mềm
n Xếp loại theo tiêu chí:
Khoa Công Nghệ Thông Tin
¨ Giỏi (>= 8.0), Khá (>= 7.0). Đại học Bách khoa – Đại học Đà Nẵng
¨ Trung bình (>= 5.0), Yếu (< 5).
Viết chương trình thực hiện việc quản lý danh sách học sinh gồm
nhập xuất danh sách học sinh, in danh sách học sinh theo thứ tự
điểm trung bình giảm dần.
Con trỏ Con trỏ
n Khái niệm:
¨ Con trỏ là biến dùng để chứa địa chỉ của biến khác
¨ Cùng kiểu dữ liệu với kiểu dữ liệu của biến mà nó trỏ
tới.
n Cú pháp: <kiểudữliệu> *<têncontrỏ>;
Lưu ý:
¨ Có thể viết dấu * ngay sau kiểu dữ liệu;
¨ Ví dụ: int *a; và int* a; là tương đương.
¨ Thao tác với con trỏ:
n * là toán tử thâm nhập (dereferencing operator): *p là giá trị
nội dung vùng nhớ con trỏ đang trỏ đến;
n & là toán tử địa chỉ (address of operator): &x là địa chỉ của
biến x; nếu int *p = &x thì p ↔ x.
n Kiểm tra vùng nhớ cấp phát có thành công hay không?
¨ Thành công: con trỏ chứa địa chỉ đầu vùng nhớ được cấp phát;
¨ Không thành công: p = NULL.
n Sau đó
n Sắp xếp tăng, giảm (theo các phương pháp sắp xếp: Chọn
trực tiếp, Chèn trực tiếp, Nổi bọt BubbleSort, QuickSort,
HeapSort, ShellSort, RadixSort)
¨ Sau đó n Tìm phần tử nào đó trong mảng (tuần tự, nhị phân, nội suy).
Nội dung
n Định nghĩa cấu trúc
CHƯƠNG 2:
n Các thao tác trên cấu trúc
n Mảng cấu trúc, con trỏ cấu trúc
KIỂU DỮ LIỆU CẤU TRÚC
n Danh sách liên kết
n Ngăn xếp, Hàng đợi
TS. LÊ THỊ MỸ HẠNH
Bộ môn Công nghệ Phần mềm
Khoa Công Nghệ Thông Tin
Đại học Bách khoa – Đại học Đà Nẵng
12/09/2021
Cấu trúc và Hợp Cấu trúc và Hợp (2)
n Cấu trúc (Struct)
¨ Struct là một tập hợp các phần tử dữ liệu cùng kiểu hoặc khác kiểu được gộp n Ví dụ struct trong C++
chung thành một nhóm. Các phần tử này được gọi là thành viên của struct.
¨ Cú pháp ¨ Ví dụ về struct hình chữ nhật có hai chiều
Ví dụ:
struct structure_name {
<Kiểu dữ liệu thành viên số 1> <Tên thành viên số 1>; struct Student {
rộng và chiều cao:
<Kiểu dữ liệu thành viên số 2> <Tên thành viên số 2>; char name[20]; 1 #include <iostream>
… 2 using namespace std;
int id;
}; 3 struct Rectangle {
¨ Định nghĩa thể hiện của struct int age; 4 int width, height;
structure_name variable_name; }; 5 };
Student s; 6 int main(void) {
¨ Cách truy cập biến struct 7 struct Rectangle rec;
?
n Biến của struct có thể được truy cập bằng cách sử dụng thể hiện 8 rec.width = 8;
của struct theo sau bởi toán tử (.) Và sau đó là trường của struct. 9 rec.height = 5;
10 cout << "Dien tich hinh chu nhat la: "
n Ví dụ: s.name
11 << (rec.width * rec.height) << endl;
s.id
12 return 0;
s.age
13 }
12/09/2021 12/09/2021
12/09/2021
Cấu trúc và Hợp (5) Cấu trúc và Hợp (5)
n Mảng cấu trúc
n Mảng cấu trúc struct SinhVien {
void NhapMang(SinhVien a[], int &n) {
do{
char ten[20]; printf("Cho biet so Sinh vien: ");
¨ Một sinh viên có các thông tin bao gồm: Tên char ma[10];
int namsinh;
scanf("%d", &n);
} while (n <= 0);
sinh viên, mã số sinh viên, năm sinh, điểm float dtb;
int songaynghi;
for (int i = 1; i <= n; i++) {
printf("Thong tin Sinh vien thu %d la: \n", i);
trung bình và số ngày nghỉ. Nhà trường cần };
void XuatMang(SinhVien a[], int n) {
printf("Ten: \n"); fflush(stdin); gets(a[i].ten);
printf("Ma so: \n"); fflush(stdin); gets(a[i].ma);
nhập thông tin của tất cả các sinh viên đang printf("Ten\t\t Ma\t\t NamSinh\t DTB\t \tSoNgayNghi");
for (int i = 1; i <= n; i++) {
printf("Nam sinh :\n");
scanf("%d", &a[i].namsinh);
theo học tại trường và lập ra danh sách các printf("\n%s %s \t %d \t\t %f \t%d\n",
a[i].ten,a[i].ma,a[i].namsinh,
printf("Diem Trung Binh: \n");
scanf("%f", &a[i].dtb);
học sinh có thành tích tốt trong học tập ( điểm }
a[i].dtb, a[i].songaynghi); printf("So ngay nghi: \n");
scanf("%d", &a[i].songaynghi);
trung bình lớn hơn 7.0) để khen thưởng và }
}
}
danh sách các học sinh có số ngày nghỉ lớn void main() {
SinhVien A[100];
hơn 3 để ra nhắc nhở sinh viên đó. int N;
NhapMang(A, N);
XuatMang(A, N);
getch();
12/09/2021 12/09/2021
}
12
12/09/2021
Trừu tượng hóa danh sách Trừu tượng hóa danh sách
n Đặc tả dữ liệu n Đặc tả dữ liệu
L = (a0, a1, …, an-1)
Là một dãy hữu hạn các phần tử
L = (a0, a1, … , an-1) trong đó ai là phần tử thứ i+1 của danh sách L
Ví dụ:
n Đặc tả các phép toán L = (1, 2, 3, 3, 4, 5)
Kiểm tra danh sách có rỗng hay không L = (‘Hùng’, ‘Cường’, ‘Sang’)
n Đặc tả các phép toán
Đếm số phần tử của danh sách ¨ Kiểm tra danh sách có rỗng hay không: empty(L)
Trả về phần tử ở vị trí thứ i của danh sách ¨ Đếm số phần tử của danh sách: length(L)
Thêm phần tử x vào vị trí i trong danh sách ¨ Trả về phần tử ở vị trí thứ i của danh sách: element(L, i)
¨ Thêm phần tử x vào vị trí i trong danh sách: insert(L, i, x)
Thêm phần tử x vào đuôi danh sách ¨ Thêm phần tử x vào đuôi danh sách: append(L, x)
Loại phần tử ở vị trí thứ i trong danh sách ¨ Loại phần tử ở vị trí thứ i trong danh sách: erase(L, i)
13 14
Ví dụ Cài đặt danh sách bằng mảng
n L = (1, 2, 3, 3, 4, 5) n Mảng một chiều tĩnh
int dayso[100];
n empty(L) → false Phanso dayphanso[15];
n length(L) → 6 n Mảng một chiều động
¨ Cấp phát bộ nhớ bằng phép new
n element(L, 0) → 1 int *daysod = new int[100];
n element(L, 2) → 3 Phanso *dayphansod = new Phanso[15];
¨ Khi không dùng nữa, phải giải phóng bộ nhớ bằng phép delete
n insert(L, 2, 10) → L = (1, 2, 10, 3, 3, 4, 5) delete [] daysod;
n append(L, -5) → L = (1, 2, 10, 3, 3, 4, 5, -5) delete [] dayphansod;
Cài đặt danh sách bằng mảng Cài đặt danh sách bằng mảng
n Mảng (array) n insert(L, i, x)
¨ Tập hợp các phần tử (các biến) có cùng một kiểu a0 … ai-1 ai … an-1 ? ? … ?
¨ Một phần tử cụ thể trong mảng sẽ được xác 0 … i-1 i … n-1 n n+1 MAX-1
định và truy cập bởi một chỉ số
¨ Trong C/C++, các phần tử của mảng được đặt a0 … ai-1 x ai … an-1 ? … ?
cạnh nhau tạo thành một khối liên tục. Địa chỉ 0 … i-1 i i+1 … n n+1 MAX-1
thấp nhất tương ứng với phần tử đầu tiên, địa
chỉ cao nhất tương ứng với phần tử cuối cùng n Dồn tất cả các phần tử từ vị trí i tới vị trí n-1 về sau một vị trí
¨ Mảng thì có thể là một chiều hoặc nhiều chiều n Sau đó đặt giá trị x vào vị trí i
n Tăng số phần tử của danh sách lên 1
17 18
n Dồn tất cả các phần tử từ vị trí i+1 tới vị trí n-1 lên trước một vị trí
n Giảm số phần tử của danh sách đi 1
19 20
Danh sách Danh sách
n Cấu trúc dữ liệu tĩnh n Cấu trúc dữ liệu động
¨ Một số đối tượng dữ liệu trong chu kỳ sống của nó có thể thay ¨ Cấp phát động lúc chạy chương trình
đổi về cấu trúc, kích thước, như danh sách sinh viên trong lớp ¨ Các phần tử được cấp phát vùng nhớ rải rác trong bộ nhớ
có thể tăng hoặc giảm số lượng ¨ Kích thước danh sách chỉ bị giới hạn bởi RAM
n Nếu dùng cấu trúc dữ liệu tĩnh như mảng để biểu diễn -> thao tác
¨ Thao tác thêm/xoá đơn giản
phức tạp, kém tự nhiên -> chương trình khó đọc, khó bảo trì, sử
dụng bộ nhớ kém hiệu quả -> dữ liệu chiếm vùng nhớ đã được cấp
phát trong suốt thời gian chương trình hoạt động
¨ Ví dụ: Mảng 1 chiều
n Kích thước cố định
n Chèn 1 phần tử hoặc xoá 1 phần tử rất khó
n Các phần tử được lưu trữ tuần tự với chỉ số 0, 1, .. N-1
n Truy cập phần tử ngẫu nhiên
12/09/2021 12/09/2021
12/09/2021 12/09/2021
Danh sách liên kết (2) Danh sách liên kết (3)
n Các kiểu danh sách liên kết: n Cài đặt danh sách liên kết đơn (linked list)
¨ Khai báo LinkedList struct LinkedList{
¨ Danh sách liên vòng: Phần tử cuối danh sách liên với int data;
n data lưu giữa giá trị
phần tử đầu danh sách struct LinkedList *next;
n next là con trỏ đến node kế tiếp };
¨ Danh sách liên kết đơn vòng
¨ Tại sao next lại là kiểu LinkedList của chính nó?
¨ Tạo mới 1 Node
A B C D
typedef struct LinkedList *node;
¨ Danh sách liên kết đôi vòng //Từ giờ dùng kiểu dữ liệu LinkedList có thể thay bằng node cho ngắn gọn
12/09/2021 12/09/2021
Danh sách liên kết (5) Danh sách liên kết (6)
n Xóa Node khỏi danh sách liên kết
n Thêm Node vào danh sách liên kết
¨ Thêm vào vị trí bất kỳ ¨ Xóa đầu
1 node AddAt(node head, int value, int position){
2 if(position == 0 || head == NULL){
3 head = AddHead(head, value); // Nếu vị trí chèn là 0, tức là thêm vào đầu
4
5
}else{
// Bắt đầu tìm vị trí cần chèn. Ta sẽ dùng k để đếm cho vị trí 1 node DelHead(node head){
6
7
int k = 1;
node p = head;
2 if(head == NULL){
8
9
while(p != NULL && k != position){
p = p->next;
3 printf("\nCha co gi de xoa het!");
10 ++k; 4 }else{
11 }
12 5 node p = head;
13 if(k != position){
14 // Nếu duyệt hết danh sách lk rồi mà vẫn chưa đến vị trí cần chèn, ta sẽ mặc định chèn cuối 6 head = head->next;
15
16
// Nếu bạn không muốn chèn, hãy thông báo vị trí chèn không hợp lệ
head = AddTail(head, value);
7 delete p;
17
18
// printf("Vi tri chen vuot qua vi tri cuoi cung!\n");
}else{
8 }
19 node temp = CreateNode(value); return head;
20 temp->next = p->next;
21 p->next = temp; }
22 }
23 }
24 return head;
25 }
12/09/2021 12/09/2021
Danh sách liên kết (6) Danh sách liên kết (7)
n Xóa Node khỏi danh sách liên kết n Xóa Node khỏi danh sách liên kết
¨ Xóa cuối ¨ Xóa ở vị trí bất kỳ
1 node DelTail(node head){ 1 node DelAt(node head, int position){
2 if (head == NULL || head->next == NULL){ 2
3
if(position == 0 || head == NULL || head->next == NULL){
head = DelHead(head); // Nếu vị trí chèn là 0, tức là thêm vào đầu
3 return DelHead(head); 4
5
}else{
// Bắt đầu tìm vị trí cần chèn. Ta sẽ dùng k để đếm cho vị trí
4 } 6 int k = 1;
7 node p = head;
5 node p = head; 8 while(p->next->next != NULL && k != position){
9 p = p->next;
6 while(p->next->next != NULL){ 10 ++k;
7 p = p->next; 11
12
}
8 } 13
14
if(k != position){
// Nếu duyệt hết danh sách lk rồi mà vẫn chưa đến vị trí cần chèn, ta sẽ mặc định xóa cuối
9 node q = p->next; 15 // Nếu bạn không muốn xóa, hãy thông báo vị trí xóa không hợp lệ
16 head = DelTail(head);
10 p->next = p->next->next; // Cho next bằng NULL 17 // printf("Vi tri xoa vuot qua vi tri cuoi cung!\n");
18 }else{
11 // Hoặc viết p->next = NULL cũng được 19 node q = p-next;
12 delete q; 20
21
p->next = p->next->next;
delete q;
return head; 22
23 }
}
} }
return head;
12/09/2021 12/09/2021
1 node DelAt(node head, int position){
2
3
if(position == 0 || head == NULL || head->next == NULL){
head = DelHead(head); // Nếu vị trí chèn là 0, tức là thêm vào đầu
Danh sách liên kết (7)
4 }else{
5 // Bắt đầu tìm vị trí cần chèn. Ta sẽ dùng k để đếm cho vị trí n Lấy giá trị ở vị trí bất kỳ
6 int k = 1; ¨
7 node p = head;
8 while(p->next->next != NULL && k != position){
1 int Get(node head, int index){
9 p = p->next; 2 int k = 0;
10 ++k; 3 node p = head;
11 } 4 while(p->next != NULL && k != index){
12
13 if(k != position){
5 ++k;
14 // Nếu duyệt hết danh sách lk rồi mà vẫn chưa đến vị trí cần chèn, ta sẽ mặc định xóa cuối 6 p = p->next;
15 // Nếu bạn không muốn xóa, hãy thông báo vị trí xóa không hợp lệ 7 }
16 head = DelTail(head); 8 return p->data;
17 // printf("Vi tri xoa vuot qua vi tri cuoi cung!\n");
18 }else{
9 }
19 node q = p-next;
20 p->next = p->next->next;
21 delete q;
22 }
23 }
return head;
}
12/09/2021 12/09/2021
Danh sách liên kết (8) Danh sách liên kết (9)
n Tìm kiếm trong danh sách liên kết n Duyệt danh sách liên kết
¨ Hàm tìm kiếm này sẽ trả về chỉ số của Node đầu tiên có giá trị 1 void Traverser(node head){
bằng với giá trị cần tìm. Nếu không tìm thấy, chúng ta trả về -1. 2 for(node p = head; p != NULL; p = p->next){
3 cout<<setw(5)<< p->data;
1 int Search(node head, int value){ 4 }
2 int position = 0; 5 cout<<endl;
3 for(node p = head; p != NULL; p = p->next){ 6 }
4 if(p->data == value){
5 return position;
6 } n Xoá tất cả các phần tử trong danh sách
7 ++position; 1 node DelByVal(node head, int value){
8 } 2 int position = Search(head, value);
9 return -1; 3 while(position != -1){
10 } 4 DelAt(head, position);
5 position = Search(head, value);
6 }
7 return head;
8 }
12/09/2021 12/09/2021
38
12/09/2021
Ngăn xếp Ngăn xếp
Trừu tượng hóa cấu trúc ngăn xếp n Ứng dụng
Đặc tả dữ liệu ¨ Trực tiếp
A = (a0, a1, …, an-1)
trong đó an-1 là đỉnh ngăn xếp n Nhật trình lướt web lưu trong trình duyệt
Đặc tả các phép toán n Chuỗi undo trong một trình soạn thảo văn bản
1. Thêm phần tử x vào đỉnh ngăn xếp: push(x) n Việc lưu trữ các biến cục bộ khi một hàm gọi hàm
2. Loại phần tử ở đỉnh ngăn xếp: pop() khác và hàm này lại gọi tới hàm khác nữa, …
3. Kiểm tra ngăn xếp có rỗng hay không: isEmpty()
¨ Gián tiếp
4. Kiểm tra ngăn xếp có đầy hay không: isFull()
5. Đếm số phần tử của ngăn xếp: size() n Cấu trúc dữ liệu phụ trợ cho các thuật toán
6. Trả về phần tử ở đỉnh ngăn xếp: top() n Một phần của CTDL khác
39 40
ngoặc đóng “)“, “]“, “}“ tương ứng. variable, an arithmetic operator, or a number
Output: true if and only if all the grouping symbols in X match
n Ví dụ Let S be an empty stack
for i=0 to n-1 do
¨ cân xứng: ( )(( )){([( )])} if X[i] is an opening grouping symbol then
¨ không cân xứng: ((( )(( )){([( )])} S.push(X[i])
else if X[i] is a closing grouping symbol then
¨ không cân xứng: )(( )){([( )])} if S.isEmpty() then
¨ không cân xứng: ({[ ])} return false {nothing to match with}
if S.pop() does not match the type of X[i] then
¨ không cân xứng: ( return false {wrong type}
if S.isEmpty() then
return true {every symbol matched}
else
43
return false {some symbols were never matched}
44
Ngăn xếp Ngăn xếp
n Ứng dung n Bài tập
¨ Kiểm tra thẻ HTML cân xứng
n Mỗi thẻ mở <name> phải được cặp với một thẻ đóng </name> tương ứng 1. Viết chương trình cài đặt ngăn xếp bằng
mảng.
<body>
<center> The Little Boat 2. Viết chương trình cài đặt ngăn xếp bằng
<h1> The Little Boat </h1>
47 48
Shared
Service
56
55
Nội dung
n Lớp – Quyền truy xuất
Lập trình C/C++ n Khai báo, định nghĩa 1 lớp đơn giản
LỚP và ĐỐI TƯỢNG n Hàm thành viên nội tuyến (inline)
n Hàm xây dựng (constructor)
(CLASS & OBJECT) n Hàm hủy (destructor)
Khoa Công Nghệ Thông Tin Đại học n Hàm bạn (friend) – Lớp bạn
Bách khoa – Đại học Đà Nẵng n Đối số mặc định
n Đối số thành viên ẩn (con trỏ this)
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 1 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 2
Objectname.datamember n Public
¨ Các thành viên được khai báo là public có thể được truy nhập từ bên ngoài đối
n Hàm thành viên: tượng
¨ là mặc định đối với các thành viên của struct
Objectname. Memberfunction(parameter)
¨ Ví dụ: n protected
Point pt; n friend
pt.SetPt(10,20);
pt.OffsetPt(2,2);
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 5 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 6
Đóng gói trong C++ Ví dụ: Lớp đơn giản
Tạo ra
n Khi nào sử dụng quyền nào? đối tượng
class Point { thuộc lớp
¨ Theo phong cách lập trình hướng đối tượng tốt, ta sẽ int xVal, yVal; void main() { Point
giữ mọi thành viên dữ liệu ở dạng private (che dấu dữ public: Point pt;
Khai báo void SetPt (int, int);
liệu). Lớp void OffsetPt (int, int); pt.SetPt(10,20);
}; pt.OffsetPt(2,2);
¨ Các phương thức thường khai báo là public để có thể …….. Gọi hàm
void Point::SetPt (int x, int y) { trên
liên lạc được với đối tượng từ bên ngoài(giao diện của Định nghĩa xVal = x; đối tượng
đối tượng). các hàm yVal = y;
thành viên } pt.xVal = 10; // Đúng hay sai?
¨ Các phương thức tiện ích chỉ được dùng bởi các void Point::OffsetPt (int x, int y) {
xVal += x; Point pt1, pt2, pt3;
phương thức khác trong cùng lớp nên được khai báo ……….
yVal += y;
private. }
}
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 7 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 8
Khai báo các phương thức Khai báo các phương thức
n Giao diện của phương thức luôn đặt trong định nghĩa
lớp, cũng như các khai báo thành viên dữ liệu.
n Hàm inline:
n Phần cài đặt (định nghĩa phương thức) có thể đặt trong ¨ Cải thiện tốc độ thực thi
định nghĩa lớp hoặc đặt ở ngoài. ¨ Tốn bộ nhớ (dành cho mã lệnh) khi thực thi.
n Hai lựa chọn: Cách 2:
class Point { class Point {
class Point { class Point { int xVal, yVal; thêm int xVal, yVal;
Từ
int xVal, yVal; int xVal, yVal; Cách 1: public: public:
public: public: void SetPt (int x, int y) { khóa void SetPt (int, int);
void SetPt (int, int); void SetPt (int x, int y) { Định xVal = x; inline void OffsetPt (int, int);
void OffsetPt (int, int); xVal = x; nghĩa yVal = y; };
}; yVal = y; bên }
inline void Point::SetPt (int x, int y) {
void Point:: SetPt (int x, int y) { } trong void OffsetPt (int x, int y) {
xVal = x;
xVal = x; yVal = y; void OffsetPt (int, int); lớp xVal += x;
yVal = y;
yVal += y;
} }; } }
}; ……………
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 9 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 10
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 11 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 12
Đặt khai báo lớp ở đâu? File header car.h
n Để đảm bảo tính đóng gói, ta thường đặt khai // car.h
#ifndef CAR_H
báo của lớp trong file header #define CAR_H
¨ tên file thường trùng với tên lớp. Ví dụ khai báo lớp class Car {
Car đặt trong file “car.h” public:
//...
n Phần cài đặt (định nghĩa) đặt trong một file void drive(int speed, int distance);
nguồn tương ứng //...
void stop();
¨ “car.cpp” hoặc “car.cc” //...
void turnLeft();
n Quy ước đặt khai báo/định nghĩa của lớp trong private:
file trùng tên lớp được chấp nhận rộng rãi trong int vin; //...
C++ string make; //...
string model; //...
¨ là quy tắc bắt buộc đối với các lớp của Java string color; //...
};
#endif
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 13 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 14
Định nghĩa các phương thức Định nghĩa các phương thức
n Định nghĩa của các phương thức cần đặt trong 1 file n Khi định nghĩa một phương thức, ta cần sử dụng toán tử
nguồn trùng tên với tên lớp phạm vi để trình biên dịch hiểu đó là phương thức của
n File bắt đầu với các lệnh #include và có thể có các khai một lớp cụ thể chứ không phải một hàm thông thường
báo using cho các namespace khác
n Bên cạnh việc include các thư viện C++ cần thiết, ta con ¨ Ví dụ, định nghĩa phương thức drive của lớp Car được viết
như sau Toán tử định phạm vi
phải include header file chứa khai báo lớp
// car.cpp
// car.cpp ...
#include <iostream> void Car::drive(int speed, int distance)
#include <string> {
#include “car.h” //method definition
using namespace std; Tên lớp }
...
Tên phương thức
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 15 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 16
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 17 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 18
Đối số thành viên ẩn Con trỏ this
n Con trỏ *this: n Tuy không bắt buộc sử dụng tường minh con trỏ this, ta
có thể dùng nó để giải quyết vấn đề tên trùng và phạm vi
¨ Là 1 thành viên ẩn, có thuộc tính là private.
void Foo::bar()
¨ Trỏ tới chính bản thân đối tượng. {
int x;
x = 5; // local x
void Point::OffsetPt (int xValL, int yVal) { void Point::OffsetPt (int x, int y) { this->x = 6; // this instance’s x
this->xVal += xVal; this->xVal += x; }
this->yVal += yVal; this->yVal += y;
} } hoặc
void Foo::bar(int x)
{
this->x = x;
• 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
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 19 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 20
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 23 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 24
Hàm xây dựng Copy constructor
n Đối với constructor mặc định, nếu ta không cung n Copy constructor là constructor đặc biệt được gọi khi ta tạo
đối tượng mới là bản sao của một đối tượng đã có sẵn
cấp một phương thức constructor nào, C++ sẽ MyClass x(5);
tự sinh constructor mặc định là một phương MyClass y = x; hoặc MyClass y(x);
thức rỗng (không làm gì) n C++ cung cấp sẵn một copy constructor, nó chỉ đơn giản
¨ mục đích để luôn có một constructor nào đó để gọi copy từng thành viên dữ liệu từ đối tượng cũ sang đối
khi không có tham số nào tượng mới.
n Tuy nhiên, trong nhiều trường hợp, ta cần thực hiện các
n Tuy nhiên, nếu ta không định nghĩa constructor công việc Khởi tạo khác trong copy constructor
mặc định nhưng lại có các constructor khác, ¨ Thí dụ: lấy giá trị cho một ID duy nhất từ đâu đó, hoặc thực
trình biên dịch sẽ báo lỗi không tìm thấy hiện sao chép “sâu” (chẳng hạn khi một trong các thành viên
constructor mặc định nếu ta không cung cấp là con trỏ giữ bộ nhớ cấp phát động)
tham số khi tạo thể hiện. n Trong trường hợp đó, ta có thể định nghĩa lại copy
constructor
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 25 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 26
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 27 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 28
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 31 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 32
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 35 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 36
friend – Ví dụ friend – Ví dụ
n Khai báo hàm nhân ma trận với vecto n Khai báo hàm nhân ma trận với vecto không dùng hàm bạn
const int N = 4;
class Vector
Vector Multiply(const Matrix &m, const Vector &v)
{
double a[N]; {
public: Vector r;
double Get(int i) const {return a[i];} for (int i = 0; i < N; i++)
void Set(int i, double x) {a[i] = x;} {
}; r.Set(i, 0);
class Matrix for (int j = 0; j < N; j++)
{ r.Set(i, r.Get(i)+ m.Get(i,j)*v.Get(j));
double a[N][N]; }
public: return r;
double Get(int i, int j) const {return a[i][j];}
}
void Set(int i, int j, double x) {a[i][j] = x;}
//...
};
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 37 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 38
friend – Ví dụ friend – Ví dụ
n Khai báo hàm nhân ma trận với vecto có dùng hàm bạn
const int N = 4; Vector Multiply(const Matrix &m, const Vector &v)
class Matrix; // khai báo forward {
class Vector {
double a[N]; Vector r;
public: for (int i = 0; i < N; i++)
double Get(int i) const {return a[i];} {
void Set(int i, double x) {a[i] = x;}
friend Vector Multiply(const Matrix &m, const Vector &v); r.a[i] = 0;
}; for (int j = 0; j < N; j++)
class Matrix { r.a[i] += m.a[i][j]*v.a[j];
double a[N][N];
public: }
double Get(int i, int j) const {return a[i][j];} return r;
void Set(int i, int j, double x) {a[i][j] = x;} }
friend Vector Multiply(const Matrix &m, const Vector &v);
};
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 39 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 40
class Image {
class Point { class Image { public:
int xVal, yVal; public: Image(const int w, const int h);
Image(const int w, const int h); Khai báo bình thường
public: private: như dữ liệu thành viên
private: const int width;
Point (int x, int y) { int width; const int height;
xVal = x; int height; //...
yVal = y; //... };
} };
Image::Image(const int w, const int h) { class Image {
// ……………………
width = w; const int width = 256;
}; height = h; Khởi tạo const int height = 168;
//..................... SAI //...
} };
Point::Point (int x, int y)
: xVal(x), yVal(y) Image::Image (const int w, const int h)
{ } Image::Image (const int w, const int h) Khởi tạo ĐÚNG
: width(w), height(h)
{ //............... }
: width(w), height(h) thông qua danh sách
{//………..} khởi tạo thành viên
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 41 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 42
Thành viên hằng Thành viên hằng
n Hằng đối tượng: không được thay đổi giá trị. inline char *strdup(const char *s){
n Hàm thành viên hằng: return strcpy(new char[strlen(s) + 1], s);
¨ Được phép gọi trên hằng đối tượng.(đảm bảo không thay đổi giá
trị của đối tượng chủ) }
¨ Không được thay đổi giá trị dữ liệu thành viên. class string{
n nên khai báo mọi phương thức truy vấn là hằng, vừa để char *p;
báo với trình biên dịch, vừa để tự gợi nhớ. public:
class Set { void main() { string(char *s = "") {p = strdup(s);}
public: const Set s;
Set(void){ card = 0; } s.AddElem(10); // SAI ~string() {delete [] p;}
Bool Member(const int) const; string(const string &s2) {p = strdup(s2.p);}
s.Member(10); // OK
void AddElem(const int);
//... void Output() const {cout << p;}
}; }
void ToLower() {strlwr(p);}
Bool Set::Member (const int elem) const
{ //... };
}
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 43 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 44
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 45 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 46
Khởi tạo biến đếm bằng 0 vì ban đầu không có đối tượng nào
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 47 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 48
Thành viên tĩnh Thành viên tĩnh
Định nghĩa và khởi tạo n chương trình demo sử dụng MyClass
n thành viên tĩnh được lưu trữ độc lập với các thể int main()
{
hiện của lớp, do đó, các thành viên tĩnh phải MyClass* x = new MyClass;
được định nghĩa x->PrintCount();
int MyClass::count; MyClass* y = new MyClass;
x->PrintCount();
n ta thường định nghĩa các thành viên tĩnh trong y->PrintCount();
file chứa định nghĩa các phương thức delete x;
There are currently 1 instance(s) of MyClass.
y->PrintCount();
n nếu muốn khởi tạo giá trị cho thành viên tĩnh ta }
There are currently 2 instance(s) of MyClass.
There are currently 2 instance(s) of MyClass.
cho giá trị khởi tạo tại định nghĩa There are currently 1 instance(s) of MyClass.
int MyClass::count = 0;
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 49 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 50
Thành viên tham chiếu Thành viên là đối tượng của 1 lớp
n Tham chiếu dữ liệu thành viên: n Dữ liệu thành viên có thể có kiểu:
class Image { ¨ Dữ liệu (lớp) chuẩn của ngôn ngữ.
int width; Khai báo bình thường
int height; như dữ liệu thành viên
¨ Lớp do người dùng định nghĩa (có thể là chính lớp đó).
int &widthRef;
//... class Point { ……. };
}; class Rectangle {
public:
class Image { Rectangle (int left, int top, int right, int bottom);
int width; //... Khởi tạo cho các
int height; private: dữ liệu thành viên
Khởi tạo Point topLeft; qua danh sách khởi
int &widthRef = width;
SAI Point botRight; tạo thành viên
//...
}; };
Rectangle::Rectangle (int left, int top, int right, int bottom)
Image::Image (const int w, const int h) Khởi tạo ĐÚNG :topLeft(left, top), botRight(right, bottom)
: widthRef(width) thông qua danh sách {
{ //……………... } khởi tạo thành viên }
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 57 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 58
Thành viên là đối tượng của 1 lớp Thành viên là đối tượng của 1 lớp
class Diem { class Diem {
double x,y;
double x,y;
public:
public:
Diem(double xx, double yy) {x = xx; y = yy;}
Diem(double xx, double yy) {x = xx; y = yy;} // ...
// ... };
}; class TamGiac {
class TamGiac { Diem A,B,C;
Diem A,B,C; public:
public: TamGiac(double xA, double yA, double xB, double yB,
void Ve() const; double xC, double yC):A(xA,yA),(xB,yB),C(xC,yC){}
// ... void Ve() const;
// ...
};
};
TamGiac t; // Bao sai TamGiac t(100,100,200,400,300,300);
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 59 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 60
Mảng các đối tượng Mảng các đối tượng
n Sử dụng hàm xây dựng không đối số (hàm n Sử dụng dạng con trỏ:
xây dựng mặc nhiên - default constructor). ¨ Cấp vùng nhớ:
VD: Point pentagon[5]; VD: Point *pentagon = new Point[5];
n Sử dụng bộ khởi tạo mảng: ¨ Thu hồi vùng nhớ:
delete[] pentagon;
VD: Point triangle[3] =
delete pentagon; // Thu hồi vùng nhớ đầu
{ Point(4,8), Point(10,20), Point(35,15) };
Ngắn gọn: class Polygon {
Không cần biết kích
public:
Set s[4] = { 10, 20, 30, 40 }; //... thước mảng.
tương đương với: private:
Point *vertices; // các đỉnh
Set s[4] = { Set(10), Set(20), Set(30), Set(40) }; int nVertices; // số các đỉnh
};
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 61 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 62
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 63 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 64
Bộ môn Công nghệ Phần mềm n Các đối tượng được thừa kế
Khoa Công Nghệ Thông Tin ¨ Downcast
CHƯƠNG
Đại học Bách khoa – Đại học Đà Nẵng ¨ Upcast
6 n Đa thừa kế - Sự mơ hồ
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 1 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 2
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 3 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 4
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 5 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 6
Kế thừa Kế thừa
n Ích lợi: có thể tận dụng lại n Lớp cha – superclass (hoặc lớp cơ sở - base class)
¨ lớp tổng quát hơn trong một quan hệ “là”
¨ Các thuộc tính chung ¨ các đối tượng thuộc lớp cha có cùng tập thuộc tính và
¨ Các hàm có thao tác tương tự hành vi S
n Lớp con – subclass (hoặc lớp dẫn xuất – derived class)
¨ lớp cụ thể hơn trong một quan hệ “là”
Lớp cơ sở LỚP CHA ¨ các đối tượng thuộc lớp con có cùng tập thuộc tính và
(Base class) (Super class) hành vi S (do thừa kế từ lớp cha), kèm thêm tập thuộc tính
và hành vi S’ của riêng lớp con
n Quan hệ thừa kế - Inheritance hay còn gọi là quan hệ “là”
n Ta nói rằng lớp con “thừa kế từ” lớp cha, hoặc lớp con
Lớp dẫn xuất LỚP CON “được dẫn xuất từ” lớp cha.
(Derived class) (Sub class)
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 7 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 8
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 9 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 10
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 15 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 16
Ví dụ MotorVehicle Ví dụ MotorVehicle
n Ta định nghĩa constructor, destructor, và hàm
n Tạo lớp con Car
drive() (ở đây, ta chỉ định nghĩa tạm drive())
Chỉ rõ quan hệ giữa lớp con Car
MotorVehicle::MotorVehicle(int vin, string make, string và lớp cha MotorVehicle
model)
{
this->vin = vin; class Car : public MotorVehicle
this->make = make; {
this->model = model; public:
} Car (int passengers);
MotorVehicle::~MotorVehicle(){}// We could actually use ~Car();
// the default destructor private:
void MotorVehicle::drive(int speed, int distance) int passengers;
{ };
cout << “Dummy drive() of MotorVehicle.” << endl;
}
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 17 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 18
Định nghĩa lớp con Định nghĩa lớp con
n Hiện giờ constructor của lớp Car chỉ nhận 1 tham số n Tối thiểu, ta sẽ định nghĩa constructor và (có thể cả)
passengers, trong khi các đối tượng Car cũng có tất destructor
cả các thành viên được thừa kế từ MotorVehicle ¨ Các lớp con không thừa kế constructor và
destructor của lớp cha, do việc khởi tạo và huỷ các lớp
Car (int passengers); khác nhau là khác nhau
n Do vậy, trừ khi ta muốn dùng giá trị mặc định cho các n Phiên bản constructor đầu tiên mà ta có thể nghĩ tới:
thành viên được thừa kế, ta nên truyền thêm tham số
cho constructor để khởi tạo vin, make, model. Car::Car(int vin, string make, string model, int passengers)
Quy ước: đặt các tham số {
class Car : public MotorVehicle { cho lớp cha lên đầu danh sách. this->vin = vin;
public: this->make = make;
Car (int vin, string make, string model, int passengers); this->model = model;
~Car(); this->passengers = passengers;
private: }
int passengers; Car::~Car() {}
};
Car a(…);
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 19 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 20
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 25 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 26
n Có thể dùng thừa kế private để tạo lớp n Vậy, đoạn mã sau sẽ có lỗi: if (this->make == “Ford”) {
...
}
con có mọi chức năng của lớp cha nhưng }
lại không cho bên ngoài biết về các chức Lớp Truck không có quyền truy
nhập thành viên private make
năng đó. của lớp cơ sở MotorVehicle
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 27 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 28
n Vậy, đoạn mã sau sẽ không có lỗi if (this->make == “Ford”) { được nhìn thấy từ
...
n Tuy nhiên truy nhập từ bên ngoài vẫn } ¨ mọi phương thức bên trong Car,
}
sẽ bị cấm ¨ mọi phương thức thuộc các lớp con của Car
Lớp Truck có quyền truy
nhập thành viên protected make n Tuy nhiên, mọi đối tượng khác của C++ không
của lớp cơ sở MotorVehicle
nhìn thấy quan hệ này
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 31 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 32
class Truck : public MotorVehicle { ¨ Sử dụng các con trỏ để khai báo các đối tượng thuộc
public: các lớp tương ứng
Truck(int vin,string make, string model, int maxPayload);
~Truck(); ...
void Load(); mvPointer = new MotorVehicle(10, “Honda”, “S2000”);
void Unload(); cPointer = new Car(10, “Honda”, “S2000”, 2);
protected: tPointer = new Truck(10, “Toyota”, “Tacoma”, 5000);
int maxPayload; ...
};
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 37 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 38
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 39 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 40
Upcast Upcast
n Điều đáng lưu ý là ta thực hiện tất cả các lệnh n Upcast là quá trình tương tác với thể hiện của lớp dẫn
gán đó mà không cần đổi kiểu tường minh xuất như thể nó là thể hiện của lớp cơ sở.
¨ do mọi lớp con của MotorVehicle đều chắc chắn có n Cụ thể, đây là việc đổi một con trỏ (hoặc tham chiếu) tới
lớp dẫn xuất thành một một con trỏ (hoặc tham chiếu) tới
mọi thành viên và phương thức có trong một
MotorVehicle, việc tương tác với thể hiện của các lớp cơ sở
¨ ta đã thấy ví dụ về upcast đối với con trỏ
lớp này như thể chúng là MotorVehicle không có
MotorVehicle* mvPointer2 = cPointer;
chút rủi ro nào
¨ ví dụ về upcast đối với tham chiếu
¨ Ví dụ, lệnh sau đây là hợp lệ, bất kể mvPointer2
// Refer to the instance pointed to by cPointer
đang trỏ tới một MotorVehicle, một Car, hay một MotorVehicle& mvReference = *cPointer;
Truck // Refer to an automatically-allocated instance c
Car c(10, “Honda”, “S2000”, 2);
mvPointer2->Drive(); MotorVehicle& mvReference2 = c;
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 41 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 42
Upcast Upcast
n Upcast thường gặp tại các định nghĩa hàm, khi
n Nếu ta dùng một con trỏ tới lớp cơ sở để
một con trỏ/tham chiếu đến lớp cơ sở được yêu trỏ tới một thể hiện của lớp dẫn xuất, trình
cầu, nhưng con trỏ/tham chiếu đến lớp dẫn xuất biên dịch sẽ chỉ cho ta coi đối tượng như
cũng được chấp nhận thể nó thuộc lớp cơ sở
¨ xét hàm sau ¨ Như vậy, ta không thể làm như sau
void sellMyVehicle(MotorVehicle& myVehicle){...} mvPointer2->Load(); // Error
¨ có thể gọi sellMyVehicle một cách hợp lệ với tham n Đó là vì trình biên dịch không thể đảm bảo
số là một tham chiếu tới một MotorVehicle, một Car,
hoặc một Truck. rằng con trỏ thực ra đang trỏ tới một thể
hiện của Truck.
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 43 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 44
Upcast Downcast
n Chú ý rằng khi gắn một con trỏ/tham chiếu lớp cơ sở với một thể hiện
của lớp dẫn xuất, ta không hề thay đổi bản chất của đối tượng được n Upcast là đổi con trỏ/tham chiếu tới lớp
trỏ tới
¨ Ví dụ, lệnh dẫn xuất thành con trỏ/tham chiếu tới lớp
MotorVehicle* mvPointer2 = tPointer; cơ sở.
mvPointer2->Load();////error
//Point to a Truck n Downcast là quy trình ngược lại: đổi kiểu
không làm một thể hiện của Truck suy giảm thành một
n
MotorVehicle, nó chỉ cho ta một cách nhìn khác đối với đối tượng
con trỏ/tham chiếu tới lớp cơ sở thành con
Truck và tương tác với đối tượng đó. trỏ/tham chiếu tới lớp dẫn xuất.
¨ Do vậy, ta vẫn có thể truy nhập tới các thành viên và phương thức
của lớp dẫn xuất ngay cả sau khi gán con trỏ lớp cơ sở tới nó: n downcast là quy trình rắc rối hơn và có
tPointer = new Truck(…);
mvPointer2 = tPointer; // Point to a Truck nhiều điểm không an toàn
tPointer->Load(); // We can still do this
mvPointer2->Load(); // Even though we can’t do this (error)
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 45 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 46
Downcast Downcast
n Trước hết, downcast không phải là một quy trình tự động - nó n Nếu ta biết chắc chắn rằng một con trỏ lớp cơ sở quả thực
luôn đòi hỏi đổi kiểu tường minh (explicit type cast)
đang trỏ tới một lớp con, ta có thể tự đổi kiểu cho con trỏ
lớp cơ sở bằng cách sử dụng static_cast
n Điều đó là hợp lý
Car* cPointer = new Car(10,”Honda”,”S2000”,2);
¨ nhớ lại rằng: không phải “mọi xe chạy bằng máy đều là xe tải” MotorVehicle *mv=cPointer;//Upcast
Car* cPointer2;
¨ do đó, rắc rối sẽ nảy sinh nếu trình biên dịch cho ta đổi một con cPointer2 = static_cast<Car *> ( mv); //(car*) mv
trỏ bất kỳ tới MotorVehicle thành một con trỏ tới Truck, trong
khi thực ra con trỏ đó đang trỏ tới một đối tượng Car. n Ta có thể thấy mối nguy hiểm khi làm việc này - chuyện gì
xảy ra nếu đối tượng ta đang cố đổi kiểu thực ra không
n Ví dụ, đoạn mã sau sẽ gây lỗi biên dịch: thuộc lớp mà ta nghĩ?
MotorVehicle* mvPointer3;
Truck* tPointer = new Truck(10, “Toyota”, “Tacoma”, 5000);
…
MotorVehicle* mv = tPointer; // Upcast
Car* cPointer2 = mvPointer3; // Error Car* cPointer2;
Truck* tPointer2 = mvPointer3; // Error cPointer2 = static_cast<Car*>(mv); // Explicit downcast
MotorCycle mcPointer2 = mvPointer3; // Error
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 47 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 48
Downcast Đa thừa kế
Car* cPointer = new Car(10,”Honda”,”S2000”,2);
MotorVehicle *mv=cPointer;//Upcast OptionList Window class OptionList { class Window {
Truck* tPointer = static_cast<Truck*>(mv); // Explicit downcast public: public:
tPointer->Load(); OptionList (int n); Window (Rect &);
~OptionList (); ~Window (void);
Menu //... //...
n Đoạn mã trên hoàn toàn hợp lệ và sẽ được trình }; };
biên dịch chấp nhận
class Menu
n Tuy nhiên, nếu chạy đoạn trình trên, chương OptionList object Window object Menu object : public OptionList, public Window {
trình có thể bị đổ vỡ (thường là khi lần đầu truy OptionList
Window OptionList
public:
data members Menu (int n, Rect &bounds);
nhập đến thành viên/phương thức được định data members data members
~Menu (void);
nghĩa của lớp dẫn xuất mà ta đổi tới) Window //...
data members };
n Ví dụ: Point-Circle Menu
Menu::Menu (int n, Rect &bounds) :
data members
OptionList(n), Window(bounds)
{ /* ... */ }
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 49 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 50
void main() {
void main() { n Không được thực hiện phép gán ngược:
Gọi Menu m1(….);
hàm Menu m1(….); xử lý
¨ Đối tượng lớp con = Đối tượng lớp cha; // SAI
m1.OptionList::Highlight(10);
của lớp m1.Highlight(10);
m1.Window::Highlight(20); Nếu muốn thực hiện
class Menu : public OptionList, public Window {
nào ? ….
phải tự định nghĩa
public:
…. //...
} phép ép kiểu
} Menu (Window&);
};
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 51 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 52
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 53 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 54
Các toán tử được tái định nghĩa
n 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 xây dựng sao chép:
Y::Y (const Y&)
¨ Phép gán:
Y& Y::operator = (const Y&)
n Nếu không định nghĩa, sẽ tự động có hàm xây ĐA HÌNH
dựng sao chép và phép gán do ngôn ngữ tạo ra.
=> SAI khi có con trỏ thành viên.
(POLYMORPHISM)
n Cẩn thận với toán tử new và delete.
Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 55 Khoa Công Nghệ Thông Tin - Đại Học Bách khoa Đà Nẵng 56
chú ý rằng kangaroo và cóc thuộc hai nhánh trong cây phả hệ động vật
Overloading vs. Overriding Overloading vs. Overriding
n Function overloading - Hàm chồng: dùng một tên n Đa hình được cài đặt bởi một khái niệm tương tự
hàm cho nhiều định nghĩa hàm, khác nhau ở danh nhưng hơi khác: method overriding
¨ “override” có nghĩa “vượt quyền”
sách tham số
n Method overriding: nếu một phương thức của lớp
n Method overloading – Phương thức chồng: tương cơ sở được định nghĩa lại tại lớp dẫn xuất thì định
tự nghĩa tại lớp cơ sở có thể bị “che” bởi định nghĩa tại
void jump(int howHigh); lớp dẫn xuất.
void jump(int howHigh, int howFar);
n Với method overriding, toàn bộ thông điệp (cả tên
¨ hai phương thức jump trùng tên nhưng có danh sách và tham số) là hoàn toàn giống nhau - điểm khác
tham số khác nhau nhau là lớp đối tượng được nhận thông điệp.
n Tuy nhiên, đây không phải đa hình hướng đối tượng Kangaroo k; Chú ý, Kangaroo và Frog đều là các lớp
Hai thông điệp Frog f; dẫn xuất từ lớp cơ sở gián tiếp Animal
mà ta đã định nghĩa, vì đây thực sự là hai thông giống nhau,
k.jump(10); // the kangaroo jumps 10m high
nhưng đích là hai
điệp jump khác nhau. lớp khác nhau f.jump(10); // the frog jumps 10m far
và kiểu tĩnh của pP là Point* nên gọi Point::draw(). n Với liên kết tĩnh, địa chỉ đoạn mã cần chạy cho một
lời gọi hàm cụ thể là không đổi trong suốt thời gian
chương trình chạy
Static polymorphism Static polymorphism
n Static polymorphism – đa hình tĩnh là kiểu n Đa hình tĩnh thích hợp cho các phương thức:
được định nghĩa tại một lớp, và được gọi từ một thể hiện
đa hình được cài đặt bởi liên kết tĩnh ¨
của chính lớp đó (trực tiếp hoặc gián tiếp qua con trỏ)
n Đối với đa hình tĩnh, trình biên dịch xác ¨ được định nghĩa tại một lớp cơ sở và được thừa kế public
nhưng không bị override tại lớp dẫn xuất, và được gọi từ
định trước định nghĩa hàm/phương thức một thể hiện của lớp dẫn xuất đó
nào sẽ được thực thi cho một lời gọi n trực tiếp hoặc gián tiếp qua con trỏ tới lớp dẫn xuất, hoặc
hàm/phương thức nào. n qua một con trỏ tới lớp cơ sở
¨ được định nghĩa tại một lớp cơ sở và được thừa kế public
và bị override tại lớp dẫn xuất, và được gọi từ một thể hiện
của lớp dẫn xuất đó (trực tiếp hoặc gián tiếp qua con trỏ
tới lớp dẫn xuất)
Hàm ảo
của Point và các lớp dẫn
xuất đều là hàm ảo
class Point{
public:
Hàm ảo
n Hàm ảo là phương thức được khai
báo với từ khoá virtual trong định ... n đa hình động dễ cài và cần thiết. Vậy tại sao lại cần đa hình
virtual void draw(); tĩnh?
nghĩa lớp (nhưng không cần tại định ...
nghĩa hàm, nếu định nghĩa hàm nằm };
n Trong Java, mọi phương thức đều mặc định là phương thức ảo,
ngoài định nghĩa lớp) ... tại sao C++ không như vậy?
n Một khi một phương thức được khai void Point::draw() n Có hai lý do:
{... } 1. tính hiệu quả
báo là hàm ảo tại lớp cơ sở, nó sẽ
tự động là hàm ảo tại mọi lớp dẫn class Circle: public Point { ¨ C++ cần chạy nhanh, trong khi liên kết động tốn thêm phần xử lý
xuất trực tiếp hoặc gián tiếp. public: phụ, do vậy làm giảm tốc độ
... ¨ Ngay cả những hàm ảo không được override cũng cần xử lý phụ
n Tuy không cần tiếp tục dùng từ khoá virtual void draw();
virtual trong các lớp dẫn xuất, nhưng Þ vi phạm nguyên tắc của C++: chương trình không phải chạy
... chậm vì những tính năng không dùng đến
vẫn nên dùng để tăng tính dễ đọc }; Không cần
của các file header. ... nhưng nên có 2. tính đóng gói
void Circle::draw() ¨ khi khai báo một phương thức là phương thức không ảo, ta có ý
¨ nhắc ta và những người dùng lớp
{... } rằng ta không định để cho phương thức đó bị override.
dẫn xuất của ta rằng phương thức
đó sử dụng liên kết động
Destructor ảo Destructor ảo
n Còn nếu ta dùng một con trỏ tới MotorVehicle thay cho n Chú ý: việc gọi nhầm destructor không ảnh hưởng
con trỏ tới Car, chỉ có destructor của MotorVehicle đến việc thu hồi bộ nhớ
được gọi. ¨ trong mọi trường hợp, phần bộ nhớ của đối tượng sẽ
được thu hồi chính xác
Car* c = new Car(10, “Suzuki”, “RSX-R1000”, 4); ¨ Trong ví dụ trước, kể cả nếu chỉ có destructor của
MotorVehicle được gọi, phần bộ nhớ của toàn bộ đối
MotorVehicle* mv = c; // Upcasting tượng Car vẫn được thu hồi
delete mv; // With static polymorphism, the compiler n Tuy nhiên, nếu không gọi đúng destructor, các đoạn
// thinks that this is referring to an mã dọn dẹp quan trọng có thể bị bỏ qua
// instance of MotorVehicle, so only the ¨ chẳng hạn xoá các thành viên được cấp phát động
// base class (MotorVehicle) destructor
// is invoked
Destructor ảo
n Quy tắc chung: mỗi khi tạo một lớp để được dùng
làm lớp cơ sở, ta nên khai báo destructor là hàm ảo.
¨
¨
kể cả khi destructor của lớp cơ sở rỗng (không làm gì)
Vậy ta sẽ sửa lại lớp MotorVehicle như sau:
Lớp trừu tượng
class MotorVehicle { - Abstract class
public:
MotorVehicle(int vin, string make, string model);
virtual ~MotorVehicle();
...
}
Tạo thể hiện của lớp Tạo thể hiện của lớp
n Khi mới giới thiệu khái niệm đối tượng, ta nói rằng n Tuy nhiên, với một số lớp, có
chúng có thể được nhóm lại thành các lớp, trong đó thể không hợp lý khi nghĩ đến
chuyện tạo thể hiện của các
mỗi lớp là một tập các đối tượng có cùng thuộc tính lớp đó
và hành vi. n Ví dụ, một hệ thống quản lý tài
n Ta cũng nói theo chiều ngược lại rằng ta có thể định sản (asset): chứng khoán
nghĩa một đối tượng như là một thể hiện của một (stock), ngân khoản (bank
account), bất động sản (real estate), ô tô (car), v.v..
lớp ¨ một đối tượng tài sản chính xác là cái gì?
n Nghĩa là: lớp có thể tạo đối tượng ¨ phương thức computeNetWorth() (tính giá trị) sẽ tính theo kiểu
n Hầu hết các lớp ta đã gặp đều tạo được thể hiện gì nếu không biết đó là ngân khoản, chứng khoán, hay ô tô?
¨ ta có thể nói rằng một đối tượng ngân khoản là một thể hiện của
¨ ta có thể tạo thể hiện của Point hay Circle tài sản, nhưng thực ra không phải, nó là một thể hiện của lớp
¨ ta có thể tạo thể hiện của MotorVehicle hay Car dẫn xuất của tài sản
n Số tín chỉ:
GIỚI THIỆU MÔN HỌC ¨ Lý thuyết: 1 đvht
¨ Bài tập: 2 đvht
n Hiểu và mô tả được các khái niệm và đặc trưng n Kiến thức cơ bản về C/C++
hướng đối tượng, thành phần, kỹ thuật lập trình n Kiến thức cơ bản về lập trình,
hướng đối tượng với C++
¨ các cấu trúc dữ liệu cơ bản: mảng, xâu, con trỏ
n Áp dụng các kỹ thuật để phân tích, thiết kế và
¨ giải thuật cơ bản: sắp xếp, tìm kiếm
cài đặt chương trình theo tiếp cận hướng đối
tượng n Phong cách lập trình (đặt tên, chú thích, lùi đầu dòng,
n Đánh giá các thiết kế và chương trình hướng đối tách dòng…) sẽ được yêu cầu trong các bài tập, bài thi.
tượng.
08/09/2020 6
08/09/2020
¢ Chương 4. Vào ra trên tệp n Lê Đăng Hưng, Đặng Tuấn Anh, Nguyễn Hữu Đức,
Nguyễn Thanh Thuỷ,
¢ Chương 5. Lớp và đối tượng
¨ Lập trình hướng đối tượng với C++. NXB Khoa học kỹ thuật
¢ Chương 6. Tính thừa kế và đa hình ¨ chỉ khuyên dùng trong trường hợp không thể đọc sách tiếng
¢ Chương 7. Một số kỹ thuật nâng cao trong C++ Anh,
¨ nội dung không cập nhật lắm.
08/09/2020 7 08/09/2020 8
¨ Chuyên cần
08/09/2020 9 08/09/2020 10
BÀI KIỂM TRA Chuẩn và quy ước lập trình
n Test 1: Các mở rộng của C++ & Con trỏ trong C++
n Test 2: Lớp và đối tượng & Đa năng hóa
n Test 3: Kế thừa & Đa hình
n Test 4: Template & Exception
n Các bài test sẽ làm trắc nghiệm trên máy tính sau
khi kết thúc phần kiến thức dạy trên lớp. (thường
sẽ test vào cuối tuần, tại phòng thực hành Khoa
CNTT).
08/09/2020 11
Chuẩn và quy ước lập trình Chuẩn và quy ước lập trình
n Vì sao phải có chuẩn và quy ước? n Không có chuẩn chung toàn thế giới!!
n Làm việc một mình: n Quy ước đặt tên (Naming Convention):
¨ Tự làm tự hiểu. ¨ Quy tắc vàng: tên phải thể hiện ý nghĩa.
¨ Mình luôn hiểu mình? n x, y, f, g, … ?!
n total, rate, create, run, … !!
n Làm việc nhóm: ¨ Quy tắc đặt tên theo kiểu “lạc đà” (Camel Case)
¨ Mỗi người một việc. n Dùng để viết các từ dính liền nhau.
¨ Ráp nối công việc. n Viết hoa chữ cái đầu mỗi từ.
¨ Mọi người luôn hiểu nhau? n UpperCamelCase (thường gọi là PascalCase) nếu ký tự đầu
tiên của câu được viết hoa.
¨ Ví dụ: TheQuickBrownFoxJumpsOverTheLazyDog
Phối hợp công việc hiệu quả Áp đặt kỷ luật!! n lowerCamelCase (thường gọi là camelCase) nếu ký tự đầu
tiên của câu được viết thường.
¨ Ví dụ: theQuickBrownFoxJumpsOverTheLazyDog
n Input:
¨ Mảng A[n] và giá trị cần tìm x
n Output:
¨ True: nếu tìm thấy;
¨ False: nếu không tìm thấy.
n h=2
HEAP SORT
n Ví dụ: CHƯƠNG 7:
Template
(Khuôn mẫu)
TS. LÊ THỊ MỸ HẠNH
Bộ môn Công nghệ Phần mềm
Khoa Công Nghệ Thông Tin
Đại học Bách khoa – Đại học Đà Nẵng
Khoa Công nghệ Thông tin 1 Khoa Công nghệ Thông tin 2
Lập trình tổng quát Lập trình tổng quát
n Ta đã quen với ý tưởng có một phương thức được n Ví dụ, xét hàm sau:
định nghĩa sao cho khi sử dụng với các lớp khác void swap(int& a, int& b) {
nhau, nó sẽ đáp ứng một cách thích hợp int temp;
¨ Khi nói về đa hình, nếu phương thức "draw" được gọi cho temp = a; a = b; b = temp;
một đối tượng bất kỳ trong cây thừa kế Shape, định nghĩa }
tương ứng sẽ được gọi để đối tượng được vẽ đúng ¨ Hàm trên chỉ cần hoán đổi giá trị chứa trong hai biến int.
¨ Trong trường hợp này, mỗi hình đòi hỏi một định nghĩa ¨ Nếu ta muốn thực hiện việc tương tự cho một kiểu dữ liệu
phương thức hơi khác nhau để đảm bảo sẽ vẽ ra hình khác, chẳng hạn float?
đúng ¨ Có thực sự cần đến cả hai phiên bản không?
n Nhưng nếu định nghĩa hàm cho các kiểu dữ liệu void swap(float& a, float& b) {
khác nhau nhưng không cần phải khác nhau thì float temp;
sao? temp = a; a = b; b = temp;
}
Khoa Công nghệ Thông tin 3 Khoa Công nghệ Thông tin 4
class Stack {
tại một mức độ nào đó vào kiểu dữ liệu int n Thực ra, khái niệm lập trình tổng quát học theo sự sử dụng một
¨ Một số phương thức lấy tham số và trả về kiểu int phương pháp của lớp cơ sở cho các thể hiện của các lớp dẫn
¨ Nếu ta muốn tạo ngăn xếp cho một kiểu dữ liệu khác thì xuất
sao? ¨ Ví dụ, trong cây thừa kế, ta muốn cùng một phương thức draw()
¨ Ta có nên định nghĩa lại hoàn toàn lớp Stack (kết quả sẽ được thực thi, bất kể con trỏ/tham chiếu đang chỉ tới một Point
tạo ra nhiều lớp chẳng hạn IntStack, FloatStack, …) hay hay Circle
không? n Với lập trình tổng quát, ta tìm cách mở rộng sự trừu tượng hoá
ra ngoài địa hạt của các cây thừa kế
Khoa Công nghệ Thông tin 5 Khoa Công nghệ Thông tin 6
Khoa Công nghệ Thông tin 9 Khoa Công nghệ Thông tin 10
Khoa Công nghệ Thông tin 19 Khoa Công nghệ Thông tin 20
Khuôn mẫu lớp Khuôn mẫu lớp
n Khai báo và định nghĩa lớp Stack cho kiểu int
¨ Bắt đầu bằng một ngăn xếp đơn giản
Khoa Công nghệ Thông tin 21 Khoa Công nghệ Thông tin 22
Khoa Công nghệ Thông tin 23 Khoa Công nghệ Thông tin 24
int x = 5, y;
char c = 'a', d;
Stack<int> s;
Stack<char> t;
s.push(x);
t.push(c);
s.pop(y);
t.pop(d);
Khoa Công nghệ Thông tin 25 Khoa Công nghệ Thông tin 26
Các tham số khuôn mẫu khác Các tham số khuôn mẫu khác
Nhớ lại rằng trong cài đặt Stack, ta có một hằng max quy định số
n Ta mới nói đến các lệnh template với n
lượng tối đa các đối tượng mà ngăn xếp có thể chứa
tham số thuộc "kiểu" typename ¨ Như vậy, mỗi thể hiện sẽ có cùng kích thước đối với mọi kiểu
của đối tượng được chứa
n Tuy nhiên, còn có hai "kiểu" tham số khác
n Nếu ta không muốn đòi hỏi mọi Stack đều có kích thước tối đa
¨ Kiểu thực sự (ví dụ: int) như nhau?
n Ta có thể thêm một tham số vào lệnh template chỉ ra một số int
¨ Các template
(giá trị này sẽ được dùng để xác định giá trị cho max)
n Lưu ý: ta khai báo tham số int giống như trong các khai báo khác
Khoa Công nghệ Thông tin 27 Khoa Công nghệ Thông tin 28
Các tham số khuôn mẫu khác Các tham số khuôn mẫu khác
template <typename T, int I>
n Sửa khai báo và định nghĩa trước để sử dụng tham số mới:
Stack<T,I>::Stack() {
this->current = 0;
template <typename T, int I> Sửa các lệnh
}
class Stack { template
public: Khai báo tham số mới Sửa tên template <typename T, int I>
Stack(); lớp dùng Stack<T, I>::~Stack() {}
~Stack(); cho các
void push(const T& i) throw (logic_error); toán tử template <typename T, int I>
void pop(T& i) throw (logic_error); phạm vi void Stack<T, I>::push(const T& i) {
if (this->current < this->max) {
bool isEmpty() const;
this->contents[this->current++] = i;
bool isFull() const; Sử dụng tham số mới để
}
private: xác định giá trị max của else {
static const int max = I; một lớp thuộc một kiểu throw logic_error(“Stack is full.”);
T contents[max]; nào đó }
int current; }
}; ...
Khoa Công nghệ Thông tin 29 Khoa Công nghệ Thông tin 30
Các tham số khuôn mẫu khác Các tham số khuôn mẫu khác
n Giờ ta có thể tạo các thể hiện của các lớp n Các ràng buộc khi sử dụng các kiểu thực
Stack với các kiểu dữ liệu và kích thước sự làm tham số cho lệnh template:
đa dạng ¨ Chỉ có thể dùng các kiểu số nguyên, con trỏ,
hoặc tham chiếu
Stack<int, 5> s; // Creates an instance of a Stack
// class of ints with max = 5 ¨ Không được gán trị cho tham số hoặc lấy địa
Stack<int, 10> t; // Creates an instance of a Stack
// class of ints with max = 10
chỉ của tham số
Stack<char, 5> u; // Creates an instance of a Stack
// class of chars with max = 5
n Khi đó, nó sẽ sử dụng khuôn mẫu Stack để sinh mã n Khởi tạo từ một mảng int [ ] với kích thước cho trước.
một khuôn mẫu, nếu không, làm thế nào để có thể n Toán tử lấy phần tử: [ ].
Ngoại lệ
n Xử lý lỗi
CHƯƠNG 8: n Xử lý lỗi theo kiểu truyền thống
Ngoại lệ
n Ý tưởng về ngoại lệ trong C++
n Giới thiệu về ngoại lệ
n Cú pháp
(Exception) n
n
Ném ngoại lệ
try-catch
n Khớp ngoại lệ
TS. LÊ THỊ MỸ HẠNH
n Chuyển tiếp ngoại lệ
Bộ môn Công nghệ Phần mềm
Khoa Công Nghệ Thông Tin n Chuyện hậu trường
Đại học Bách khoa – Đại học Đà Nẵng n Lớp exception
n Khai báo ngoại lệ
Khoa Công nghệ Thông tin 5 Khoa Công nghệ Thông tin 6
Khoa Công nghệ Thông tin 7 Khoa Công nghệ Thông tin 8
Cơ chế ngoại lệ Cơ chế ngoại lệ
Quy trình gọi hàm và trả về trong trường hợp bình thường
n Quá trình truyền ngoại lệ từ ngữ cảnh n
ngoại lệ (catch the exception) cout << “The first number divided by the second is: “
<< MyDivide(x, y) << endl;
}
Khoa Công nghệ Thông tin 9 Khoa Công nghệ Thông tin 10
Khoa Công nghệ Thông tin 11 Khoa Công nghệ Thông tin 12
Khoa Công nghệ Thông tin 13 Khoa Công nghệ Thông tin 14
throw – Ném ngoại lệ throw – Ném ngoại lệ
n Để ném một ngoại lệ, ta dùng từ khoá throw, kèm theo đối tượng mà ta n Trường hợp cần cung cấp nhiều thông tin hơn cho
định ném hàm gọi, ta tạo một class dành riêng cho các ngoại lệ
throw <object>;
//Throws an exception (replace “<object>” n Ví dụ, ta cần cung cấp cho người dùng 2 số nguyên
//with whatever type of object is to be thrown) ¨ Ta có thể tạo một
n Ta có thể dùng mọi thứ làm ngoại lệ, kể cả giá trị thuộc kiểu có sẵn lớp ngoại lệ:
throw 15; // Throws the int 15 as an exception
throw MyObj(…); // Throws an instance of MyObj as an exception
Khoa Công nghệ Thông tin 17 Khoa Công nghệ Thông tin 18
Khoa Công nghệ Thông tin 19 Khoa Công nghệ Thông tin 20
Exception Matching
Khối try – catch (so khớp ngoại lệ)
n Nhận xét về slide trước n Khi một ngoại lệ được ném từ trong một khối try, hệ thống xử lý
ngoại lệ sẽ kiểm tra các kiểu được liệt kê trong khối catch theo
¨ Tại lệnh catch, có hai thay đổi so với phiên thứ tự liệt kê
bản trước: ¨ Khi tìm thấy kiểu ăn khớp, ngoại lệ xem như là được giải quyết,
không cần tiếp tục tìm kiếm.
n Ta đã sửa kiểu thành tham chiếu đến string thay
¨ Nếu không tìm thấy, mức thực thi hiện hành bị kết thúc và ngoại
vì string. Cách này hiệu quả hơn và tránh được lệ sẽ được chuyển lên mức cao hơn.
slicing nếu ta làm việc với các ngoại lệ là thể hiện n Chú ý: khi tìm các kiểu dữ liệu khớp với ngoại lệ, trình biên dịch
của các lớp dẫn xuất nói chung sẽ không thực hiện đổi kiểu tự động
¨ Nếu một ngoại lệ kiểu float được ném, nó sẽ không khớp với
n Ngoại lệ bắt được được đặt tên ("s") để ta có thể
một khối catch cho ngoại lệ kiểu int
truy nhập nó từ bên trong khối catch ¨ Tuy nhiên, một đối tượng hoặc tham chiếu kiểu dẫn xuất sẽ khớp với một
lệnh catch dành cho kiểu cơ sở
n Nếu một ngoại lệ kiểu Car được ném, nó sẽ khớp với một khối catch cho
ngoại lệ kiểu MotorVehicle
Khoa Công nghệ Thông tin 21 Khoa Công nghệ Thông tin 22
Re-Throwing Exception
(chuyển tiếp ngoại lệ)
Chuyển tiếp ngoại lệ
n Khi một ngoại lệ được chuyển tiếp, mọi lệnh catch
n Trong một số trường hợp, ta có thể muốn chuyển tiếp một ngoại
còn lại trong khối sẽ bị bỏ qua, ngoại lệ được chuyển
lệ mà ta đã bắt - thường là khi ta không có đủ Thông tin để giải
quyết trọn vẹn ngoại lệ đó
lên mức thực thi tiếp theo (như khi ném một ngoại lệ
mới)
¨ Thông thường, ta sẽ chuyển tiếp một ngoại lệ sau khi thực hiện
một số công việc dọn dẹp bên trong một lệnh catch ba chấm n Do ngoại lệ được ném lại mà không bị sửa đổi, ta có
¨ Để chuyển tiếp, ta dùng từ khoá throw, không kèm tham số, từ thể thấy được tầm quan trọng của việc sử dụng tham
bên trong lệnh catch. chiếu
catch (...) { ¨ Nếu một lệnh catch cho lớp cơ sở với tham số không phải
cout << “An exception was thrown.” << endl;
tham chiếu bắt được một ngoại lệ thuộc lớp dẫn xuất, nó sẽ
slice (thay đổi vĩnh viễn) đối tượng để đối tượng đó chỉ
...
còn chứa các thuộc tính của lớp cơ sở
throw; // Re-throw exception
¨ Bằng cách sử dụng tham chiếu, ta có thể đảm bảo rằng
};
ngoại lệ được chuyển tiếp mà không bị thay đổi.
Khoa Công nghệ Thông tin 25 Khoa Công nghệ Thông tin 26
Quản lý bộ nhớ Quản lý bộ nhớ
n Ta đã biết: khi một ngoại lệ được ném, hàm hiện đang n Ví dụ
chạy sẽ kết thúc, điều khiển được trả về cho mức thực thi bool FlightList::contains(Flight *f) const throw(char*){
tiếp theo cao hơn cho đến khi gặp điểm bắt ngoại lệ. stack Flight flight;
Airport *a = new Airport(“SFO”, “San Francisco”);
sẽ được cuốn (unwind) cho đến khi gặp điểm bắt ngoại lệ
...
n Do đó quy trình dọn dẹp tự động xảy ra giống như khi hàm throw “Out of Bounds”;
kết thúc bình thường, đó là: ...
}
¨ Các đối tượng được cấp phát tự động bên trong hàm sẽ ...
được thu hồi try {
if (flightList->contains(flight))
¨ Trong các đối tượng đó, đối với đối tượng bất kỳ mà ...
constructor của nó đã được thực hiện hoàn chỉnh, }
Khi ngoại lệ được ném,
destructor của nó sẽ được gọi catch(char* str) {
destructor cho flight
// Handle the error
¨ Các đối tượng còn lại phải được hủy một cách tường }
được tự động gọi, nhưng
destructor của a thì không.
minh.
Khoa Công nghệ Thông tin 27 Khoa Công nghệ Thông tin 28
Khoa Công nghệ Thông tin 29 Khoa Công nghệ Thông tin 30
Khoa Công nghệ Thông tin 31 Khoa Công nghệ Thông tin 32
Lớp exception Lớp exception
n runtime_error có các lớp dẫn xuất sau: n Ta có thể viết lại hàm MyDivide() để sử dụng các ngoại lệ chuẩn
¨ range_error điều kiện sau (post-condition) bị vi phạm tương ứng như sau:
¨ overflow_error xảy ra tràn số học
double MyDivide(double numerator, double denominator)
¨ bad_alloc không thể cấp phát bộ nhớ {
n logic_error có các lớp dẫn xuất sau: if (denominator == 0.0) {
throw invalid_argument(“The denominator cannot be 0.”);
¨ domain_error điều kiện trước (pre-condition) bị vi
}
phạm else {
¨ invalid_argument tham số không hợp lệ được truyền return numerator / denominator;
cho hàm }
¨ length_error tạo đối tượng lớn hơn độ dài cho }
phép
¨ out_of_range tham số ngoài khoảng (chẳng hạn n Ta sẽ phải sửa lệnh catch cũ để bắt được ngoại lệ kiểu
invalid_argument (thay cho kiểu string trong phiên bản trước)
chỉ số không hợp lệ
Khoa Công nghệ Thông tin 33 Khoa Công nghệ Thông tin 34
Khoa Công nghệ Thông tin 35 Khoa Công nghệ Thông tin 36
Khoa Công nghệ Thông tin 37 Khoa Công nghệ Thông tin 38
Khai báo ngoại lệ Khai báo ngoại lệ
n Ta phải đặc biệt thận trọng khi làm việc với các cây thừa n Nhớ lại rằng một khai báo phương thức về cốt yếu là để tuyên
bố những gì người dùng có thể mong đợi từ phương thức đó
kế và các khai báo ngoại lệ
¨ Ta đã nói rằng đưa ngoại lệ vào khai báo hàm/phương thức hạn
n Giả sử ta có lớp cơ sở B chứa một phương thức ảo foo() chế các loại đối tượng có thể được ném từ hàm/phương thức
¨ Ta khai báo rằng foo() có thể ném hai loại ngoại lệ e1 n Khi có sự có mặt của thừa kế và đa hình, điều trên cũng phải áp
và e2 dụng được
class B { n Do vậy, nếu một lớp dẫn xuất override một phương thức của lớp
void foo() throw (e1, e2); cơ sở, nó không thể bổ sung các kiểu ngoại lệ mới vào phương
... thức
}; ¨ Nếu không, ai đó truy nhập phương thức qua một con trỏ tới lớp
n Giả sử D là lớp dẫn xuất của B,D định nghĩa lại foo() cơ sở có thể gặp phải một ngoại lệ mà họ không mong đợi (do
nó không có trong khai báo của lớp cơ sở)
¨ Cần có những hạn chế nào đối với khả năng ném ngoại
n Tuy nhiên, một lớp dẫn xuất được phép giảm bớt số loại ngoại
lệ của D? lệ có thể ném
Khoa Công nghệ Thông tin 39 Khoa Công nghệ Thông tin 40
Khoa Công nghệ Thông tin 41 Khoa Công nghệ Thông tin 42
Khoa Công nghệ Thông tin 43 Khoa Công nghệ Thông tin 44