Bài giảng Lập trình hướng đối tượng - Chương 6: Tính đa hình (Polymorphism) - Trần Minh Thái

Giới thiệu đa hình Phương thức ảo Lớp trừu tượng Bài tập ví dụ Đ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 Đ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.

pptx40 trang | Chia sẻ: candy98 | Lượt xem: 560 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng Lập trình hướng đối tượng - Chương 6: Tính đa hình (Polymorphism) - Trần Minh Thái, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Chương 6. Tính đa hình (Polymorphism)TRẦN MINH THÁIEmail: minhthai@itc.edu.vnWebsite: www.minhthai.edu.vn Cập nhật: 10 tháng 04 năm 2015Nội dungGiới thiệu đa hìnhPhương thức ảoLớp trừu tượngBài tập ví dụGiới thiệu [1/6]Giả sử có 2 hàmdouble 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 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 nhauCù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 nhauGiớ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.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ạyclass A{ public: void Print() { coutPrint(); //A::Print()Giới thiệu [5/6]CCircle *pc = new CCircle(50, 30, "Blue",100);CMyPoint *pp = pc;pp -> Draw(); //draw point ??? Giới thiệu [6/6]Để gọi được phương thức với đối tượng đươc trỏ/tham chiếu tớiCầ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ỉPhương thức ảo – Virtual method [1/14]??? Muốn thực hiện Print() của lớp Bclass A{ public: void Print() { coutPrint(); //A::Print()Phương thức ảo [2/14]Là cơ chế của C++ cho phép cài đặt kết nối độngGọi được phương thức với đối tượng đươc trỏ/tham chiếu tớiPhương thức ảo: thêm từ khóa virtual vào trước khai báo phương thức trong lớpPhươ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ấtPhương thức ảo [4/14]class A{ public: virtual void Print() { coutPrint(); //B::Print()Phương thức ảo – Ví dụ [5/14]class CHome{ public : virtual void Paint() { }};class CWoodframe : public CHome{ public: virtual void Paint() { cout Paint();Phương thức ảo [6/14]Phương thức ảo chỉ hoạt động thông qua con trỏ/ tham chiếuPhươ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ở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ở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ượngBả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ượngTBD 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ỏ ảoPhươ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 ảoKhi đố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àyKhi 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ọiPhương thức ảo [10/14]Các đặc trưngViệ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 độngPhương thức ảo [11/14]Các đặc trưngKhô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 ảoPhươ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 ảoclass A{ public: ~A() { coutx=x; this->y=y; } int GetX() { return x; } int GetY() { return y; } void Print() { coutPrint(); coutPerimeter()Area()<<endl; } }int MyRand(int a,int b){ return rand()%(b-a+1)+a;}#define MIN 10#define MAX 80#define DIM(x) sizeof(x)/sizeof(x[0])Lớp trừu tượng – Ví dụ 1 [6/10]void main(){ srand((unsigned)time(NULL)); CTriangle t1(MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX), MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX)); CTriangle t2(MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX), MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX)); CCircle c1(MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX)); CCircle c2(MyRand(MIN,MAX),MyRand(MIN,MAX),MyRand(MIN,MAX)); CShape *s[]={&t1,&c1,&c2,&t2}; int n=DIM(s); Output(s,n);}Lớp trừu tượng – Ví dụ 2 [7/10]Đa năng hóa toán tử với hàm toán tử là phương thức ảoclass A{ protected: int x1; public: A(int i) { x1=i; } virtual A& operator + (A& t); virtual A& operator = (A& t); virtual int GetA() { return x1; } virtual int GetB() { return 0; } virtual int GetC() { return 0; } void Print(char *st) { cout<<st<<":x1="<<x1<<endl; }}; //end of class AA& A::operator + (A& t){ x1+=t.GetA(); return *this;}A& A::operator = (A& t){ x1=t.GetA(); return *this;}Lớp trừu tượng – Ví dụ 2 [8/10]class B : public A{ protected: int x2; public: B(int i,int j):A(i) { x2=j; } virtual A& operator + (A& t); virtual A& operator = (A& t); virtual int GetB() { return x2; } void Print(char *st) { cout<<st<<":x1="<<x1; cout<<",x2="<<x2<<endl; }};A& B::operator + (A& t){ x1+=t.GetA(); x2+=t.GetB(); return *this;}A& B::operator = (A& t){ x1=t.GetA(); x2=t.GetB(); return *this;}Lớp trừu tượng – Ví dụ 2 [9/10]class C : public B{ protected: int x3; public: C(int i,int j,int k):B(i,j) { x3=k; } virtual A& operator + (A& t); virtual A& operator = (A& t); virtual int GetC() { return x3; } void Print(char *st) { cout<<st<<":x1="<<x1<<",x2="; cout<<x2<<",x3="<<x3<<endl; }};A& C::operator + (A& t){ x1+=t.GetA(); x2+=t.GetB(); x3+=t.GetC(); return *this;}A& C::operator = (A& t){ x1=t.GetA(); x2=t.GetB(); x3=t.GetC(); return *this;}Lớp trừu tượng – Ví dụ 2 [10/10]void main(){ A a(10); B b(10,20); C c(10,20,30); a.Print("a"); b.Print("b"); c.Print("c"); AddObject(a,b);//a=a+b a.Print("a"); AddObject(b,c);//b=b+c b.Print("b"); AddObject(c,a);//c=c+a c.Print("c"); a=b+c; a.Print("a"); c=c+a; c.Print("c"); }void AddObject(A& t1,A& t2){ t1 = t1+t2;}Q&A