• Không có kết quả nào được tìm thấy

4. Phương thức ảo và tính đa 4. Phương thức ảo và tính đa

N/A
N/A
Protected

Academic year: 2022

Chia sẻ "4. Phương thức ảo và tính đa 4. Phương thức ảo và tính đa "

Copied!
23
0
0

Loading.... (view fulltext now)

Văn bản

(1)

4. Phương thức ảo và tính đa 4. Phương thức ảo và tính đa

hình hình

1

hình

hình

(2)

Nội dung Nội dung 1

1 Bài toán quản lý một danh Bài toán quản lý một danh sách các đối tượng khác kiểu sách các đối tượng khác kiểu 3

3 Phương thức ảo Phương thức ảo 4

4 Phương thức ảo thuần tuý Phương thức ảo thuần tuý

2

4

4 Phương thức ảo thuần tuý Phương thức ảo thuần tuý

(3)

1 B.toán q.lý các đ.tượng khác kiểu 1 B.toán q.lý các đ.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 - 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.

(4)

Dùng vùng chọn kiểu 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.

(5)

Ví dụ minh hoạ Ví dụ minh hoạ

class Nguoi {

protected:

char *HoTen;

int NamSinh;

public:

Nguoi(char *ht, int ns):NamSinh(ns) {HoTen = strdup(ht);}

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; } };

(6)

Ví dụ minh hoạ Ví dụ minh hoạ

class SinhVien : public Nguoi {

protected:

char *MaSo;

public:

SinhVien(char *n, char *ms, int ns) : Nguoi(n,ns) { MaSo = strdup(ms);}

~SinhVien() {delete [] MaSo;}

~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";}

(7)

Ví dụ minh hoạ Ví dụ minh hoạ

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 "

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";

}

(8)

Ví dụ minh hoạ Ví dụ minh hoạ

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, a[2] = new CongNhan("Tran Nhan Cong", 1000000,

1984);

a[3] = new Nguoi("Nguyen Thanh Nhan", 1960);

XuatDs(4,a);

}

(9)

Dùng vùng chọn kiểu 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.

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.

Một cách tiếp cận khác là dùng phương thức ảo.

(10)

2. Phương thức ảo 2. 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.

(11)

Phương thức ảo Phương thức ảo

class Nguoi {

protected:

char *HoTen;

int NamSinh;

public:

Nguoi(char *ht, int ns):NamSinh(ns) {HoTen = strdup(ht);}

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; }

};

(12)

Phương thức ảo 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;}

~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";}

(13)

Phương thức ảo 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 "

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";

(14)

Phương thức ảo 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);

"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:

(15)

Phương thức ảo 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 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.

(16)

Thêm lớp con mới 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 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;}

};

(17)

Thêm lớp con mới 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.

(18)

Các lưu ý khi sử dụng phương thức ảo 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 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).

(19)

Phương thức huỷ bỏ ảo Phương thức huỷ bỏ ảo

Trong ví dụ quản lý danh sách các đối tượng thuộc các lớp Nguoi, SinhVien, CongNhan, … Thao tác dọn dẹp đối tượng là cần thiết.

const int N = 4;

void main() {

Nguoi *a[N];

a[0] = new SinhVien("Vien Van Sinh", a[0] = new SinhVien("Vien Van Sinh",

"20001234", 1982);

a[1] = new NuSinh("Le Thi Ha Dong",

"20001235", 1984);

a[2] = new CongNhan("Tran Nan Cong", 1000000, 1984);

a[3] = new Nguoi("Nguyen Thanh Nhan", 1960);

XuatDs(4,a);

for (int i = 0; i < 4; i++)

(20)

Phương thức huỷ bỏ ảo Phương thức huỷ bỏ ảo

Thông qua con trỏ thuộc lớp cơ sở Nguoi, chỉ có phương thức huỷ bỏ của lớp Nguoi được gọi.

Để bảo đảm việc dọn dẹp là đầy đủ, ta dùng phương thức huỷ bỏ ảo.

class Nguoi {

protected:

protected:

char *HoTen;

int NamSinh;

public:

Nguoi(char *ht, int ns):NamSinh(ns) {HoTen = strdup(ht);}

virtual ~Nguoi() {delete [] HoTen;}

virtual void Xuat(ostream &os) const { os <<

"Nguoi, ho ten: " << HoTen << " sinh " <<

NamSinh; }

void Xuat() const { Xuat(cout); }

(21)

3. PT ảo th tuý và lớp csở tr tượng 3. PT ảo th tuý và lớp csở tr tượng

Lớp cơ sở trừu tượng là lớp cơ sở không có đối tượng nào thuộc chính nó. Một đối tượng thuộc lớp cơ sở trừu tượng phải thuộc một trong các lớp con.

Xem chương trình nguồn t_hinh.cpp

(22)

Khi lớp có phương thức ảo thuần tuý, lớp trở thành lớp cơ sở trừu tượng. Ta không thể tạo đối tượng thuộc lớp cơ sở thuần tuý.

Ta có thể định nghĩa phương thức ảo thuần tuý, nhưng chỉ có các đối tượng thuộc lớp con có thể gọi nó.

Phương thức ảo thuần tuý có ý nghĩa cho việc tổ chức sơ đồ phân cấp các lớp, nó đóng vai trò chừa sẵn chỗ trống cho

Phương thức ảo thuần tuý và lớp cơ sở trừu tượng Phương thức ảo thuần tuý và lớp cơ sở trừu tượng

phân cấp các lớp, nó đóng vai trò chừa sẵn chỗ trống cho các lớp con điền vào với phiên bản phù hợp.

(23)

Phương thức ảo thuần tuý và lớp cơ sở trừu tượng Phương thức ảo thuần tuý và lớp cơ sở trừu tượng Bản thân các lớp con của lớp cơ sở trừu tượng cũng có thể

là lớp cơ sở trừu tượng.

Tài liệu tham khảo

Tài liệu liên quan