Tải bản đầy đủ (.pdf) (27 trang)

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 (53.43 KB, 27 trang )




Chương 9


Tính đa hình











Con trỏ và Lớp dẫn xuất


Dẫn nhập các hàm ảo


Các hàm ảo thuần túy


Áp dụng đa hình













Chương 9
Tính đa hình


270

I/ Con trỏ và Lớp dẫn xuất

1/ Khái niệm
Tính đa hình
(polymorphism) được hổ trợ bằng hai cách khác nhau trong C++ .

Cách 1, đa hình được hổ trợ
khi biên dòch chương trình (compiler) thông qua việc quá
tải các hàm và toán tử
.

Cách 2, đa hình được hổ trợ ở thời điểm
thực thi chương trình (run-time) thông qua
các hàm ảo
. Cách này giúp lập trình viên linh động hơn.



Cơ sở của hàm ảo và đa hình khi thực thi chương trình là các con trỏ của lớp
dẫn xuất.

Chương 3 có khảo sát về con trỏ, một đặc tính mới của con trỏ sẽ được khảo sát
trong chương này.
Nếu p là một con trỏ tới lớp cơ sở, thì có thể sử dụng p để trỏ tới
bất kỳ lớp nào được suy ra từ lớp cơ sở
.

Chẳng hạn, có hai lớp cơ sở
base
và lớp dẫn xuất
derived
kế thừa base, các phát biểu
sau đều đúng

base *p; // base class pointer

base base_ob; // object of type base
derived derived_ob; // object of type derived

// p can, of course, point to base objects
p =
&
base_ob; // p points to base object

// p can also point to derived objects without error
p =

&
derived_ob; // p points to derived object

Một con trỏ của lớp cơ sở có thể trỏ tới bất kỳ lớp dẫn xuất nào của lớp cơ sở mà
không gây ra báo lỗi khác kiểu
. Song
chỉ có thể truy cập được các thành phần mà lớp
dẫn xuất được kế thừa từ lớp cơ sở
. Bởi vì con trỏ của lớp cơ sở chỉ biết lớp cơ sở mà
thôi, nó không biết gì những thành phần được thêm vào bởi lớp dẫn xuất.

Chương 9
Tính đa hình


271


Một con trỏ của lớp dẫn xuất không thể dùng để truy cập một đối tượng của lớp
cơ sở
. (Việc sử dụng linh hoạt kiểu có thể dùng để khắc phục hạn chế nói trên,
nhưng nó không được khuyến khích sử dụng)

Các phép toán số học trên con trỏ liên quan đến kiểu dữ liệu mà con trỏ đó được
khai báo để trỏ đến. Do đó, nếu con trỏ đến một đối tượng lớp dẫn xuất, rồi
tăng nội
dung con trỏ lên 1
. Điều này không làm cho con trỏ chỉ đến đối tượng mới của lớp
dẫn xuất, mà
nó sẽ chỉ đến đối tượng mới của lớp cơ sở

.


Ví dụ 1.1
Dùng con trỏ của lớp cơ sở để truy cập đến lớp dẫn xuất.

// Demonstrate pointer to derived class.
#include <iostream.h>

class base {
int x;
public:
void setx(int i) { x = i; }
int getx() { return x; }
};

class derived :
public
base {
int y;
public:
void sety(int i) { y = i; }
int gety() { return y; }
};

int main()
{
base *p; // pointer to base type
base b_ob; // object of base
derived d_ob; // object of derived


// use p to access base object
p =
&b_ob
;
Chương 9
Tính đa hình


272

p->setx(10); // access base object
cout << "Base object x: " << p->getx() << '\n';

// use p to access derived object
p =
&d_ob
; // point to derived object
p->setx(99); // access derived object

// can't use p to set y, so do it directly
d_ob.sety(88);
cout << "Derived object x: " << p->getx() << '\n';
cout << "Derived object y: " << d_ob.gety() << '\n';

return 0;
}


II/ Dẫn nhập hàm ảo (virtual function)


1/ Khái niệm
Hàm ảo
là hàm thành phần của một lớp, nó được khai báo ở lớp cơ sở và được đònh
nghiã lại trong lớp dẫn xuất.

Khai báo hàm ảo bắt đầu bằng từ khoá
virtual
. Một lớp có chứa hàm ảo được kế
thừa, lớp dẫn xuất sẽ tái đònh nghiã hàm ảo đó cho chính mình.


Các hàm ảo triển khai ý tưởng chủ đạo của đa hình là "
một giao diện cho nhiều
phương thức
". Hàm ảo bên trong một lớp cơ sở
đònh nghiã hình thức giao tiếp
đối
với hàm đó.

Việc tái đònh của hàm ảo ở lớp dẫn xuất là
thi hành các tác vụ của hàm liên quan đến
chính lớp dẫn xuất đó
. Nói cách khác, tái đònh các hàm ảo chính là tạo ra các phương
thức cụ thể. Hàm ảo tái đònh ở lớp dẫn xuất không cần sử dụng từ khoá virtual.






Nguyên lý làm việc của đa hình trong khi thực thi chương trình
:
Chương 9
Tính đa hình


273

Hàm ảo được gọi thực thi giống như các hàm thành phần bình thường của lớp. Tuy
nhiên, khi gọi hàm ảo bằng con trỏ, việc hổ trợ tính đa hình trong khi thực thi chương
trình sẽ xảy ra.

Khi một con trỏ trỏ đến một lớp dẫn xuất có chứa hàm ảo và hàm ảo này được gọi
bằng con trỏ thì trình biên dòch sẽ xác đònh phiên bản nào của hàm đó sẽ được thực
thi. Do đó nếu có hai hay nhiều lớp dẫn xuất của một lớp cơ sở nào đó, và chúng đều
có chứa hàm ảo, thì con trỏ của lớp cơ sở có thể trỏ đến các đối tượng khác nhau của
lớp dẫn xuất nói trên, tức là có thể gọi đến nhiều phiên bản khác nhau của các hàm
ảo.


Một lớp có chứa hàm ảo được gọi là
lớp đa hình
.

Ví dụ 2.1

// A simple example using a virtual function.
#include <iostream.h>

class base {

public:
int i;
base(int x) { i = x; }

virtual
void
func()

{
cout << "Using base version of func(): ";
cout << i << '\n';
}
};

class derived1 :
public
base {
public:
derived1(int x) :
base(x)
{}
void
func()

{
cout << "Using derived1's version of func(): ";
cout << i
*
i << '\n';
}

Chương 9
Tính đa hình


274

};

class derived2 :
public
base {
public:
derived2(int x) :
base(x)
{}
void
func()

{
cout << "Using derived2's version of func(): ";
cout << i
+
i << '\n';
}
};

int main()
{
base *p;
base ob(10);

derived1 d_ob1(10);
derived2 d_ob2(10);

p = &ob;

p->func()
; // use base's func()

p = &d_ob1;

p->func()
; // use derived1's func()

p = &d_ob2;

p->func()
; // use derived2's func()

return 0;
}

@ Kiểu của đối tượng được trỏ đến sẽ xác đònh phiên bản nào của hàm ảo được thực
thi thông qua cách gọi hàm bằng con trỏ. Điều này chỉ xác đònh được trong lúc run-
time.


2/ Hàm ảo và quá tải hàm
Giải thích kết qủa chương trình ?

Using base version of func() : 10

Using derived1's version of func() : 100
Using derived2's version of func() : 20
Chương 9
Tính đa hình


275

Việc tái đònh hàm ảo trong một lớp dẫn xuất có tương tự như quá tải hàm không ?
Câu trả lời là không.


Quá tải hàm Hàm ảo
Số lượng đối số Cho phép khác biệt Phải giống nhau
Kiểu dữ liệu của đối số Cho phép khác biệt Phải giống nhau
Hàm thành phần của lớp Không bắt buộc Bắt buộc
Hàm tạo Được phép Không được
Hàm hủy Không được Có thể
Vò trí

Việc tái đònh hàm ảo trong một lớp dẫn xuất còn được gọi là
gán thứ tự ưu tiên cao
hơn cho hàm đó
.

3/ Các hàm ảo được phân cấp theo thứ tự kế thừa
.
Nếu lớp dẫn xuất không tái đònh hàm ảo nào đó thì lớp này sẽ sử dụng phiên bản
hàm của lớp cơ sở.


Ví dụ 2.2
// Virtual functions are hierarchical.
#include <iostream.h>

class
base
{
public:
int i;
base(int x) { i = x; }

virtual void func()

{
cout << "Using base version of func(): ";
cout << i << '\n';
}
};

class
derived1
:
public
base {
public:
derived1(int x) : base(x) {}

void func()
Chương 9
Tính đa hình



276

{
cout << "Using derived1's version of func(): ";
cout << i*i << '\n';
}
};

class
derived2
:
public
base {
public:
derived2(int x) : base(x) {}
// derived2 does not override func()
};

int main()
{
base *p;
base ob(10);
derived1 d_ob1(10);
derived2 d_ob2(10);
p = &ob;

p->func()
; // use base's func()


p = &d_ob1;
p->func(); // use derived1's func()


p = &d_ob2;

p->func()
; // use base's func()

return 0;
}


4/ Cách đáp ứng của hàm ảo đối với một biến cố ngẫu nhiên ở thời điểm run-
time

Ví dụ 2.3
// This example illustrates how a virtual function can be used to respond to random
// events occurring at run time.
Kết qủa chương trình

Using base version of func() : 10
Using derived1's version of func() : 100
Using base version of func() : 10
Chöông 9
Tính ña hình


277



#include <iostream.h>
#include <stdlib.h>

class base {
public:
int i;
base(int x) { i = x; }

virtual void func()
{
cout << "Using base version of func(): ";
cout << i << '\n';
}
};


class
derived1
:
public
base {
public:
derived1(int x) : base(x) {}

void func()
{
cout << "Using derived1's version of func(): ";
cout << i*i << '\n';

}
};

class
derived2
:
public
base {
public:
derived2(int x) : base(x) {}
void func()
{
cout << "Using derived2's version of func(): ";
cout << i+i << '\n';
}
};



int main()
Chương 9
Tính đa hình


278

{
base *p;
derived1 d_ob1(10);
derived2 d_ob2(10);

int i, j;

for(i=0; i<10; i++) {
j = rand();
if( ( j % 2) ) p = &d_ob1; // if odd use d_ob1
else p = &d_ob2; // if even use d_ob2
p->func(); // call appropriate function
}

return 0;
}

Việc chọn lựa để thực thi phiên bản hàm ảo nào (hàm func() của lớp derived1 hay
hàm func() của lớp derived2) chỉ được quyết đònh trong lúc run-time. Điều này
không thể nào thực hiện được ở thời điểm biên dòch chương trình.


5/ Sử dụng hàm ảo để triển khai cách thức giao diện

Ví dụ 2.4
// Use virtual function to define interface.

#include <iostream.h>

class area {
double dim1, dim2; // dimensions of figure
public:
void setarea(double d1, double d2) {
dim1 = d1;
dim2 = d2;

}

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×