Tải bản đầy đủ (.ppt) (38 trang)

Bài giảng lập trình hướng đối tượng chương 5 phương thức ảo và tính đa hình

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (183.46 KB, 38 trang )

Chương 5
Phương thức ảo và tính đa hình
5.1 Bài toán quản lý một danh sách các đối
tượng khác kiểu
5.2 Vùng chọn kiểu
5.3 Phương thức ảo
5.4 Phương thức thiết lập ảo
5.5 Phương thức ảo thuần tuý

1


5.1 Bài toán quản lý một danh sách các đối tượng khác
kiểu
- Giả sử ta cần quản lý một danh sách các đối tượng có kiểu có thể
khác nhau, ta cần giải quyết hai vấn đề: Cách lưu trữ và thao tác
xử lý.
- Xét trường hợp cụ thể, các đối tượng có thể là người, sinh viên
hoặc công nhân.
- Về lưu trữ: Ta có thể dùng union, trong trường hợp này mỗi đối
tượng phải có kích thước chứa được đối tượng có kích thước lớn
nhất. Điều này gây lãng phí không gian lưu trữ. Một cách thay
thế là lưu trữ đối tượng bằng đúng kích thước của nó và dùng
một danh sách (mảng, dslk,...) các con trỏ để quản lý các đối
tượng.
- Về thao tác, phải thoả yêu cầu đa hình: Thao tác có hoạt động
khác nhau ứng với các loại đối tượng khác nhau. Có hai cách
giải quyết là vùng chọn kiểu và phương thức ảo.
2



5.2 Dùng vùng chọn kiểu
 Về lưu trữ: Ta sẽ dùng một mảng các con trỏ đến lớp cơ sở để
có thể trỏ đến các đối tượng thuộc lớp con.
 Xét lớp Người và các lớp kế thừa sinh viên và công nhân.
Thao tác ta quan tâm là xuat. Ta cần bảo đảm thao tác xuất áp
dụng cho lớp sinh viên và lớp công nhân khác nhau.

3


Dùng vùng chọn kiểu
class Nguoi
{
protected:
char *HoTen;
int NamSinh;
public:
Nguoi(char *ht, int ns):NamSinh(ns) {HoTen = strdup(ht);}
~Nguoi() {delete [] HoTen;}
void An() const { cout << HoTen << " an 3 chen com";}
void Ngu() const { cout << HoTen << " ngu ngay 8 tieng";}
void Xuat() const { cout << "Nguoi, ho ten: " << HoTen << " sinh " <<
NamSinh; }
};

4


Dùng vùng chọn kiểu
class SinhVien : public Nguoi

{
protected:
char *MaSo;
public:
SinhVien(char *n, char *ms, int ns) : Nguoi(n,ns) { MaSo =
strdup(ms);}
~SinhVien() {delete [] MaSo;}
void Xuat() const { cout << "Sinh vien " << HoTen << ", ma so " <<
MaSo;}
};
class NuSinh : public SinhVien
{
public:
NuSinh(char *ht, char *ms, int ns) : SinhVien(ht,ms,ns) {}
void An() const { cout << HoTen << " ma so " << MaSo << " an 2 to
pho";}
};
5


Dùng vùng chọn kiểu
class CongNhan : public Nguoi
{
protected:
double MucLuong;
public:
CongNhan(char *n, double ml, int ns) : Nguoi(n,ns), MucLuong(ml)
{}
void Xuat() const { cout << "Cong nhan, ten " << HoTen << " muc
luong: " << MucLuong;}

};
void XuatDs(int n, Nguoi *an[])
{
for (int i = 0; i < n; i++)
{
an[i]->Xuat();
cout << "\n";
}
}
6


Dùng vùng chọn kiểu
const int N = 4;
void main()
{
Nguoi *a[N];
a[0] = new SinhVien("Vien Van Sinh", ”200001234", 1982);
a[1] = new NuSinh("Le Thi Ha Dong", ”200001235", 1984);
a[2] = new CongNhan("Tran Nhan Cong", 1000000, 1984);
a[3] = new Nguoi("Nguyen Thanh Nhan", 1960);
XuatDs(4,a);
}

7


Dùng vùng chọn kiểu
 Xuất liệu cho đoạn chương trình trên như sau:
Nguoi, ho ten: Vien Van Sinh sinh 1982

Nguoi, ho ten: Le Thi Ha Dong sinh 1984
Nguoi, ho ten: Tran Nhan Cong sinh 1984
Nguoi, ho ten: Nguyen Thanh Nhan sinh 1960

 Tất cả mọi đối tượng đều được quan điểm như người vì thao
tác được thực hiện thông qua con trỏ đến lớp Người.
 Để bảo đảm xuất liệu tương ứng với đối tượng, phải có cách
nhận diện đối tượng, ta thêm một vùng dữ liệu vào lớp cơ sở
để nhận diện, vùng này có giá trị phụ thuộc vào loại của đối
tượng và được gọi là vùng chọn kiểu.
 Các đối tượng thuộc lớp người có cùng giá trị cho vùng chọn
kiểu, các đối tượng thuộc lớp sinh viên có giá trị của vùng
chọn kiểu khác của lớp người.
8


Dùng vùng chọn kiểu
class Nguoi
{
public:
enum LOAI {NGUOI, SV, CN};
protected:
char *HoTen;
int NamSinh;
public:
LOAI pl;
Nguoi(char *ht, int ns):NamSinh(ns), pl(NGUOI) {HoTen =
strdup(ht);}
~Nguoi() {delete [] HoTen;}
void An() const { cout << HoTen << " an 3 chen com";}

void Ngu() const { cout << HoTen << " ngu ngay 8 tieng";}
void Xuat() const { cout << "Nguoi, ho ten: " << HoTen << " sinh " <<
NamSinh; }
};
9


Dùng vùng chọn kiểu
class SinhVien : public Nguoi
{
protected:
char *MaSo;
public:
SinhVien(char *n, char *ms, int ns) : Nguoi(n,ns) { MaSo =
strdup(ms); pl = SV;}
~SinhVien() {delete [] MaSo;}
void Xuat() const { cout << "Sinh vien " << HoTen << ", ma so " <<
MaSo;}
};
class NuSinh : public SinhVien
{
public:
NuSinh(char *ht, char *ms, int ns) : SinhVien(ht,ms,ns) {}
void An() const { cout << HoTen << " ma so " << MaSo << " an 2 to
pho";}
};
10


Dùng vùng chọn kiểu

class CongNhan : public Nguoi
{
protected:
double MucLuong;
public:
CongNhan(char *n, double ml, int ns) : Nguoi(n,ns), MucLuong(ml)
{ pl = CN;}
void Xuat() const { cout << "Cong nhan, ten " << HoTen << " muc
luong: " << MucLuong;}
};

 Khi thao tác ta phải căn cứ vào giá trò của vùng
chọn kiểu để “ép kiểu” phù hợp.

11


Dùng vùng chọn kiểu
void XuatDs(int n, Nguoi *an[])
{
for (int i = 0; i < n; i++)
{
switch(an[i]->pl)
{
case Nguoi::SV:
((SinhVien *)an[i])->Xuat();
break;
case Nguoi::CN:
((CongNhan *)an[i])->Xuat();
break;

default:
an[i]->Xuat();
break;
}
cout << "\n";
}
}
12


Dùng vùng chọn kiểu
const int N = 4;
void main()
{
Nguoi *a[N];
a[0] = new SinhVien("Vien Van Sinh", "200001234", 1982);
a[1] = new NuSinh("Le Thi Ha Dong", "200001235", 1984);
a[2] = new CongNhan("Tran Nhan Cong", 1000000, 1984);
a[3] = new Nguoi("Nguyen Thanh Nhan", 1960);
XuatDs(4,a);
}

 Xuất liệu của đoạn chương trình trên sẽ là:
Sinh vien Vien Van Sinh, ma so 200001234
Sinh vien Le Thi Ha Dong, ma so 200001235
Cong nhan, ten Tran Nhan Cong muc luong: 1000000
Nguoi, ho ten: Nguyen Thanh Nhan sinh 1960

13



Dùng vùng chọn kiểu

14


Dùng vùng chọn kiểu
 Cách tiếp cận trên giải quyết được vấn để: Lưu trữ được các
đối tượng khác kiểu nhau và thao tác khác nhau tương ứng với
đối tượng. Tuy nhiên nó có các nhược điểm sau:
– Dài dòng với nhiều switch, case.
– Dễ sai sót, khó sửa vì trình biên dịch bị cơ chế ép kiểu che
mắt.
– Khó nâng cấp ví dụ thêm một loại đối tượng mới, đặc biệt
khi chương trình lớn.
 Các nhược điểm trên có thể được khắc phục nhờ phương thức
ảo.

15


5.3 Phương thức ảo
 Con trỏ thuộc lớp cơ sở có thể trỏ đến lớp con:

Nguoi* pn = new SinhVien(“Le Vien Sinh”, 200001234, 1982);

 Ta mong muốn thông qua con trỏ thuộc lớp cơ sở có thể truy
xuất hàm thành phần được định nghĩa lại ở lớp con:
pn->Xuat(); // Mong muon: goi Xuat cua lop sinh
// vien, thuc te: goi Xuat cua lop

// Nguoi

 Phương thức ảo cho phép giải quyết vấn đề. Ta qui định một
hàm thành phần là phương thức ảo bằng cách thêm từ khoá
virtual vào trước khai báo hàm.
 Trong ví dụ trên, ta thêm từ khoá virtual vào trước khai báo
của hàm xuat.

16


Phương thức ảo
class Nguoi
{
protected:
char *HoTen;
int NamSinh;
public:
Nguoi(char *ht, int ns):NamSinh(ns) {HoTen = strdup(ht);}
~Nguoi() {delete [] HoTen;}
void An() const { cout << HoTen << " an 3 chen com";}
void Ngu() const { cout << HoTen << " ngu ngay 8 tieng";}
virtual void Xuat() const { cout << "Nguoi, ho ten: " << HoTen << "
sinh " << NamSinh; }
};

17


Phương thức ảo

class SinhVien : public Nguoi
{
protected:
char *MaSo;
public:
SinhVien(char *n, char *ms, int ns) : Nguoi(n,ns) { MaSo =
strdup(ms);}
~SinhVien() {delete [] MaSo;}
void Xuat() const { cout << "Sinh vien " << HoTen << ", ma so " <<
MaSo;}
};
class NuSinh : public SinhVien
{
public:
NuSinh(char *ht, char *ms, int ns) : SinhVien(ht,ms,ns) {}
void An() const { cout << HoTen << " ma so " << MaSo << " an 2 to
pho";}
};
18


Phương thức ảo
class CongNhan : public Nguoi
{
protected:
double MucLuong;
public:
CongNhan(char *n, double ml, int ns) : Nguoi(n,ns), MucLuong(ml) { }
void Xuat() const { cout << "Cong nhan, ten " << HoTen << " muc
luong: " << MucLuong;}

};
void XuatDs(int n, Nguoi *an[])
{
for (int i = 0; i < n; i++)
{
an[i]->Xuat();
cout << "\n";
}
}
19


Phương thức ảo
const int N = 4;
void main()
{
Nguoi *a[N];
a[0] = new SinhVien("Vien Van Sinh", "200001234", 1982);
a[1] = new NuSinh("Le Thi Ha Dong", "200001235", 1984);
a[2] = new CongNhan("Tran Nhan Cong", 1000000, 1984);
a[3] = new Nguoi("Nguyen Thanh Nhan", 1960);
XuatDs(4,a);
}

 Phương thức ảo xuat được khai báo ở lớp Nguoi cho
phép sử dụng con trỏ đến lớp cơ sở (Nguoi) nhưng trỏ
đến một đối tượng thuộc lớp con (Sinh viên, công
nhân) gọi đúng thao tác ở lớp con:

20



Phương thức ảo
Nguoi *pn;
pn = new SinhVien("Vien Van Sinh", "200001234", 1982);
pn->Xuat(); // Goi thao tac xuat cua lop Sinh vien

 Con trỏ pn thuộc lớp Nguoi nhưng trỏ đến đối tượng
sinh viên, vì vậy pn->Xuat() thực hiện thao tác xuất của
lớp sinh viên.
 Trở lại ví dụ trên, khi i a[i] lần lượt trỏ đến các đối
tượng thuộc các loại khác nhau, thao tác tương ứng với
lớp sẽ được gọi.
 Dùng phương thức ảo khắc phục được các nhược điểm
của cách tiếp cận dùng vùng chọn kiểu:
 Thao tác đơn giản không phải dùng switch/case vì vậy
khó sai, dễ sửa.
21


Thêm lớp con mới
 Dùng phương thức ảo, ta dễ dàng nâng cấp sửa chữa. Việc
thêm một loại đối tượng mới rất đơn giản, ta không cần phải
sửa đổi thao tác xử lý (hàm XuatDs). Qui trình thêm chỉ là xây
dựng lớp con kế thừa từ lớp cơ sở hoặc các lớp con đã có và
định nghĩa lại phương thức (ảo) ở lớp mới tạo nếu cần
class CaSi : public Nguoi
{
protected:
double CatXe;

public:
CaSi(char *ht, double cx, int ns) : Nguoi(ht,ns), CatXe(cx) {}
void Xuat() const { cout << "Ca si, " << HoTen << " co cat xe " <<
CatXe;}
};

22


Thêm lớp con mới
void XuatDs(int n, Nguoi *an[])
{
for (int i = 0; i < n; i++)
{
an[i]->Xuat();
cout << "\n";
}
}

 Hàm XuatDs không thay đổi, nhưng nó có thể hoạt
động cho các loại đối tượng ca só thuộc lớp mới ra
đời.
 Có thể xem như thao tác XuatDs được viết trước cho
các lớp con cháu chưa ra đời.

23


Các lưu ý khi sử dụng phương thức ảo
 Phương thức ảo chỉ hoạt động thông qua con trỏ.

 Muốn một hàm trở thành phương thức ảo có hai cách: Khai
báo với từ khoá virtual hoặc hàm tương ứng ở lớp cơ sở đã là
phương thức ảo.
 Phương thức ảo chỉ hoạt động nếu các hàm ở lớp cơ sở và lớp
con có nghi thức giao tiếp giống hệt nhau.
 Nếu ở lớp con định nghĩa lại phương thức ảo thì sẽ gọi phương
thức ở lớp cơ sở (gần nhất có định nghĩa).

24


Ví dụ thêm về phương thức ảo
 Một ví dụ tương tự về sử dụng phương thức ảo là quản lý một
danh sách các động vật. Xem mamal.cpp

25


×