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ẹ”

Trang 1

Trang 2

Trang 3

Trang 4

Trang 5

Trang 6

Trang 7

Trang 8

Trang 9

Trang 10
Tải về để xem bản đầy đủ
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ạ
ấ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ạ 40File đính kèm:
bai_giang_lap_trinh_chuong_5_dan_xuatthua_ke_va_da_hinhda_xa.pdf

