Tải bản đầy đủ (.pptx) (40 trang)

Bài giảng lập trình hướng đối tượng dùng c + +chương 6 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 (5.19 MB, 40 trang )

Chương 6. Tính đa hình
(Polymorphism)
TRẦN MINH THÁI
Email:
Website: www.minhthai.edu.vn
Cập nhật: 10 tháng 04 năm 2015
#2
Nội dung
1. Giới thiệu đa hình
2. Phương thức ảo
3. Lớp trừu tượng
4. Bài tập ví dụ
#3
Giới thiệu [1/6]
Giả sử có 2 hàm

double max(double d1, double d2);

int max(int i1, int i2);

Một thông điệp (lời gọi hàm) được hiểu theo các cách khác nhau tùy theo danh sách
tham số của thông điệp

Đa hình hàm → đa năng hóa hàm
#4
Giới thiệu [2/6]
Đa hình là hiện tượng các đối tượng thuộc các lớp khác nhau có khả năng hiểu cùng một
thông điệp theo các cách khác nhau
Cùng thông điệp “nhảy”, kangaroo và con cóc nhảy theo hai kiểu khác nhau: chúng cùng có
hành vi “nhảy” nhưng các hành vi này có nội dung khác nhau
#5


Giới thiệu [3/6]
Đa hình được cài đặt bởi cơ chế overriding
Nếu một phương thức của lớp cơ sở được định nghĩa lại tại lớp dẫn xuất thì định nghĩa
tại lớp cơ sở có thể bị “che” bởi định nghĩa tại lớp dẫn xuất.
Với overriding, toàn bộ thông điệp (cả tên và tham số) là hoàn toàn giống nhau - điểm
khác nhau là lớp đối tượng được nhận thông điệp.
#6
Giới thiệu [4/6]
!!! Lời gọi đến một phương thức của một đối tượng trỏ /tham chiếu tới được xem như lời gọi đến phương thức chứ
không phải tương ứng với đối tượng đang được trỏ /tham chiếu tới → Kết nối tĩnh (static binding). Hàm thành viên
gọi từ con trỏ đối tượng được xác định trước khi chương trình chạy
class A
{
public:
void Print()
{
cout<<"A::Print()";
cout<<endl;
}
};
class B: public A
{
public:
void Print()
{
cout<<"B::Print()";
cout<<endl;
}
};
B b;

A *pa=&b;
pa->Print(); //A::Print()
#7
Giới thiệu [5/6]
CCircle *pc = new CCircle(50, 30, "Blue",100);
CMyPoint *pp = pc;
pp -> Draw(); //draw point ???
#8
Giới thiệu [6/6]
Để gọi được phương thức với đối tượng đươc trỏ/tham chiếu tới

Cần phải xác định được kiểu của đối tượng được xem xét tại thời điểm chương trình đang chạy (runtime)

Kết nối động (dynamic binding) hoặc kết nối trễ (late binding)

Xác định hàm thành viên nào tương ứng với một lời gọi hàm thành viên từ con trỏ/tham chiếu đối tượng
phụ thuộc vào cụ thể vào đối tượng mà con trỏ/tham chiếu chứa địa chỉ
#9
Phương thức ảo – Virtual method [1/14]
??? Muốn thực hiện Print() của lớp B
class A
{
public:
void Print()
{
cout<<"A::Print()";
cout<<endl;
}
};
class B: public A

{
public:
void Print()
{
cout<<"B::Print()";
cout<<endl;
}
};
B b;
A *pa=&b;
pa->Print(); //A::Print()
#10
Phương thức ảo [2/14]

Là cơ chế của C++ cho phép cài đặt kết nối động

Gọi được phương thức với đối tượng đươc trỏ/tham chiếu tới

Phương thức ảo: thêm từ khóa virtual vào trước khai báo phương thức trong lớp
#11
Phương thức ảo [3/14]

Một khi một phương thức được khai báo là phương thức ảo tại lớp cơ sở, nó sẽ tự động
là phương thức ảo tại mọi lớp dẫn xuất trực tiếp hoặc gián tiếp
→ Không cần thêm virtual khi khai báo một phương thức ảo trong lớp dẫn xuất
#12
Phương thức ảo [4/14]
class A
{
public:

virtual void Print()
{
cout<<"A::Print()";
cout<<endl;
}
};
class B: public A
{
public:
virtual void Print()
{
cout<<"B::Print()";
cout<<endl;
}
};
B b;
A *pa=&b;
pa->Print(); //B::Print()
#13
Phương thức ảo – Ví dụ [5/14]
class CHome
{
public :
virtual void Paint()
{
}
};
class CWoodframe : public CHome
{
public:

virtual void Paint()
{
cout <<"Son nha go\n" ;
}
};
class CStucco: public CHome
{
public:
virtual void Paint()
{
cout <<"Son nha xay\n" ;
}
};
class CLand: public CHome
{
public:
virtual void Paint()
{
cout <<"Son nha dat\n" ;
}
};
CWoodframe w;
w.Paint ();
CLand a, b;
CStucco c;
CWoodframe d,e;
CHome *h[5];
h[0] = &a;
h[1] = &b;
h[2] = &c;

h[3] = &d;
h[4] = &e;
int i;
for (i=0;i<5;i++)
h[i]->Paint();
#14
Phương thức ảo [6/14]

Phương thức ảo chỉ hoạt động thông qua con trỏ/ tham chiếu

Phương thức ảo tồn tại để có hiệu lực nhưng không có thực trong lớp cơ sở, trong lớp dẫn
xuất mới định nghĩa rõ ràng
→ Phương thức ảo chỉ được xây dựng khi có kế thừa. Phương thức này sẽ được gọi thực hiện từ
thực thể của lớp dẫn xuất nhưng được mô tả trong lớp cơ sở
#15
Phương thức ảo [7/14]

Không thể có tính đa hình khi không có sự kế thừa và phương thức ảo

Điều kiện để có tính đa hình là phải có sự kế thừa và phương thức ảo trong lớp cơ
sở
#16
Phương thức ảo [8/14]
Cơ chế đa hình được thực hiện dựa vào bảng phương thức ảo của đối tượng
Bảng chứa địa chỉ của các phương thức ảo
Được TBD khởi tạo một cách ngầm định khi thiết lập đối tượng
TBD gặp đối tượng đầu tiên thuộc lớp có phương thức ảo  thêm vào mỗi đối tượng
của lớp cơ sở và các lớp dẫn xuất một con trỏ ảo
#17
Phương thức ảo [9/14]

virtual pointer (vptr) nằm trong bảng phương thức ảo và có nhiệm vụ quản lý địa chỉ của các
phương thức ảo
Khi đối tượng khác tạo ra, TBD không tạo thêm vptr → Mỗi lớp chỉ có một bảng phương
thức ảo lưu các vptr
Nếu lớp có constructor và destructor, vptr sẽ được tạo ra trước khi gọi thực hiện các phương
thức này
Khi thao tác được thực hiện thông qua con trỏ/tham chiếu, hàm có địa chỉ trong bảng phương
thức ảo sẽ được gọi
#18
Phương thức ảo [10/14]
Các đặc trưng
Việc khai báo các phương thức ảo giữa lớp cơ sở và dẫn xuất phải thống nhất với nhau
→ Tất cả các phiên bản của phương thức ảo phải được khai báo cùng kiểu trả về, cùng tên, danh
sách các tham số (gọi là cùng giao diện)
→ Điều kiện của kết nối động
#19
Phương thức ảo [11/14]
Các đặc trưng
Không thể là hàm thành viên tĩnh (do bảng phương thức ảo chỉ tạo ra khi đối tượng của lớp
được tạo ra)
Có thể được khai báo là friend trong một lớp khác nhưng các hàm friend của lớp thì không
thể là phương thức ảo
#20
Phương thức ảo [12/14]

Không thể khai báo các constructor ảo (do bảng phương thức ảo tạo trước)

Có thể (và rất nên) khai báo destructor là hàm ảo
class A
{

public:
~A()
{
cout<<"~A";
cout<<endl;
}
};
class B: public A
{
public:
~B()
{
cout<<"~B";
cout<<endl;
}
};
B *pb = new B;
A *pa = pb;
delete pa; //A::~A()
#21
Phương thức ảo [13/14]
Việc gọi nhầm destructor không ảnh hưởng đến việc thu hồi bộ nhớ của đối tượng
(trong mọi trường hợp phần bộ nhớ của đối tượng sẽ được thu hồi chính xác)
Tuy nhiên, nếu không gọi đúng destructor, các đoạn mã dọn dẹp quan trọng có thể bị
bỏ qua (chẳng hạn như giải phóng các thành viên được cấp phát động)
#22
Phương thức ảo [14/14]
Quy tắc chung: mỗi khi tạo một lớp để được dùng làm lớp cơ sở  khai báo destructor
ảo
class A

{
public:
virtual ~A()
{
cout<<"~A";
cout<<endl;
}
};
class B: public A
{
public:
~B()
{
cout<<"~B";
cout<<endl;
}
};
B *pb = new B;
A *pa = pb;
delete pa; //B::~B()

//A::~A()
#23
Phương thức ảo thuần tuý [1/2]
Pure virtual method

Để tránh tình trạng lãng phí bộ nhớ khi xây dựng các đối tượng. C++ cho phép xây dựng
các phương thức ảo không có phần định nghĩa

Hàm thuần túy ảo


Cú pháp

Thêm “= 0” vào cuối khai báo phương thức
#24
Phương thức ảo thuần tuý [2/2]
Không cần định nghĩa phương thức
Nếu không có “ = 0” và không định nghĩa → TBD báo lỗi
class CHome
{
public :
virtual void Paint() = 0;
};
#25
Lớp trừu tượng – abstract class [1/5]

Lớp không thể tạo thực thể nào  Lớp không có đối tượng nào nhưng có thể tạo ra biến
con trỏ/ tham chiếu của nó

Thực tế, ta thường phân nhóm các đối tượng theo kiểu này
o
Chim và ếch đều là động vật, nhưng một con động vật là con gì?
o
Bia và rượu đều là đồ uống, nhưng một thứ đồ uống chính xác là cái gì?

×