Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ

Dẫn xuất và kế thừa ( )

 Tandem bicycle là một loại xe đạp

– Xe đạp có hai yên

 Mountain bicycle là một loại xe đạp

– Xe đạp có khả năng chống sốc (lốp dầy và nhiều bắnh răng)

 Racing bicyle là một loại xe đạp

– Xe đạp có cấu tạo khí động lực học nhẹ

 Tandem, mountain, racing bicycle là những loại xe

đạp chuyên dụng

– Có các thành phần cơ bản của một chiếc xe đạp

– Cùng nguyên lý hoạt động

– Bổ sung thêm các thông tin khác

Dẫn xuất và kế thừa ( )

 Cơ chế dẫn xuất/thừa kế là một kỹ thuật lập trình hướng đối

tượng cho phép chuyên biệt hóa

 Dẫn xuất cho phép tạo ra một lớp mới (lớp dẫn xuất) của các đối

tượng bằng cách sử dụng các lớp cũ như là các lớp cơ sở

– Lớp dẫn xuất thừa hưởng các thuộc tính và hành vi của lớp “chamẹ” (lớp cơ sở)

– Lớp dẫn xuất là một phiên bản chuyên biệt hóa của lớp “cha-mẹ”

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 1

Trang 1

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 2

Trang 2

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 3

Trang 3

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 4

Trang 4

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 5

Trang 5

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 6

Trang 6

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 7

Trang 7

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 8

Trang 8

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 9

Trang 9

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ trang 10

Trang 10

Tải về để xem bản đầy đủ

pdf 40 trang duykhanh 7680
Bạn đang xem 10 trang mẫu của tài liệu "Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ

Bài giảng Lập trình - Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ
ất 
 . Xây dựng các lớp biểu diễn về con người, sinh viên, 
 giảng viên
 . Các thuộc tính và phép toán cơ bản của lớp Person
 Tên lớp
 Person
 name: string
 age: int Thuộc tính
 gender: string
 set_name
 get_name
 set_age Phép toán/ 
 get_age phương thức
 set_gender
 get_gender
 display
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 7
 . Các thuộc tính và phép toán cơ bản của lớp Student và 
 Lecture
 Student Lecture
 name: string name: string
 age: int age: int
 gender: string gender: string
 class: string faculty: string
 id: int telnumber: int
 set_name set_name
 get_name get_name
 set_age set_age
 get_age get_age
 set_gender set_gender
 get_gender get_gender
 set_class set_faculty
 get_class get_faculty
 set_id set_telnumber
 get_id get_telnumber
 display display
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 8
 5.1 Dẫn xuất và kế thừa ()
 . Ba lớp trên giống nhau về:
 – Thuộc tính:
 Name
 Age
 gender
 – Phương thức
 set_name, get_name
 set_age, get_age
 set_gender, get_gender
 . Khác nhau: lớp Student, Lecture có bổ sung thêm các 
 thuộc tính và phương thức
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 9
 Person
 . Quan hệ lớp
 name: string Lớp cơ sở
 age: int
 gender: string
 set_name
 get_name
 set_age
 get_age
 set_gender
 get_gender
 display
 Quan hệ dẫn xuất
 Lớp dẫn xuất
 Student Lecture
 class: string Faculty: string
 ID: int Telnumber: int
 set_class set_faculty
 get_class get_faculty
 set_ID set_telnumber
 get_ID get_telnumber
 display display
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 10
Thực hiện (Lớp Person)
 //Khai báo lớp Person trong file person.h
 #include 
 #include 
 using namespace std;
 class Person{
 string name;
 int age;
 string gender;
 public:
 Person(string,int,string);
 string get_name();
 void set_name(string);
 int get_age();
 void set_age(int);
 string get_gender();
 void set_gender(string);
 void display()
 };
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 11
 Thực hiện (Lớp Person)
 //Định nghĩa lớp Person trong file person.cpp
 #include “person.h”
 Person::Person(string _name,int _age, string _gender){
 name = _name; 
 age = _age; 
 sex = _gender;
 }
 string Person::get_name(){ return name;}
 void Person::set_name(string _name){ name = _name;}
 int Person::get_age(){ return age;}
 void Person::set_age(int _age){ age = _age;}
 string Person::get_gender(){ return gender;}
 void Person::set_gender(string _gender){gender = _gender;}
 void Person::display(){
 cout<<“Person:\n”;
 cout<<“Name:\t”<<name<< endl;
 cout<<“Age:\t”<<age<< endl;
 cout<<“Gender:\t”<<gender<< endl;
 }
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 12
Thực hiện (Lớp Student)
 //Khai báo lớp Student trong file student.h
 class Student: public Person
 {
 string lop;
 int id;
 public:
 Student(string, int, string, 
 string,int);
 void set_class(string c);
 string get_class();
 void set_id(int i);
 int get_id();
 void display();
 };
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 13
Thực hiện (Lớp Student)
//Định nghĩa lớp Student trong file student.cpp
#include “student.h”
Student::Student(string _n,int _a, string _g, string 
_l,int _id)
 :Person(_n,_a,_g){
 lop = _l;
 id = _id;
}
void Student:: set_class(string c){lop = c;}
string Student:: get_class(){ return lop;}
void Student:: set_id(int i){id = i;}
int Student:: get_id(){return id;}
void Student:: display(){
 Person::display();
 cout<<"class:\t"<<lop<<endl;
 cout<<"ID:\t"<<id<<endl;
}
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 14
Thực hiện (Lớp Lecture)
 // Khai báo lớp Lecture trong file lecture.h
 class Lecture:public Person{
 string faculty;
 int telnumber;
 public:
 Lecture(string,int, string, string,int); 
 void set_faculty(string f);
 string get_faculty();
 void set_telnumber(int tel);
 int get_telnumber(); 
 void display();
 };
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 15
 Thực hiện (Lớp Lecture)
//Định nghĩa lớp Lecture trong file lecture.cpp
#include “lecture.h”
Lecture::Lecture(string _n,int _a, string _g, string _f,int
_t)
 :Person(_n,_a,_g){
 faculty = _f;
 telnumber = _t;
}
void Lecture:: set_faculty(string f){faculty = f;}
string Lecture:: get_faculty(){ return faculty;}
void Lecture:: set_telnumber(int tel){telnumber = tel;}
int Lecture:: get_telnumber(){return telnumber;}
void Lecture:: display(){
 Person::display();
 cout<<"Faculty:\t"<<faculty<<endl;
 cout<<"Telephone Number:\t"<<telnumber<<endl;
}
 Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 16
 . Chương trình minh họa sử dụng 1
//Thực hiện trong file main.cpp
#include 
#include “person.h”
#include “student.h”
#include “lecture.h”
void main(){
 Person per("John",21,"man");
 Student stu("Marry",22,"woman","Electronics1-K53",20080001);
 Lecture lec("Michel",22,"man","Electronics Engineering",123456789);
 cout<<“Person:\t”<<per.get_name()<<“\t”<<per.get_age()
 <<“\t”<<per.get_gender()<<“\n\n”;
 cout<<“Student:\t”<<stu.get_name()<<“\t”<<stu.get_age()<<“\t”
 <<stu.get_gender()<<“\t”<<stu.get_class()<<“\t”<<stu.get_id()<<“\n\n”;
 cout<<“Lecture:\t”<<lec.get_name()<<“\t”<<lec.get_age()<<“\t”
 <<lec.get_gender() <<“\t”<<lec.get_faculty()<<“\t”
 <<lec.get_telnumber()<<“\n\n”;
}
 . Kết quả chạy chương trình
 Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 17
. Chương trình minh họa sử dụng 2
//Thực hiện trong file main.cpp
void main(){
 Person per("John",21,"man");
 Student stu("Marry",22,"woman","Electronics1-K53",20080001);
 Lecture lec("Michel",22,"man","Electronics Engineering",123456789);
 per.display();
 stu.display();
 lec.display();
}
. Kết quả chạy chương trình
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 18
 Các dạng dẫn xuất/thừa kế
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 19
 Tóm lược
 . Lớp dẫn xuất có hai mục đích cơ bản
 – Mở rộng các tính năng của lớp cơ sở
 – Thừa hưởng các thuộc tính và phép toán của lớp cơ sở
 – Cụ thể hóa các phép toán qua những phương thức khác nhau
 . Ưu điểm của cơ chế thừa hưởng
 – Xây dựng một mô hình phần mềm hướng đối tượng dễ hiểu
 – Tiết kiệm được công việc thực hiện qua sử dụng lại các lớp cơ 
 sở
 – Hạn chế lỗi qua cơ chế thừa hưởng
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 20
 Hàm tạo và hàm tạo bản sao
 class A{ class B: public A{
 int n; int m;
 int *data; public:
 public: B(int i=0, int j=0):A(i),m(j){}
 A(int i = 0):n(i){ B(const B& b):A(b), m(b.m){}
 data = new int[n]; ...
 } };
 A(const A& a){...}
 ~A(){ delete [] data;}
 ...
 };
 . Hàm tạo, hàm tạo bản sao không thừa hưởng được 
 mà chỉ có thể gọi ở phần liệt kê khởi tạo (sau dấu :)
 . B không định nghĩa thêm các biến thành viên thì B 
 vẫn phải định nghĩa hàm tạo; chỉ trừ trường hợp A 
 chỉ có hàm tạo mặc định (do compiler sinh ra) thì nó 
 sẽ gọi hàm tạo mặc định của lớp cơ sở
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 21
 ...
 B():m(j){}//không gọi hàm tạo của lớp cơ sở
 ...
 . Nếu không gọi hàm tạo của lớp cơ sở thì compiler sẽ 
 bổ sung thêm lệnh gọi hàm tạo mac dinh của lớp cơ 
 sở. A()
 . Nếu không định nghĩa hàm tạo bản sao ở lớp dẫn 
 xuất thì hàm do compiler tạo ra sẽ gọi hàm tự sao 
 chép của lớp cơ sở và phần còn lại sẽ sao chép theo 
 kiểu từng bít của các biến B định nghĩa thêm.
 . Nếu trong B không định nghĩa thêm biến có sử dụng 
 bộ nhớ động thì không cần thiết viết hàm tạo bản 
 sao cho B.
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 22
 Hàm hủy
 . Nếu không định nghĩa hàm hủy cho lớp dẫn xuất B 
 thì compiler sẽ tự sinh ra và gọi hàm hủy của A
 . Quá trình hủy ngược lại với quá trình tạo, phần tạo 
 trước của lớp cơ sở sẽ được hủy sau.
 . Sự cần thiết định nghĩa lại hàm hủy ở lớp dẫn xuất 
 cũng giống như một lớp bình thường
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 23
 Hàm toán tử gán
 . Nếu không định nghĩa lại hàm toán tử gán thì 
 compiler sẽ tự sinh ra cho ta hàm toán tử gán, hàm 
 này sẽ gọi hàm toán tử gán của lớp cơ sở và sau đó sẽ 
 gán từng bít một của các biến mà lớp dẫn xuất định 
 nghĩa thêm.
 . Nếu ta định nghĩa lại ở lớp dẫn xuất thì cũng cần gọi 
 hàm toán tử gán của lớp cơ sở
 . Việc cần định nghĩa lại hàm toán tử gán cũng giống 
 như việc cần định nghĩa lại hàm tạo bản sao
 ...
 B& operator=(const B& b) {
 A::operator=(b); //hàm toán tử gán của lớp cơ sở
 m = b.m; //biến B định nghĩa thêm
 return (*this);
 }
 ...
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 24
 5.2 Hàm ảo và cơ chế đa hình/đa xạ
 . Nghiên cứu chương trình minh họa sử dụng các lớp 
 Person, Student và Lecture ở trên
//Thực hiện trong file main.cpp
void main(){
 Person *per = new Person ("John",21,"man");
 Person *stu = new Student ("Marry",22,"woman",
 "Electronics1-K53",20080001);
 Person *lec = new Lecture ("Michel",22,"man",
 "Electronics Engineering",123456789);
 per->display();
 stu->display();
 lec->display();
 delete per; delete stu; delete lec;
} 
 Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 25
 5.2 Hàm ảo và cơ chế đa hình/đa xạ(...)
 . Kết quả chạy chương trình
 Tại sao lại 
 vậy?
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 26
 5.2 Hàm ảo và cơ chế đa hình/đa xạ(...)
 . Nguyên nhân: Trong quá trình liên kết, lời gọi các 
 hàm và hàm thành viên thông thường được chuyển 
 thành các lệnh nhảy tới địa chỉ cụ thể của mã thực 
 hiện hàm => "liên kết tĩnh“
 . Giải pháp sử dụng hàm ảo
 . Hàm ảo là hàm thành viên của một lớp mà phần mã 
 thực hiện nó được xác định đúng cho đối tượng định 
 nghĩa nó trong khi chương trình chạy, kể cả trong 
 trường hợp ta gọi hàm đó qua một con trỏ vào một lớp 
 cơ sở (lớp cơ sở đã khai báo hàm ảo đó).
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 27
 5.2 Hàm ảo và cơ chế đa hình/đa xạ(...)
 . Sửa lại chương trình như sau:
 – Chỉ cần khai báo hàm display của lớp person là hàm ảo bằng 
 cách thêm chữ virtual vào trước hàm display trong phần 
 khai báo lớp Person
 – Các phần còn lại giữ nguyên
 //Khai báo lớp Person trong file person.h
 #include 
 #include 
 using namespace std;
 class Person{
 ... //giữ nguyên như cũ
 public:
 ... //giữ nguyên như cũ
 virtual void display()
 };
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 28
 . Kết quả chạy chương trình
 Như mong 
 đợi chưa?
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 29
 5.3 Hàm thuần ảo, lớp thuần ảo
 . Hàm thuần ảo (hàm trừu tượng) là hàm ảo có khai 
 báo mà không có định nghĩa
 . Lớp thuần ảo (lớp trừu tượng) là lớp có ít nhất một 
 hàm thuần ảo
 . Lớp thuần ảo chỉ là giao diện, không sử dụng được
 . Bắt buộc phải định nghĩa lớp dẫn xuất
 . Như vậy:
 – Phân biệt rõ phần giao diện và phần thực hiện
 – Có thể công khai phần giao diện cho người sử dụng, che giấu 
 phần thực hiện
 – Có thể thay đổi phần thực hiện mà không ảnh hưởng đến 
 cách sử dụng
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 30
//Ví dụ 1 //Ví dụ 2
class A{ class A{
 int a; int a;
public: public:
 virtual void f() = 0; virtual void f() = 0;
}; };
class B: public A{ class B: public A{
 int b; int b;
public: public:
 void f(){ b = 0;} void f(){ b = 0;}
 void h(){} void h(){}
}; };
void main(){ void main(){
 A a; //Lỗi B b; //OK
 ... b.f(); //OK
} ...
 }
 Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 31
 Hàm hủy là hàm ảo
 . Hàm hủy lớp cơ sở là ảo thì có thể dùng con trỏ lớp cơ 
 sở để hủy đối tượng lớp dẫn xuất
 . Nếu hàm hủy lớp cơ sở là ảo thì hàm hủy lớp dẫn 
 xuất cũng tự động là ảo.
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 32
 class A{
 int n;
 int *data;
 public:
 A(int _n):n(_n){ data = new int[n];}
 ~A(){ delete [] data;}
 };
 class B: public A{
 int m;
 int* data;
 public:
 B(int _n,int _m):A(_n),m(_m){data = new int[m];}
 ~B(){ delete [] data; }
 };
 void main(){
 A *pb = new B(5,5); Gọi ~A(); không gọi 
 ... ~B() không hủy 
 delete pb;
 pb->data
 }
 . Giải pháp thêm virtual trước ~A()
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 33
 class A{
 int n;
 int *data;
 public:
 A(int _n):n(_n){ data = new int[n];}
 virtual ~A(){ delete [] data;}
 };
 class B: public A{
 int m;
 int* data;
 public:
 B(int _n,int _m):A(_n),m(_m){data = new int[m];}
 ~B(){ delete [] data; }
 };
 void main(){
 A *pb = new B(5,5);
 ... Gọi ~B(); sau đó gọi 
 delete pb; ~A()
 }
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 34
 5.3 Kiểm soát truy nhập
 a) Kế thừa dạng public
 class A{
 int a;
 class B: public A{
 void g();
 ...
 Public:
 };
 int n;
 void f();
 Protected:
 char c;
 void h();
 };
 . Tất cả các thành viên public và protected của A giữ 
 nguyên quyền kiểm soát truy nhập
 – public của A public của B
 – protected của A protected của B
 – Private của A B thừa hưởng nhưng không truy nhập trực 
 tiếp được
 . Friend không thừa hưởng được; friend của lớp nào thì 
 chỉ có ý nghĩa của lớp đó
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 35
 b) Kế thừa dạng protected
 class A{
 ...
 };
 class B: protected A{
 ...
 };
 . public của A protected của B
 . protected của A private của B
 . B thừa hưởng các private của A nhưng không truy nhập trực 
 tiếp được.
 class C: public B{
 void g2(){
 h(); //ok;mac du h() trở thành private của B
 f(); //OK; f(x) trở thành protected của B
 }
 };
 void func(C c){
 c.n = 1; //LỖI; n là protected của B
 c.f(); //LỖI; f() là protected của B
 }
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 36
 c) Kế thừa dạng private
 class A{
 ...
 };
 class B: private A{
 ...
 };
 . Mặc định private ta có thể viết
 class A{
 ...
 };
 class B: A{
 ...
 };
 . Tất cả các thành viên pubic và protected của A trở 
 thành private của B
 . B thừa kế các private của A nhưng không truy nhập 
 trực tiếp được
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 37
 5.4 Tương thích kiểu
 class A{
 ...
 };
 class B: public A{
 ...
 };
 . Ví dụ 1 Gọi hàm tự sao chép 
 A::A(const&)
 B b;
 A a = b; //OK
 void f(A a){...}
 f(b); //OK
 . Ví dụ 2
 A a;
 B b1 = a; //LỖI
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 38
 . Ví dụ 2
 void g(A *pa){...} Đ/c của b có kiểu là con trỏ B (B*).
 void main(){ Con trỏ vào B sẽ được tự động 
 B b; chuyển đổi kiểu sang con trỏ A*
 g(&b); //OK
 }
 . Kiểu con trỏ hoặc tham chiếu vào một kiểu dẫn xuất 
 có thể tự động chuyển đổi thành con trỏ/tham chiếu 
 vào kiểu cơ sở, ngược lại không đúng.
 A a;
 B b;
 A *pa = &b; //OK; tự động chuyển đổi kiểu
 B *pb = pa; //LỖI;
 pb = (B*)pa; // khi biên dịch thì compiler không phát hiện ra lõi; khi 
 chạy có lỗi
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 39
 Bài tập
 . Thực hiện lại trên máy tính các lớp Person, Student, 
 Lecture theo sườn bài giảng.
Chương 5: Dẫn xuất/thừa kế và đa hình/đa xạ 40

File đính kèm:

  • pdfbai_giang_lap_trinh_chuong_5_dan_xuatthua_ke_va_da_hinhda_xa.pdf