Lớpcơsởtrừutượng 5.1. Lớpcơsởtrừutượng Một lớpcơsởtrừutượng là một lớp chỉ được dùng làm cơsở cho các lớp khác. Không hề có đối tượng nào của một lớptrừutượng được tạo ra cả, bởi vì nó chỉ được dùng để định nghĩa một số khái niệm tổng quát, chung cho các lớp khác. Một ví dụ về lớptrừutượng là lớp CON_VAT (con vật), nó sẽ dùng làm cơsở để xây dựng các lớp con vật cụ thể như lớp CON_CHO (con chó), CON_MEO (con mèo), . (xem ví dụ bên dưới) Trong C++ , thuật ngữ “Lớp trừu tượng” đặc biệt áp dụng cho các lớpcó chứa các phương thức ảo thuần tuý. Phương thức ảo thuần tuý là một phương thức ảo mà nội dung của nó không có gì. Cách thức định nghĩa một phương thức ảo thuần tuý như sau: virtual void tên_phương_thức() = 0 ; Ví dụ: class A { public: virtual void nhap() = 0 ; virtual void xuat() = 0 ; void chuong(); } ; Trong ví dụ trên, thì A là lớp cơsởtrừu tượng. Các phương thức nhap và xuat được khai báo là các lớp ảo thuần tuý (bằng cách gán số 0 cho chúng thay cho việc cài đặt các phương thức này). Phương thức chuong() là một phương thức bình thường và sẽ phải có một định nghĩa ở đâu đó cho phương thức này. Không có đối tượng nào của một lớptrừutượng lại có thể được phát sinh. Tuy nhiên các con trỏ và các biến tham chiếu đến các đối tượng của lớptrừutượng thì vẫn hợp lệ. Bất kỳ lớp nào dẫn xuất từ một lớpcớsởtrừutượng phải định nghĩa lại tất cả các phương thức thuần ảo mà nó thừa hưởng, hoặc bằng các phương thức ảo thuần tuý, hoặc bằng những định nghĩa thực sự. Ví dụ: class B : public A { public: virtual void nhap() = 0 ; virtual void xuat() { // Các câu lệnh } } ; Theo ý nghĩa về hướng đối tượng, ta vẫn có thể có một lớptrừutượng mà không nhất thiết phải chứa đựng những phương thức thuần tuý ảo. 343 344 Một cách tổng quát mà nói thì bất kỳ lớp nào mà nó chỉ được dùng làm cơsở cho những lớp khác đều có thể được gọi là “lớp trừu tượng”. Một cách dễ dàng để nhận biết một lớptrừutượng là xem có dùng lớp đó để khai báo các đối tượng hay không? . Nếu không thì đó là lớp cơsởtrừu tượng. 5.2. Ví dụ Giả sử có 20 ô, mỗi ô có thể nuôi một con chó hoặc một con mèo. Yêu cầu xây dựng chương trình gồm các chức năng: + Nhập một con vật mới mua (hoặc chó, hoặc mèo) vào ô rỗng đầu tiên. + Xuất (đem bán) một con vật (hoặc chó, hoặc mèo). + Thống kê các con vật đang nuôi trong 20 ô. Chương trình được tổ chức như sau: + Trước tiên định nghĩa lớp CON_VAT là lớp cơsở ảo. Lớp này có một thuộc tính là tên con vật và một phương thức ảo dùng để xưng tên. + Hai lớp là CON_MEO và CON_CHO được dẫn xuất từ lớp CON_VAT + Cuối cùng là lớp DS_CON_VAT (Danh sách con vật) dùng để quản lý chung cả mèo và chó. Lớp này có 3 thuộc tính là: số con vật cực đại (chính bằng số ô), số con vật đang nuôi và một mảng con trỏ kiểu CON_VAT. Mỗi phần tử mảng sẽ chứa địa chỉ của một đối tượng kiểu CON_MEO hoặc CON_CHO. Lớp sẽ có 3 phương thức để thực hiện 3 chức năng nêu trên của chương trình. Nội dung chương trình như sau: //CT6-04 // Lopcosotruutuong // Lop CON_VAT #include <conio.h> #include <stdio.h> #include <iostream.h> #include <ctype.h> #include <string.h> class CON_VAT { protected: char *ten; public: CON_VAT() { ten = NULL; } CON_VAT(char *ten1) { ten = strdup(ten1); } 345 346 virtual void xung_ten() { } } ; class CON_MEO:public CON_VAT { public: CON_MEO() : CON_VAT() { } CON_MEO(char *ten1) : CON_VAT(ten1) { } virtual void xung_ten() { cout << "\nToi la chu meo: " << ten ; } }; class CON_CHO:public CON_VAT { public: CON_CHO() : CON_VAT() { } CON_CHO(char *ten1) : CON_VAT(ten1) { } virtual void xung_ten() { cout << "\nToi la chu cho: " << ten ; } }; class DS_CON_VAT // Danh sach con vat { private: int max_so_con_vat; int so_con_vat; CON_VAT **h ; public: DS_CON_VAT(int max); ~DS_CON_VAT(); int nhap(CON_VAT *c); CON_VAT* xuat(int n); void thong_ke(); } ; DS_CON_VAT::DS_CON_VAT(int max) { max_so_con_vat = max; so_con_vat = 0; h = new CON_VAT*[max]; for (int i=0; i<max; ++i) h[i] = NULL; } DS_CON_VAT::~DS_CON_VAT() { max_so_con_vat = 0; so_con_vat = 0; delete h; } int DS_CON_VAT::nhap(CON_VAT *c) { if (so_con_vat==max_so_con_vat) return 0; int i=0; while (h[i]!=NULL) ++i; h[i]=c; so_con_vat++ ; return (i+1); } CON_VAT* DS_CON_VAT::xuat(int n) { if (n<1 || n > max_so_con_vat) return NULL ; --n ; if (h[n]) { CON_VAT *c = h[n]; 347 348 h[n]=NULL; so_con_vat-- ; return c; } else return NULL; } void DS_CON_VAT::thong_ke() { if (so_con_vat) { cout << "\n" ; for (int i=0; i<max_so_con_vat; ++i) if (h[i]) h[i]->xung_ten(); } } CON_CHO c1("MUC"); CON_CHO c2("VEN"); CON_CHO c3("LAI"); CON_CHO c4("NHAT"); CON_CHO c5("BONG"); CON_MEO m1("MUOP"); CON_MEO m2("DEN"); CON_MEO m3("TRANG"); CON_MEO m4("TAM THE"); CON_MEO m5("VANG"); void main() { DS_CON_VAT d(20); clrscr(); d.nhap(&c1); int im2 = d.nhap(&m2); d.nhap(&c3); d.nhap(&m1); int ic4 = d.nhap(&c4); d.nhap(&c5); d.nhap(&m5); d.nhap(&c2); d.nhap(&m3); 349 350 d.thong_ke(); d.xuat(im2); d.xuat(ic4); d.thong_ke(); getch(); } Chú ý: Theo quan điểm chung về cách thức sử dụng, thì lớp CON_VAT là lớp cơsởtrừu tượng. Tuy nhiên theo quan điểm của C++ thì lớp này chưa phải là lớp cơsởtrừu tượng, vì trong lớp không có các phương thức thuần tuý ảo. Phương thức xung_ten: virtual void xung_ten() { } là phương thức ảo, được định nghĩa đầy đủ , mặc dù thân của nó là rỗng. Do vậy khai báo: CON_VAT cv(“Con vat chung”); vẫn được C++ chấp nhận. Bây giờ nếu định nghĩa lại phương thức xung_ten như sau: virtual void xung_ten() = 0 ; thì nó trở thành phương thức thuần ảo và C++ sẽ quan niệm lớp CON_VAT là lớptrừu tượng. Khi đó câu lệnh khai báo: CON_VAT cv(“Con vat chung”); sẽ bị C++ bắt lỗi với thông báo: Cannot create instance of abstruct class ‘CON_VAT’ . Lớp cơ sở trừu tượng 5.1. Lớp cơ sở trừu tượng Một lớp cơ sở trừu tượng là một lớp chỉ được dùng làm cơ sở cho các lớp khác. Không hề có đối tượng. sử dụng, thì lớp CON_VAT là lớp cơ sở trừu tượng. Tuy nhiên theo quan điểm của C++ thì lớp này chưa phải là lớp cơ sở trừu tượng, vì trong lớp không có