Lập trình hướng đối tượng với C++ - Chương 6: Khuôn hình

 

1. Khuôn hình hàm 233

1.1 Khuôn hình hàm là gì? 233

1.2 Tạo một khuôn hình hàm 233

1.3 Sử dụng khuôn hình hàm 234

1.3.1 Khuôn hình hàm cho kiểu dữ liệu cơ sở 234

1.3.2 Khuôn hình hàm min cho kiểu char * 235

1.3.3 Khuôn hình hàm min với kiểu dữ liệu lớp 236

1.4 Các tham số kiểu của khuôn hình hàm 237

1.4.1 Các tham số kiểu trong định nghĩa khuôn hình hàm 237

1.5 Giải thuật sản sinh một hàm thể hiện 240

1.6 Khởi tạo các biến có kiểu dữ liệu chuẩn 241

1.7 Các hạn chế của khuôn hình hàm 241

1.8 Các tham số biểu thức của một khuôn hình hàm 242

1.9 Định nghĩa chồng các khuôn hình hàm 244

1.10 Cụ thể hoá các hàm thể hiện 246

1.11 Tổng kết về các khuôn hình hàm 247

2. KHUôN hình lớp 247

2.1 Khuôn hình lớp là gì? 247

2.2 Tạo một khuôn hình lớp 248

2.3 Sử dụng khuôn hình lớp 249

2.4 Ví dụ sử dụng khuôn hình lớp 250

2.5 Các tham số trong khuôn hình lớp 251

2.5.1 Số lượng các tham số kiểu trong một khuôn hình lớp 251

2.5.2 Sản sinh một lớp thể hiện 251

2.6 Các tham số biểu thức trong khuôn hình lớp 252

2.7 Tổng quát về khuôn hình lớp 254

2.8 Cụ thể hoá khuôn hình lớp 255

2.9 Sự giống nhau của các lớp thể hiện 257

2.10 Các lớp thể hiện và các khai báo bạn bè 258

2.10.1 Khai báo các lớp bạn hoặc các hàm bạn thông thường 258

2.10.2 Khai báo bạn bè của một thể hiện của khuôn hình hàm, khuôn hình lớp 258

2.10.3 Khai báo bạn bè của khuôn hình hàm, khuôn hình lớp 259

2.11 Ví dụ về lớp bảng có hai chỉ số 259

3. Tóm tắt 263

3.1 Ghi nhớ 263

4. Bài tập 263

 

 

 

doc33 trang | Chuyên mục: C/C++ | Chia sẻ: dkS00TYs | Lượt xem: 4184 | Lượt tải: 4download
Tóm tắt nội dung Lập trình hướng đối tượng với C++ - Chương 6: Khuôn hình, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
 về khuôn hình lớp
Ta có thể khai báo một số tuỳ ý các tham số biểu thức trong danh sách các tham số của khuôn hình hàm. Các tham số này có thể xuất hiện ở bất kỳ nơi nào trong định nghĩa của khuôn hình lớp. Khi sản sinh một lớp có các tham số biểu thức, các tham số thực tế tương ứng phải là các biểu thức hằng phù hợp với kiểu dữ liệu đã khai báo trong danh sách các tham số hình thức của khuôn hình lớp.
Cụ thể hoá khuôn hình lớp 
Khả năng cụ thể hoá khuôn hình lớp có đôi chút khác biệt so với khuôn hình hàm. 
Khuôn hình lớp định nghĩa họ các lớp trong đó mỗi lớp chứa đồng thời định nghĩa của chính nó và các hàm thành phần. Như vậy, tất cả các hàm thành phần cùng tên sẽ được thực hiện theo cùng một giải thuật. Nếu ta muốn cho một hàm thành phần thích ứng với một tình huống cụ thể cụ thể nào đó, có thể viết một định nghĩa khác cho nó. Sau đây là một ví dụ cải tiến khuôn hình lớp point. ở đây chúng ta đã cụ thể hoá hàm hiển thị display() cho trường hợp kiểu dữ liệu char: 
Ví dụ 6.10
/*templat11.cpp*/
#include 
#include 
//tạo một khuôn hình lớp 
template class point {
 T x, y;
 public:
 point(T abs = 0, T ord = 0) {
 x = abs; y = ord;
 }
 void display();
 };
template void point::display() {
 cout<<"Toa do: "<<x<<" "<<y<<"\n";
 }
//Thêm một hàm display cụ thể hoá trong trường hợp các ký tự
void point::display() {
 cout<<"Toa do: "<<(int)x<<" "<<(int)y<<"\n";
 }
void main() {
 clrscr();
 point ai(3,5); 
 ai.display(); 
 point ac('d','y'); 
 ac.display(); 
 point ad(3.5, 2.3); 
 ad.display();
 getch();
 }
Toa do: 3 5 
Toa do: 100 121 
Toa do: 3.5 2.3
Ta chú ý dòng tiêu đề trong khai báo một thành phần được cụ thể hoá: 
void point::display() 
Khai báo này nhắc chương trình dịch sử dùng hàm này thay thế hàm display() của khuôn hình lớp point (trong trường hợp giá trị thực tế cho tham số kiểu là char). 
Nhận xét
Có thể cụ thể hoá giá trị của tất cả các tham số. Xét khuôn hình lớp sau đây:
template class table {
 T tab[n];
 public:
 table() {cout<<" Tao bang\n"; }
 ...
 }; 
Khi đó, chúng ta có thể viết một định nghĩa cụ thể hoá cho hàm thiết lập cho các bảng 10 phần tử kiểu point như sau: 
table::table(...) {...} 
Có thể cụ thể hoá một hàm thành phần hay một lớp. Trong ví dụ 6.10 chương trình template11.cpp đã cụ thể hoá một hàm thành phần của khuôn hình lớp. Nói chung có thể: cụ thể hoá một hay nhiều hàm thành phần, hoặc không cần thay đổi định nghĩa của bản thân lớp (thực tế cần phải làm như vậy) mà cụ thể hoá bản thân lớp bằng cách đưa thêm định nghĩa. Khả năng thứ hai này có dẫn tới việc phải cụ thể hoá một số hàm thành phần. Chẳng hạn, sau khi định nghĩa khuôn hình template class point, ta có thể định nghĩa một phiên bản cụ thể cho kiểu dữ liệu point thích hợp với thể hiện point. Ta làm như sau: 
class point {
//định nghĩa mới };
Sự giống nhau của các lớp thể hiện 
Xét câu lệnh gán giữa hai đối tượng. Như chúng ta đã biết, chỉ có thể thực hiện được phép gán khi hai đối tượng có cùng kiểu (với trường hợp thừa kế vấn đề đặc biệt hơn một chút nhưng thực chất chỉ là chuyển kiểu ngầm định đối với biểu thức vế phải của lệnh gán). Trước đây, ta biết rằng hai đối tượng có cùng kiểu nếu chúng được khai báo với cùng một tên lớp. Trong trường hợp khuôn hình lớp, nên hiểu sự cùng kiểu ở đây như thế nào? Thực tế, hai lớp thể hiện tương ứng với cùng một kiểu nếu các tham số kiểu tương ứng nhau một cách chính xác và các tham số biểu thức có cùng giá trị. Như vậy (giả thiết rằng chúng ta đã định nghĩa khuôn hình lớp table trong mục 2.6) với các khai báo: 
table t1; 
table t2; 
ta không có quyền viết: 
t2 = t1; //không tương thích giữa hai tham số đầu 
Cũng vậy, với các khai báo: 
table ta; 
table tb; 
cũng không được quyền viết 
ta = tb;//giá trị thực của hai tham số sau khác nhau.
Những qui tắc chặt chẽ trên nhằm làm cho phép gán ngầm định được thực hiện chính xác. Trường hợp có các định nghĩa chồng toán tử gán, có thể không nhất thiết phải tuân theo qui tắc đã nêu trên. Chẳng hạn hoàn toàn có thể thực hiện được phép gán
t2 = t1; 
nếu ta có định nghĩa hai lớp thể hiện table và table và trong lớp thứ hai có định nghĩa chồng phép gán bằng. 
Các lớp thể hiện và các khai báo bạn bè 
Các khuôn hình lớp cũng cho phép khai báo bạn bè. Bên trong một khuôn hình lớp, ta có thể thực hiện ba kiểu khai báo bạn bè như sau. 
Khai báo các lớp bạn hoặc các hàm bạn thông thường
Giả sử A là một lớp thông thường và fct() là một hàm thông thường. Xét khai báo sau đây trong đó khai báo A là lớp bạn và fct() là hàm bạn của tất cả các lớp thể hiện của khuôn hình lớp: 
template class try {
int x; public:
friend class A;
friend int fct(float);
...
}; 
Khai báo bạn bè của một thể hiện của khuôn hình hàm, khuôn hình lớp 
Xét hai ví dụ khai báo sau đây. Giả sử chúng ta có khuôn hình lớp và khuôn hình hàm sau: 
template class point {...}; 
template int fct (T) {...}; 
Ta định nghĩa hai khuôn hình lớp như sau: 
template class try1 {
int x; public:
friend class point;
friend int fct(double);
...
}; 
Khai báo này xác định hai thể hiện rất cụ thể của khuôn hình hàm fct và khuôn hình lớp point là bạn của khuôn hình lớp try1.
template class try2 {
int x; public:
friend class point;
friend int fct(U);
...
};
So với try1, trong try2 người ta không xác định rõ các thể hiện của fct() và point là bạn của một thể hiện của try2. Các thể hiện này sẽ được cụ thể tại thời điểm chúng ta tạo ra một lớp thể hiện của try2. Ví dụ, với lớp thể hiện try2 ta có lớp thể hiện bạn là point và hàm thể hiện bạn là fct
Khai báo bạn bè của khuôn hình hàm, khuôn hình lớp
Xét ví dụ sau đây:
template class try3 {
int x; public:
template friend class point;
template friend int fct(X);
...
}; 
Lần này, tất cả các thể hiện của khuôn hình lớp point đều là bạn của các thể hiện nào của khuôn hình lớp try3. Tương tự như vậy tất cả các thể hiện của khuôn hình hàm fct() đều là bạn của các thể hiện của khuôn hình lớp try3.
Ví dụ về lớp bảng có hai chỉ số 
Trước đây, để định nghĩa một lớp table có hai chỉ số ta phải sử dụng một lớp vector trung gian. ở đây, ta xét một cách làm khác nhờ sử dụng khuôn hình lớp.
Với định nghĩa sau: 
template class table {
 T tab[n];
 public:
 T &operator [](int i) {
 return tab[i];
 }
}; 
ta có thể khai báo: 
table ,3> t2d; 
Trong trường hợp này, thực chất ta có một bảng ba phần tử, mỗi phần tử có kiểu table. Nói cách khác, mỗi phần tử lại là một bảng chứa hai số nguyên. Ký hiệu t2d[1][2] biểu thị một tham chiếu đến thành phần thứ ba của t2d[1], bảng t2d[1] là phần tử thứ hai của bảng hai chiều các số nguyên t2d. Sau đây là một chương trình hoàn chỉnh: 
Ví dụ 6.10
#include 
#include 
template class table {
 T tab[n];
 public:
 table() //hàm thiết lập
 {
 cout<<"Tao bang co "<<n<<"phan tu\n";
 }
 T & operator[] (int i) //hàm toán tử []
 {
 return tab[i];
 }
 };
void main() {
 clrscr();
 table ,3> t2d;
 t2d[1][2] = 15;
 cout <<"t2d [1] [2] ="<<t2d[1][2]<<"\n";
 int i, j;
 for (i = 0; i < 2; i++)
 for(j = 0; j < 3; j++)
 t2d[i][j] = i*3+j;
 for(i =0; i < 2; i++) {
 for(j = 0; j < 3; j++)
 cout <<t2d[i][j]<<" ";
 cout<<"\n";
 }
}
Tao bang co 2 phan tu 
Tao bang co 2 phan tu 
Tao bang co 2 phan tu 
Tao bang co 3 phan tu 
t2d [1] [2] = 15 
0 1 2 3 4 5 6 7 8
Chú ý 
Chương trình ví dụ trên đây còn có một số điểm yếu: chưa quản lý vấn đề tràn chỉ số bản, không khởi tạo giá trị cho các thành phần của bảng. Để giải quyết cần có một hàm thiết lập với một tham số có kiểu int, và một hàm thiết lập có nhiệm vụ chuyển kiểu int thành kiểu T bất kỳ. Các yêu cầu này coi như là bài tập.
Ví dụ 6.11
/*templa13.cpp*/
#include 
#include 
template class table {
 T tab[n];
 int limit;
 public:
 table (int init = 0);
 T & operator[] (int i) {
	if (i limit)
	 cout<<"Tran chi so "<<i<<"\n";
	else return tab[i];
 }
};
template table::table(int init = 0) {
 int i;
 for (i = 0; i < n; i++) tab[i] = init;
	limit = n - 1;
 cout<<"Tao bang kich thuoc "<<n<<" init = "<<init<<"\n";
 }
void main() {
 clrscr();
 table ,2>ti; //khởi tạo ngầm định
 table ,2> td(10); //khởi tao bằng 10
 ti[1][6] = 15;
 ti[8][-1] = 20;
 cout<<ti[1][2]<<"\n"; cout<<td[1][0]<<"\n";
 getch();
 }
Tao bang kich thuoc 3 init = 0 
Tao bang kich thuoc 3 init = 0 
Tao bang kich thuoc 3 init = 0 
Tao bang kich thuoc 3 init = 0 
Tao bang kich thuoc 2 init = 0 
Tao bang kich thuoc 4 init = 0 
Tao bang kich thuoc 4 init = 0 
Tao bang kich thuoc 4 init = 10 
Tao bang kich thuoc 4 init = 10 
Tao bang kich thuoc 2 init = 10 
Tran chi so 6 
Tran chi so 8 
Tran chi so -1 0 10
Chú ý 
Nếu để ý các thông báo tạo các bảng ta thấy rằng ta thu được nhiều hơn hai lần so với dự kiến với các bảng có hai chỉ số. Lời giải thích nằm ở trong câu lệnh gán tab[i] = init của hàm thiết lập table. Khi khai báo mảng các đối tượng T chương trình dịch gọi tới các hàm thiết lập ngầm định với các tham số bằng 0. Để thực hiện lệnh gán tab[i] = init chương trình dịch sẽ thực hiện chuyển đổi từ kiểu số nguyên sang một đối tượng tạm thời kiểu T. Điều này cũng sẽ gọi tới hàm thiết lập table(int). Chẳng hạn trong khai báo của bảng td, ta thấy, chương trình dịch tạo ra hai bảng một chiều các số thực với kích thước là 4 và init =0, đồng thời cũng có hai lần tạo các bảng trung gian với kích thước bằng 4 và init=10.
Tóm tắt
Ghi nhớ
Khuôn hình lớp/hàm là phương tiện mô tả ý nghĩa của một lớp/hàm tổng quát còn lớp/hàm thể hiện là một bản sao của khuôn hình tổng quát với các kiểu dữ liệu cụ thể.
Các khuôn hình lớp/hàm thường được tham số hoá, tuy nhiên vẫn có thể sử dụng các kiểu cụ thể trong các khuôn hình lớp/hàm nếu cần.
Bài tập
Bài 6.1. Viết chương trình khai báo khuôn hình lớp để mô phỏng hoạt động của hàng đợi hoặc ngăn xếp trên các kiểu đối tượng khác nhau.

File đính kèm:

  • docLập trình hướng đối tượng với C++ - Chương 6 Khuôn hình.DOC