C++ và lập trình hướng đối tượng - Chương 6 - Pdf 91

Chơng 6
Tơng ứng bội và phơng thức ảo
Tơng ứng bội và phơng thức ảo là công cụ mạnh của C++
cho phép tổ chức quản lý các đối tợng khác nhau theo cùng một lợc
đồ. Một khái niệm khác liên quan là: lớp cơ sở trừu tợng. Chơng này
sẽ trình bầy cách sử dụng các công cụ trên để xây dựng chơng trình
quản lý nhiều đối tợng khác nhau theo một lợc đồ thống nhất.
Đ
1. Phơng thức tĩnh
1.1. Lời gọi tới phơng thức tĩnh
Nh đã biết một lớp dẫn xuất đợc thừa kế các phơng thức của các
lớp cơ sở tiền bối của nó. Ví dụ lớp A là cơ sở của B, lớp B lại là cơ
sở của C, thì C có 2 lớp cơ sở tiền bối là B và A. Lớp C đợc thừa kế
các phơng thức của A và B. Các phơng thức mà chúng ta vẫn nói là
các phơng thức tĩnh. Để tìm hiểu thêm về cách gọi tới các phơng thức
tĩnh, ta xét ví dụ về các lớp A, B và C nh sau:
class A
{
public:
void xuat()
{
cout << "\n Lop A " ;
}
};
class B:public A
{
public:
void xuat()
{
cout << "\n Lop B " ;
}

p = &a ;
q = &b ;
r = &c ;
Chúng ta tiếp tục xét các lời gọi phơng thức từ các con trỏ p, q, r:
p->xuat();
q->xuat();
r->xuat();
và hãy lý giải xem phơng thức nào (trong các phơng thức A::xuat,
B::xuat và C::xuat) đợc gọi. Câu trả lời nh sau:
Cả 3 câu lệnh trên đều gọi tới phơng thức A::xuat() , vì các con trỏ
p, q và r đều có kiểu A.
Nh vậy có thể tóm lợc cách thức gọi các phơng thức tĩnh nh sau:
Quy tắc gọi phơng thức tĩnh: Lời gọi tới phơng thức tĩnh bao giờ
cũng xác định rõ phơng thức nào (trong số các phơng thức trùng tên
của các lớp có quan hệ thừa kế) đợc gọi:
1. Nếu lời gọi xuất phát từ một đối tợng của lớp nào, thì phơng
thức của lớp đó sẽ đợc gọi.
2. Nếu lời gọi xuất phát từ một con trỏ kiểu lớp nào, thì phơng
thức của lớp đó sẽ đợc gọi bất kể con trỏ chứa địa chỉ của đối tợng
nào.
1.2. Ví dụ
Xét 4 lớp A, B, C và D. Lớp B và C có chung lớp cơ sở A. Lớp D
dẫn xuất từ C. Cả 4 lớp đều có phơng thức xuat(). Xét hàm:
void hien(A *p)
{
p->xuat();
}
Không cần biết tới địa chỉ của đối tợng nào sẽ truyền cho đối con
trỏ p, lời gọi trong hàm luôn luôn gọi tới phơng thức A::xuat() vì con
trỏ p kiểu A. Nh vậy bốn câu lệnh:

{
return n;
}
};
class B:public A
{
public:
B():A()
{
}
B(int n1):A(n1)
{
}
void xuat()
{
cout << "\nLop B: "<<getN();
}
};
class C:public A
{
public:
C():A()
{
}
C(int n1):A(n1)
{
}
void xuat()
{
cout << "\nLop C: "<<getN();

hien(&c);
hien(&d);
getch();
}
Đ
2. Sự hạn chế của phơng thức tĩnh
Ví dụ sau cho thấy sự hạn chế của phơng thức tĩnh trong việc sử
dụng tính thừa kế để phát triển chơng trình.
Giả sử cần xây dựng chơng trình quản lý thí sinh. Mỗi thí sinh đa
vào ba thuộc tính: Họ tên, số báo danh và tổng điểm. Chơng trình
gồm ba chức năng: Nhập dữ liệu thí sinh, in dữ liệu thí sinh ra máy in
và xem - in (in họ tên ra màn hình, sau đó lựa chọn hoặc in hoặc
không). Chơng trình dới đây sử dụng lớp TS (Thí sinh) đáp ứng đợc
yêu cầu đặt ra.
//CT6-02
// Han che phuong thuc tinh
// Lop TS
#include <conio.h>
#include <stdio.h>
#include <iostream.h>
#include <ctype.h>
class TS
{
private:
char ht[25];
int sobd;
float td;
public:
void nhap()
{

for (i=1; i<=n; ++i)
t[i].nhap();
for (i=1; i<=n; ++i)
t[i].xem_in();
getch();
}
Giả sử Nhà trờng muốn quản lý thêm địa chỉ của thí sinh. Vì sự
thay đổi ở đây là không nhiều, nên chúng ta không đả động đến lớp
TS mà xây dựng lớp mới TS2 dẫn xuất từ lớp TS. Trong lớp TS2 đa
thêm thuộc tính dc (địa chỉ) và các phơng thức nhap, in. Cụ thể lớp
TS2 đợc định nghĩa nh sau:
class TS2:public TS
{
private:
char dc[30] ; // Dia chi
public:
void nhap()
{
TS::nhap();
cout << "Dia chi: " ;
fflush(stdin); gets(dc);
}
void in()
{
TS::in();
fprintf(stdprn,"\nDia chi: %s", dc);
}
};
Trong lớp TS2 không xây dựng lại phơng thức xem_in, mà sẽ
dùng phơng thức xem_in của lớp TS. Chơng trình mới nh sau:

void xem_in()
{
int ch;
cout << "\nHo ten: " << ht ;
cout << "\nCo in khong? - C/K" ;
ch = toupper(getch());
if (ch=='C')
this->in(); //Goi den TS::in() (Vi this la con tro
//kieu TS)
}
} ;
class TS2:public TS
{
private:
char dc[30] ; // Dia chi
public:
void nhap()
{
TS::nhap();
cout << "Dia chi: " ;
fflush(stdin); gets(dc);
}
void in()
{
TS::in();
fprintf(stdprn,"\nDia chi: %s", dc);
}
};
void main()
{

this->in() ;
sẽ đợc thực hiện. Mặc dù địa chỉ của t[i] (là đối tợng của lớp TS2) đ-
ợc truyền cho con trỏ this, thế nhng câu lệnh này luôn luôn gọi tới
phơng thức TS::in(), vì con trỏ this ở đây có kiểu TS và vì in() là ph-
ơng thức tĩnh. Kết quả là không in đợc địa chỉ của thí sinh.
Nh vậy việc sử dụng các phơng thức tĩnh in() (trong các lớp TS và
TS2) đã không đáp ứng đợc yêu cầu phát triển chơng trình. Có một
giải pháp rất đơn giản là: Định nghĩa các phơng thức in() trong các
lớp TS và TS2 nh các phơng thức ảo (virtual).
Đ
3. Phơng thức ảo và tơng ứng bội
3.1. Cách định nghĩa phơng thức ảo
Giả sử A là lớp cơ sở, các lớp B, C, D dẫn xuất (trực tiếp hoặc dán
tiếp) từ A. Giả sử trong 4 lớp trên đều có các phơng thức trùng dòng
tiêu đề (trùng kiểu, trùng tên, trùng các đối). Để định nghĩa các ph-
ơng thức này là các phơng thức ảo, ta chỉ cần:
+ Hoặc thêm từ khoá virtual vào dòng tiêu đề của phơng thức bên
trong định nghĩa lớp cơ sở A.
+ Hoặc thêm từ khoá virtual vào dòng tiêu đề bên trong định
nghĩa của tất cả các lớp A, B, C và D.
Ví dụ:
Cách 1:
class A
{
...
virtual void hien_thi()
{
cout << \n Đây là lớp A ;
};
} ;

{
cout << \n Đây là lớp A ;
};
} ;
class B : public A
{
...
virtual void hien_thi()
{
cout << \n Đây là lớp B ;
};
} ;
class C : public B
{
...
virtual void hien_thi()
{
cout << \n Đây là lớp C ;
};
} ;
class D : public A
{
...
virtual void hien_thi()
{
cout << \n Đây là lớp D ;
};
} ;
Chú ý: Từ khoá virtual không đợc đặt bên ngoài định nghĩa lớp.
Ví dụ nếu viết nh sau là sai (CTBD sẽ báo lỗi).

thức của lớp đó sẽ đợc gọi.
2. Nếu lời gọi xuất phát từ một con trỏ kiểu lớp nào, thì phơng
thức của lớp đó sẽ đợc gọi bất kể con trỏ chứa địa chỉ của đối tợng
nào.
3.2.2. Quy tắc gọi phơng thức ảo
Phơng thức ảo chỉ khác phơng thức tĩnh khi đợc gọi từ một con trỏ
(trờng hợp 2 nêu trong mục 3.2.1). Lời gọi tới phơng thức ảo từ một
con trỏ cha cho biết rõ phơng thức nào (trong số các phơng thức ảo
trùng tên của các lớp có quan hệ thừa kế) sẽ đợc gọi. Điều này phụ
thuộc vào đối tợng cụ thể mà con trỏ đang trỏ tới: Con trỏ đang trỏ
tới đối tợng của lớp nào thì phơng thức của lớp đó sẽ đợc gọi.
Ví dụ A, B, C và D là các lớp đã định nghĩa trong 3.1. Ta khai báo
một con trỏ kiểu A và 4 đối tợng:
A *p ; // p là con trỏ kiểu A
A a ; // a là biến đối tợng kiểu A
B b ; // b là biến đối tợng kiểu B
C c ; // c là biến đối tợng kiểu C
D d ; // d là biến đối tợng kiểu D
Xét lời gọi tới các phơng thức ảo hien_thi sau:
p = &a; // p trỏ tới đối tợng a của lớp A
p->hien_thi() ; // Gọi tới A::hien_thi()
p = &b; // p trỏ tới đối tợng b của lớp B
p->hien_thi() ; // Gọi tới B::hien_thi()
p = &c; // p trỏ tới đối tợng c của lớp C
p->hien_thi() ; // Gọi tới C::hien_thi()
p = &d; // p trỏ tới đối tợng d của lớp D
p->hien_thi() ; // Gọi tới D::hien_thi()
3.3. Tơng ứng bội
Chúng ta nhận thấy cùng một câu lệnh
p->hien_thi();


Nhờ tải bản gốc

Tài liệu, ebook tham khảo khác

Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status