Bài giảng môn học Kỹ thuật lập trình

Tóm tắt Bài giảng môn học Kỹ thuật lập trình: ... OR True] 5. True Thứ tự ưu tiên giữa các kiểu toán tử khác nhau Khi một biểu thức có nhiều hơn một kiểu toán tử thì thứ tự ưu tiên phải được thiết lập giữa các kiểu toán tử với nhau. Bảng dưới đây cho biết thứ tự ưu tiên giữa các kiểu toán tử khác nhau. Thứ tự Kiểu toán tử 1 Số học ...rị được sinh ra từ một cấp số cộng. Một vòng lặp for cũng có thể được sử dụng để khởi tạo một mảng các ký tự như sau: Ví dụ 9.3: #include void main() { char alpha[26]; int i, j; for(i = 65, j = 0; i < 91; i++, j++) { alpha[j] = i; printf(“The character now assign...rỏ: int ary[10] = {1,2,3,4,5,6,7,8,9,10}; hoặc int ary[] = {1,2,3,4,5,6,7,8,9,10}; Ví dụ sau đây tạo một mảng một chiều và sắp xếp mảng theo thứ tự tăng dần. Chương trình sử dụng con trỏ và hàm malloc() để gán bộ nhớ. #include #include void main() { int *p, n, i, j, temp; pr...

pdf358 trang | Chia sẻ: havih72 | Lượt xem: 173 | Lượt tải: 1download
Nội dung tài liệu Bài giảng môn học Kỹ thuật lập trình, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
urn true; 
 return false; 
} 
void Set::AddElem (const int elem) 
{ 
 if (Member(elem)) 
 return; 
 if (card < maxCard) 
 elems[card++] = elem; 
 else 
 cout << "Set overflow\n"; 
} 
void Set::RmvElem (const int elem) 
{ 
 for (register i = 0; i < card; ++i) 
 if (elems[i] == elem) { 
 for (; i < card-1; ++i) // dich cac phan tu sang trai 
 elems[i] = elems[i+1]; 
 --card; 
 } 
} 
void Set::Copy (Set &set) 
{ 
342 
 for (register i = 0; i < card; ++i) 
 set.elems[i] = elems[i]; 
 set.card = card; 
} 
Bool Set::Equal (Set &set) 
{ 
 if (card != set.card) 
 return false; 
 for (register i = 0; i < card; ++i) 
 if (!set.Member(elems[i])) 
 return false; 
 return true; 
} 
void Set::Intersect (Set &set, Set &res) 
{ 
 res.card = 0; 
 for (register i = 0; i < card; ++i) 
 if (set.Member(elems[i])) 
 res.elems[res.card++] = elems[i]; 
} 
void Set::Union (Set &set, Set &res) 
{ 
 set.Copy(res); 
 for (register i = 0; i < card; ++i) 
 res.AddElem(elems[i]); 
} 
void Set::Print (void) 
{ 
 cout << "{"; 
 for (int i = 0; i < card-1; ++i) 
343 
 cout << elems[i] << ","; 
 if (card > 0) // khong co dau , sau phan tu cuoi cung 
 cout << elems[card-1]; 
 cout << "}\n"; 
} 
 Hàm main sau đây tạo ra ba tập đối tượng Set và thực thi một vài hàm thành viên 
của nó. 
int main (void) 
{ 
 Set s1, s2, s3; 
 s1.EmptySet(); s2.EmptySet(); s3.EmptySet(); 
 s1.AddElem(10); s1.AddElem(20); s1.AddElem(30); s1.AddElem(40); 
 s2.AddElem(30); s2.AddElem(50); s2.AddElem(10); s2.AddElem(60); 
 cout << "s1 = "; s1.Print(); 
 cout << "s2 = "; s2.Print(); 
 s2.RmvElem(50); 
 cout << "s2 - {50} = "; 
 s2.Print(); 
 if (s1.Member(20)) 
 cout << "20 is in s1\n"; 
 s1.Intersect(s2,s3); 
 cout << "s1 intsec s2 = "; 
 s3.Print(); 
 s1.Union(s2,s3); 
 cout << "s1 union s2 = "; 
 s3.Print(); 
 if (!s1.Equal(s2)) 
 cout s2\n"; 
 return 0; 
} 
 Khi chạy chương trình sẽ cho kết quả như sau: 
344 
s1 = {10,20,30,40} 
s2 = {30,50,10,60} 
s2 - {50} = {30,10,60} 
20 is in s1 
s1 intsec s2 = {10,30} 
s1 union s2 = {30,10,60,20,40} 
s1 s2 
345 
Bài 23. Hàm xây dựng (Constructor) và Hàm hủy (Destructor) 
23.1. Hàm xây dựng (Constructor) 
Hoàn toàn có thể định nghĩa và khởi tạo các đối tượng của một lớp ở cùng một 
thời điểm. Điều này được hỗ trợ bởi các hàm đặc biệt gọi là hàm xây dựng 
(constructor). Một hàm xây dựng luôn có cùng tên với tên lớp của nó. Nó không bao 
giờ có một kiểu trả về rõ ràng. Ví dụ, 
class Point { 
 int xVal, yVal; 
public: 
 Point (int x,int y) {xVal = x; yVal = y;} // constructor 
 void OffsetPt (int,int); 
}; 
là một định nghĩa có thể của lớp Point, trong đó SetPt đã được thay thế bởi một hàm 
xây dựng được định nghĩa nội tuyến. 
Bây giờ chúng ta có thể định nghĩa các đối tượng kiểu Point và khởi tạo chúng 
một lượt. Điều này quả thật là ép buộc đối với những lớp chứa các hàm xây dựng đòi 
hỏi các đối số: 
Point pt1 = Point(10,20); 
Point pt2; // trái luật 
Hàng thứ nhất có thể được đặc tả trong một hình thức ngắn gọn. 
Point pt1(10,20); 
Một lớp có thể có nhiều hơn một hàm xây dựng. Tuy nhiên, để tránh mơ hồ thì 
mỗi hàm xây dựng phải có một dấu hiệu duy nhất. Ví dụ, 
class Point { 
 int xVal, yVal; 
public: 
 Point (int x, int y) { xVal = x; yVal = y; } 
 Point (float, float); // các tọa độ cực 
 Point (void) { xVal = yVal = 0; } // gốc 
 void OffsetPt (int, int); 
346 
}; 
Point::Point (float len, float angle) // các tọa độ cực 
{ 
 xVal = (int) (len * cos(angle)); 
 yVal = (int) (len * sin(angle)); 
} 
có ba hàm xây dựng khác nhau. Một đối tượng có kiểu Point có thể được định nghĩa sử 
dụng bất kỳ hàm nào trong các hàm này: 
Point pt1(10,20); // tọa độ Đê-cát-tơ 
Point pt2(60.3,3.14); // tọa độ cực 
Point pt3; // gốc 
 Lớp Set có thể được cải tiến bằng cách sử dụng một hàm xây dựng thay vì EmptySet: 
class Set { 
public: 
 Set (void) { card = 0; } 
 //... 
}; 
Điều này tạo thuận lợi cho các lập trình viên không cần phải nhớ gọi EmptySet 
nữa. Hàm xây dựng đảm bảo rằng mọi tập hợp là rỗng vào lúc ban đầu. 
Lớp Set có thể được cải tiến hơn nữa bằng cách cho phép người dùng điều khiển 
kích thước tối đa của tập hợp. Để làm điều này chúng ta định nghĩa elems như một 
con trỏ số nguyên hơn là mảng số nguyên. Hàm xây dựng sau đó có thể được cung cấp 
một đối số đặc tả kích thước tối đa mong muốn. 
Nghĩa là maxCard sẽ không còn là hằng được dùng cho tất cả các đối tượng Set 
nữa mà chính nó trở thành một thành viên dữ liệu: 
class Set { 
public: 
 Set (const int size); 
 //... 
private: 
347 
 int *elems; // cac phan tu tap hop 
 int maxCard; // so phan tu toi da 
 int card; // so phan tu 
}; 
 Hàm xây dựng dễ dàng cấp phát một mảng động với kích thước mong muốn và khởi 
tạo giá trị phù hợp cho maxCard và card: 
Set::Set (const int size) 
{ 
 elems = new int[size]; 
 maxCard = size; 
 card = 0; 
} 
Bây giờ có thể định nghĩa các tập hợp có các kích thước tối đa khác nhau: 
Set ages(10), heights(20), primes(100); 
Chúng ta cần lưu ý rằng một hàm xây dựng của đối tượng được ứng dụng khi đối 
tượng được tạo ra. Điều này phụ thuộc vào phạm vi của đối tượng. Ví dụ, một đối 
tượng toàn cục được tạo ra ngay khi sự thực thi chương trình bắt đầu; một đối tượng tự 
động được tạo ra khi phạm vi của nó được đăng ký; và một đối tượng động được tạo ra 
khi toán tử new được áp dụng tới nó. 
23.2. Hàm hủy (Destructor) 
Như là một hàm xây dựng được dùng để khởi tạo một đối tượng khi nó được tạo 
ra, một hàm hủy được dùng để dọn dẹp một đối tượng ngay trước khi nó được thu hồi. 
Hàm hủy luôn luôn có cùng tên với chính tên lớp của nó nhưng được đi đầu với ký tự 
~. Không giống các hàm xây dựng, mỗi lớp chỉ có nhiều nhất một hàm hủy. Hàm hủy 
không nhận bất kỳ đối số nào và không có một kiểu trả về rõ ràng. 
Thông thường các hàm hủy thường hữu ích và cần thiết cho các lớp chứa dữ liệu 
thành viên con trỏ. Các dữ liệu thành viên con trỏ trỏ tới các khối bộ nhớ được cấp 
phát từ lớp. Trong các trường hợp như thế thì việc giải phóng bộ nhớ đã được cấp 
phát cho các con trỏ thành viên là cực kỳ quan trọng trước khi đối tượng được thu hồi. 
Hàm hủy có thể làm công việc như thế. 
348 
Ví dụ, phiên bản sửa lại của lớp Set sử dụng một mảng được cấp phát động cho 
các thành viên elems. Vùng nhớ này nên được giải phóng bởi một hàm hủy: 
class Set { 
 public: 
 Set (const int size); 
 ~Set (void) {delete elems;} // destructor 
 //... 
 private: 
 int *elems; // cac phan tu tap hop 
 int maxCard; // so phan tu toi da 
 int card; // so phan tu cua tap hop 
}; 
 Bây giờ hãy xem xét cái gì xảy ra khi một Set được định nghĩa và sử dụng trong 
hàm: 
void Foo (void) 
{ 
 Set s(10); 
 //... 
} 
Khi hàm Foo được gọi, hàm xây dựng cho s được triệu tập, cấp phát lưu trữ cho 
s.elems và khởi tạo các thành viên dữ liệu của nó. Kế tiếp, phần còn lại của thân hàm 
Foo được thực thi. Cuối cùng, trước khi Foo trả về, hàm hủy cho cho s được triệu tập, 
xóa đi vùng lưu trữ bị chiếm bởi s.elems. Kể từ đây cho đến khi cấp phát lưu trữ được 
kể đến thì s ứng xử giống như là biến tự động của một kiểu có sẳn được tạo ra khi 
phạm vi của nó được biết đến và được hủy đi khi phạm vi của nó được rời khỏi. 
Nói chung, hàm xây dựng của đối tượng được áp dụng trước khi đối tượng 
được thu hồi. Điều này phụ thuộc vào phạm vi của đối tượng. Ví dụ, một đối tượng 
toàn cục được thu hồi khi sự thực hiện của chương trình hoàn tất; một đối tượng tự 
động được thu hồi khi toán tử delete được áp dụng tới nó. 
349 
Bài 24. Kỹ thuật lập trình Thừa kế 
Trong thực tế hầu hết các lớp có thể kế thừa từ các lớp có trước mà không cần 
định nghĩa lại mới hoàn toàn. Ví dụ xem xét một lớp được đặt tên là RecFile đại diện 
cho một tập tin gồm nhiều mẫu tin và một lớp khác được đặt tên là SortedRecFile đại 
diện cho một tập tin gồm nhiều mẫu tin được sắp xếp. Hai lớp này có thể có nhiều 
điểm chung. Ví dụ, chúng có thể có các thành viên hàm giống nhau như là Insert, 
Delete, và Find, cũng như là thành viên dữ liệu giống nhau. SortedRecFile là một 
phiên bản đặc biệt của RecFile với thuộc tính các mẫu tin của nó được tổ chức theo thứ 
tự được thêm vào. Vì thế hầu hết các hàm thành viên trong cả hai lớp là giống nhau 
trong khi một vài hàm mà phụ thuộc vào yếu tố tập tin được sắp xếp thì có thể khác 
nhau. Ví dụ, hàm Find có thể là khác trong lớp SortedRecFile bởi vì nó có thể nhờ vào 
yếu tố thuận lợi là tập tin được sắp để thực hiện tìm kiếm nhị phân thay vì tìm tuyến 
tính như hàm Find của lớp RecFile. 
Với các thuộc tính được chia sẻ của hai lớp này thì việc định nghĩa chúng một 
cách độc lập là rất dài dòng. Rõ ràng điều này dẫn tới việc phải sao chép lại mã đáng 
kể. Mã không chỉ mất thời gian lâu hơn để viết nó mà còn khó có thể được bảo trì hơn: 
một thay đổi tới bất kỳ thuộc tính chia sẻ nào có thể phải được sửa đổi tới cả hai lớp. 
Lập trình hướng đối tượng cung cấp một kỹ thuật thuận lợi gọi là thừa kế để giải 
quyết vấn đề này. Với thừa kế thì một lớp có thể thừa kế những thuộc tính của một lớp 
đã có trước. Chúng ta có thể sử dụng thừa kế để định nghĩa những thay đổi của một 
lớp mà không cần định nghĩa lại lớp mới từ đầu. Các thuộc tính chia sẻ chỉ được định 
nghĩa một lần và được sử dụng lại khi cần. 
Trong C++ thừa kế được hỗ trợ bởi các lớp dẫn xuất (derived class). Lớp dẫn 
xuất thì giống như lớp gốc ngoại trừ định nghĩa của nó dựa trên một hay nhiều lớp có 
sẵn được gọi là lớp cơ sở (base class). Lớp dẫn xuất có thể chia sẻ những thuộc tính đã 
chọn (các thành viên hàm hay các thành viên dữ liệu) của các lớp cơ sở của nó nhưng 
không làm chuyển đổi định nghĩa của bất kỳ lớp cơ sở nào. Lớp dẫn xuất chính nó có 
thể là lớp cơ sở của một lớp dẫn xuất khác. Quan hệ thừa kế giữa các lớp của một 
chương trình được gọi là quan hệ cấp bậc lớp (class hierarchy). 
350 
Lớp dẫn xuất cũng được gọi là lớp con (subclass) bởi vì nó trở thành cấp thấp 
hơn của lớp cơ sở trong quan hệ cấp bậc. Tương tự một lớp cơ sở có thể được gọi là 
lớp cha (superclass) bởi vì từ nó có nhiều lớp khác có thể được dẫn xuất. 
24.1. Ví dụ minh họa 
Chúng ta sẽ định nghĩa hai lớp nhằm mục đích minh họa một số khái niệm lập 
trình trong các phần sau của chương này. Hai lớp được định nghĩa trong Danh sách 
22.1 và hỗ trợ việc tạo ra một thư mục các đối tác cá nhân. 
1 #include 
2 #include 
3 class Contact { 
4 public: 
5 Contact(const char *name, const char *address, const char *tel); 
6 ~Contact (void); 
7 const char* Name (void) const {return name;} 
8 const char* Address(void) const {return address;} 
9 const char* Tel(void) const {return tel;} 
10 friend ostream& operator << (ostream&, Contact&); 
11 private: 
12 char *name; // ten doi tac 
13 char *address; // dia chi doi tac 
14 char *tel; // so dien thoai 
15 }; 
16//------------------------------------------------------------------- 
17 class ContactDir { 
 18 public: 
19 ContactDir(const int maxSize); 
10 ~ContactDir(void); 
 21 void Insert(const Contact&); 
 22 void Delete(const char *name); 
 23 Contact* Find(const char *name); 
 24 friend ostream& operator <<(ostream&, ContactDir&); 
351 
 25 private: 
 26 int Lookup(const char *name); 
 27 Contact **contacts; // danh sach cac doi tac 
 int dirSize; // kich thuoc thu muc hien tai 
 28 int maxSize; // kich thuoc thu muc toi da 
29 }; 
 Chú giải 
3 Lớp Contact lưu giữ các chi tiết của một đối tác (nghĩa là, tên, địa chỉ, và số điện 
thoại). 
18 Lớp ContactDir cho phép chúng ta thêm, xóa, và tìm kiếm một danh sách các đối 
tác. 
22 Hàm Insert xen một đối tác mới vào thư mục. Điều này sẽ viết chồng lên một đối 
tác tồn tại (nếu có) với tên giống nhau. 
23 Hàm Delete xóa một đối tác (nếu có) mà tên của đối tác trùng với tên đã cho. 
24 Hàm Find trả về một con trỏ tới một đối tác (nếu có) mà tên của đối tác khớp với 
tên đã cho. 
27 Hàm Lookup trả về chỉ số vị trí của một đối tác mà tên của đối tác khớp với tên đã 
cho. Nếu không tồn tại thì sau đó hàm Lookup trả về chỉ số của vị trí mà tại đó mà một 
đầu vào như thế sẽ được thêm vào. Hàm Lookup được định nghĩa như là riêng 
(private) bởi vì nó là một hàm phụ được sử dụng bởi các hàm Insert, Delete, và Find. 
Cài đặt của hàm thành viên và hàm bạn như sau: 
Contact::Contact (const char *name, 
 const char *address, const char *tel) 
{ 
 Contact ::name = new char[strlen(name) + 1]; 
 Contact::address = new char[strlen(address) + 1]; 
 Contact :: tel = new char[strlen(tel) + 1]; 
 strcpy(Contact::name, name); 
 strcpy(Contact::address, address); 
 strcpy(Contact: :tel, tel); 
} 
352 
Contact ::~Contact (void) 
{ 
 delete name; 
 delete address; 
 delete tel ; 
} 
ostream& operator << (ostream &os, Contact &c) 
{ 
 os << "(" << c.name << " , " 
 << c.address << " , " << c.tel << ")"; 
 return os; 
} 
ContactDir::ContactDir (const int max) 
{ 
 typedef Contact *ContactPtr; 
 dirSize = 0; 
 maxSize = max; 
 contacts = new ContactPtr[maxSize]; 
}; 
ContactDir::~ContactDir (void) 
{ 
 for (register i = 0; i < dirSize; ++i) 
 delete contacts[i]; 
 delete [] contacts; 
} 
void ContactDir::Insert (const Contact& c) 
{ 
 if (dirSize < maxSize) { 
 int idx = Lookup(c.Name()); 
 if (idx > 0 && 
 strcmp(c.Name(), contacts[idx]->Name()) == 0) { 
353 
 delete contacts[idx]; 
 } else { 
 for (register i = dirSize; i > idx; --i) // dich phai 
 contacts[i] = contacts[i-1]; 
 ++dirSize; 
 } 
 contacts[idx] = new Contact(c.Name(), c.Address(), c.Tel()); 
 } 
} 
void ContactDir::Delete (const char *name) 
{ 
 int idx = Lookup(name); 
 if (idx < dirSize) { 
 delete contacts[idx]; 
 --dirSize; 
 for (register i = idx; i < dirSize; ++i) // dich trai 
 contacts[i] = contacts[i+1]; 
 } 
} 
Contact *ContactDir: :Find (const char *name) 
{ 
 int idx = Lookup(name); 
 return (idx < dirSize && 
 strcmp(contacts[idx]->Name(), name) == 0) 
 ? contacts[idx] 
 : 0; 
} 
int ContactDir::Lookup (const char *name) 
{ 
 for (register i = 0; i < dirSize; ++i) 
 if (strcmp(contacts[i]->Name(), name) == 0) 
354 
 return i; 
 return dirSize; 
} 
ostream &operator << (ostream &os, ContactDir &c) 
{ 
 for (register i = 0; i < c.dirSize; ++i) 
 os << *(c.contacts[i]) << '\n' ; 
 return os; 
} 
 Hàm main sau thực thi lớp ContactDir bằng cách tạo ra một thư mục nhỏ và gọi 
các hàm thành viên: 
int main (void) 
{ 
 ContactDir dir(10); 
 dir.Insert(Contact("Mary", "11 South Rd", "282 1324")); 
 dir.Insert(Contact("Peter", "9 Port Rd", "678 9862")); 
 dir.Insert(Contact("Jane", "321 Yara Ln", "982 6252")); 
 dir.Insert(Contact("Jack", "42 Wayne St", "663 2989")); 
 dir.Insert(Contact("Fred", "2 High St", "458 2324")); 
 cout << dir; 
 cout << "Find Jane: " << *dir.Find("Jane") << '\n' ; 
 dir.Delete("Jack"); 
 cout << "Deleted Jack\n"; 
 cout << dir; 
 return 0; 
}; 
Khi chạy nó sẽ cho kết quả sau: 
(Mary , 11 South Rd , 282 1324) 
(Peter , 9 Port Rd , 678 9862) 
(Jane , 321 Yara Ln , 982 6252) 
(Jack , 42 Wayne St , 663 2989) 
355 
(Fred , 2 High St , 458 2324) 
Find Jane: (Jane , 321 Yara Ln , 982 6252) 
Deleted Jack 
(Mary , 11 South Rd , 282 1324) 
(Peter , 9 Port Rd , 678 9862) 
(Jane , 321 Yara Ln , 982 6252) 
(Fred , 2 High St , 458 2324) 
 24.2. Lớp dẫn xuất đơn giản 
Chúng ta muốn định nghĩa một lớp gọi là SmartDir ứng xử giống như là lớp 
ContactDir và theo dõi tên của đối tác mới vừa được tìm kiếm gần nhất. Lớp SmartDir 
được định nghĩa tốt nhất như là một dẫn xuất của lớp ContactDir như được minh họa 
bởi Danh sách 22.2. 
1 class SmartDir : public ContactDir { 
2 public: 
3 SmartDir(const int max) : ContactDir(max) {recent = 0;} 
4 Contact* Recent (void); 
5 Contact* Find (const char *name); 
6 private: 
7 char * recent; // ten duoc tim gan nhat 
8 }; 
Chú giải 
1 Phần đầu của lớp dẫn xuất chèn vào các lớp cơ sở mà nó thừa kế. Một dấu hai chấm 
(:) phân biệt giữa hai phần. Ở đây, lớp ContactDir được đặc tả là lớp cơ sở mà lớp 
SmartDir được dẫn xuất. Từ khóa public phía trước lớp ContactDir chỉ định rằng lớp 
ContactDir được sử dụng như một lớp cơ sở chung. 
3 Lớp SmartDir có hàm xây dựng của nó, hàm xây dựng này triệu gọi hàm xây dựng 
của lớp cơ sở trong danh sách khởi tạo thành viên của nó. 
4 Hàm Recent trả về một con trỏ tới đối tác được tìm kiếm sau cùng (hoặc 0 nếu không 
có). 
5 Hàm Find được định nghĩa lại sao cho nó có thể ghi nhận đầu vào được tìm kiếm sau 
cùng. 
356 
7 Con trỏ recent được đặt tới tên của đầu vào đã được tìm sau cùng. 
Các hàm thành viên được định nghĩa như sau: 
Contact* SmartDir::Recent (void) 
{ 
 return recent == 0 ? 0 : ContactDir::Find(recent); 
} 
Contact* SmartDir::Find (const char *name) 
{ 
 Contact *c = ContactDir::Find(name); 
 if (c != 0) 
 recent = (char*) c->Name(); 
 return c; 
} 
 Bởi vì lớp ContactDir là một lớp cơ sở chung của lớp SmartDir nên tất cả thành viên 
chung của lớp ContactDir trở thành các thành viên chung của lớp martDir. Điều này 
nghĩa là chúng ta có thể triệu gọi một hàm thành viên như là Insert trên một đối tượng 
SmartDir và đây là một lời gọi tới ContactDir::Insert. Tương tự, tất cả các thành viên 
riêng của lớp ContactDir trở thành các thành viên riêng của lớp SmartDir. 
Phù hợp với các nguyên lý ẩn thông tin, các thành viên riêng của lớp ContactDir 
sẽ không thể được truy xuất bởi SmartDir. Vì thế, lớp SmartDir sẽ không thể truy xuất 
tới bất kỳ thành viên dữ liệu nào của lớp ContactDir cũng như là hàm thành viên riêng 
Lookup. 
Lớp SmartDir định nghĩa lại hàm thành viên Find. Điều này không nên nhầm 
lẫn với tái định nghĩa. Có hai định nghĩa phân biệt của hàm này: ContactDir::Find và 
SmartDir::Find (cả hai định nghĩa có cùng dấu hiệu dẫu cho chúng có thể có các dấu 
hiệu khác nhau nếu được yêu cầu). Triệu gọi hàm Find trên đối tượng SmartDir thứ hai 
sẽ được gọi. Như được minh họa bởi định nghĩa của hàm Find trong lớp SmartDir,hàm 
thứ nhất có thể vẫn còn được triệu gọi bằng cách sử dụng tên đầy đủ của nó. 
 Đoạn mã sau minh họa lớp SmartDir cư xử như là lớp ContactDir nhưng cũng 
theo dõi đầu vào được tìm kiếm được gần nhất: 
SmartDir dir(10); 
357 
dir.Insert(Contact("Mary", "11 South Rd", "282 1324")); 
dir.Insert(Contact("Peter", "9 Port Rd", "678 9862")); 
dir.Insert(Contact("Jane", "321 Yara Ln", "982 6252")); 
dir.Insert(Contact("Fred", "2 High St", "458 2324")); 
dir.Find("Jane"); 
dir.Find("Peter"); 
cout << "Recent: " << *dir.Recent() << '\n'; 
Điều này sẽ cho ra kết quả sau: 
Recent: (Peter , 9 Port Rd , 678 9862) 
Một đối tượng kiểu SmartDir chứa đựng tất cả dữ liệu thành viên của 
ContactDir cũng như là bất kỳ dữ liệu thành viên thêm vào được giới thiệu bởi 
SmartDir. Hình 22.1 minh họa việc tạo ra một đối tượng ContactDir và một đối tượng 
SmartDir. 
Hình 22.1. Các đối tượng lớp cơ sở và lớp dẫn xuất. 
 24.3. Ký hiệu thứ bậc lớp 
Thứ bậc lớp thường được minh họa bằng cách sử dụng ký hiệu đồ họa đơn giản. 
Hình 9.2 minh họa ký hiệu của ngôn ngữ UML mà chúng ta sẽ đang sử dụng trong 
giáo trình này. Mỗi lớp được biểu diễn bằng một hộp được gán nhãn là tên lớp. Thừa 
kế giữa hai lớp được minh họa bằng một mũi tên có hướng vẽ từ lớp dẫn xuất đến lớp 
cơ sở. Một đường thẳng với hình kim cương ở một đầu miêu tả composition (tạm dịch 
là quan hệ bộ phận, nghĩa là một đối tượng của lớp được bao gồm một hay nhiều đối 
tượng của lớp khác). Số đối tượng chứa bởi đối tượng khác được miêu tả bởi một nhãn 
(ví dụ, n). 
358 
Hình trên được thông dịch như sau. Contact, ContactDir, và SmartDir là các lớp. 
Lớp ContactDir gồm có không hay nhiều đối tượng Contact. Lớp SmartDir được dẫn 
xuất từ lớp ContactDir. 

File đính kèm:

  • pdfbai_giang_mon_hoc_ky_thuat_lap_trinh.pdf