Hàm dựng trong kế thừa
Khi một đối tượng thuộc lớp con được gọi:
Hàm dựng của lớp cha sẽ tự động được gọi thực
hiện trước
Sau đó, hàm dựng của lớp con sẽ được thực hiện.
Trong hàm dựng của lớp con, chúng ta có thể chỉ
định hàm dựng nào của lớp cha sẽ được gọi thực
hiện. Nếu không, hàm dựng mặc định của lớp cha
sẽ được gọi
GVCN gv();
//Hàm dựng GiaoVien() sẽ được gọi trước
//Sau đó tới hàm GVCN()
55 trang |
Chia sẻ: thanhle95 | Lượt xem: 770 | Lượt tải: 1
Bạn đang xem trước 20 trang tài liệu Bài giảng Phương pháp lập trình hướng đối tượng - Tuần 7: Hàm dựng, hàm hủy, ba vấn đề con trỏ trong kế thừa - Phạm Tú San, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Tuần 07:
Hàm dựng, Hàm hủy,
Ba vấn đề con trỏ trong kế thừa
QUAN HỆ GIỮA CÁC ĐỐI TƯỢNG
Liên hệ giữa các lớp đối tượng (chương 6)
Quan hệ bao hàm (HAS-A)
Bao hàm phụ thuộc (composition)
Bao hàm độc lập (aggregation)
Tổng quát hóa (IS-A)
Friend
Quan hệ phụ thuộc
4Nội dung
Hàm dựng trong kế thừa.
Hàm hủy trong kế thừa.
Ba vấn đề về con trỏ trong kế thừa.
Bài tập.
Hàm dựng trong kế thừa
Xây móng đến mái
Trình tự tạo lập đối tượng kế thừa:
Nhà được xây từ móng đến mái.
Khái niệm được định nghĩa từ thấp đến cao.
Đối tượng được tạo lập từ lõi đến vỏ.
Thành phần kế thừa từ lớp cơ sở được tạo trước.
Đối tượng kế thừa
Thành phần mới
Đối tượng cơ sở
Thành phần cơ sở
Tạo lập từ lõi đến vỏ
6Hàm dựng trong kế thừa
Khi một đối tượng thuộc lớp con được gọi:
Hàm dựng của lớp cha sẽ tự động được gọi thực
hiện trước
Sau đó, hàm dựng của lớp con sẽ được thực hiện.
Trong hàm dựng của lớp con, chúng ta có thể chỉ
định hàm dựng nào của lớp cha sẽ được gọi thực
hiện. Nếu không, hàm dựng mặc định của lớp cha
sẽ được gọi
GVCN gv();
//Hàm dựng GiaoVien() sẽ được gọi trước
//Sau đó tới hàm GVCN()
Ví dụ hàm dựng trong kế thừa
class GiaoVien
{
private:
string mHoTen;
float mMucLuong;
public:
GiaoVien();
GiaoVien(string sHoTen,float
fMucLuong);
};
class GVCN : public GiaoVien
{
private:
string mLopCN;
public:
GVCN();
GVCN(string ten, float luong,
string lopcn);
};
Ví dụ hàm dựng trong kế thừa
GVCN gv2(“Nguyen A”, 50, “11A”);
//Hàm dựng GiaoVien(“Nguyen A”, 50,) sẽ được
gọi trước
GiaoVien::GiaoVien() //1
{
mHoTen = “”;
mMucLuong = 0;
}
GiaoVien::GiaoVien(string ten, float
luong) //2
{
mHoTen = ten;
mMucLuong = luong;
}
GVCN::GVCN() //3
{
mLopCN = “”;
}
GVCN::GVCN(string ten, float luong,
string lopcn): GiaoVien(ten, luong)
//4
{
mLopCN = lopcn;
}
GVCN gv1;
//Hàm GiaoVien() sẽ được gọi
trước
Hàm dựng trong lớp dẫn xuất
Lưu ý:
Hàm dựng của lớp dẫn xuất chỉ
được phép chỉ định hàm dựng
nào của lớp cơ sở trực tiếp của
nó thực hiện chứ không thể can
thiệp đến các lớp cơ sở kế thừa
xa hơn.
A
B
C
X
10
Nội dung
Hàm dựng trong kế thừa.
Hàm hủy trong kế thừa.
Ba vấn đề về con trỏ trong kế thừa.
Bài tập.
11
Hàm hủy trong kế thừa
Thứ tự huỷ đối tượng kế thừa
Ngược lại với quá trình tạo
Phần vỏ bên ngoài được huỷ trước
Lớp con sẽ bị huỷ trước lớp cha
Mỗi lớp chỉ có 1 hàm huỷ -> không cần chỉ định
Đối tượng kế thừa
Thành phần mới
Đối tượng cơ sở
Thành phần cơ sở
Hủy từ vỏ đến lõi
12
Hàm hủy trong kế thừa
Khi một đối tượng thuộc lớp con bị hủy:
Hàm hủy của lớp con được gọi thực hiện trước
Sau đó mới đến hàm hủy của lớp cha.
GiaoVien::~GiaoVien()
{
//huỷ giáo viên
}
GVCN::~GVCN()
{
//huỷ GVCN
}
void main()
{
GVCN a;
}
~GVCN() được gọi trước
~GiaoVien() được gọi sau
13
Nội dung
Hàm dựng trong kế thừa.
Hàm hủy trong kế thừa.
Ba vấn đề về con trỏ trong kế thừa.
Bài tập.
14
Lớp có thuộc tính con trỏ?
Phải thêm vào lớp “ba ông lớn”:
Hàm hủy.
Hàm dựng sao chép.
Toán tử gán.
Lớp kế thừa có thuộc tính con trỏ?
Ba vấn đề con trỏ trong kế thừa
15
Ba vấn đề con trỏ trong kế thừa
class GiaoVien
{
private:
char *m_strHoTen;
float m_fMucLuong;
int m_iSoNgayNghi;
public:
GiaoVien(char *strHoTen,
float fMucLuong,
int iSoNgayNghi);
};
class GVCN : public GiaoVien
{
private:
char *m_strLopCN;
public:
GVCN(char *strHoTen,
float fMucLuong,
int iSoNgayNghi,
char *strLopCN);
};
16
Ba vấn đề con trỏ trong kế thừa
Luật “ba ông lớn” trong kế thừa
Lớp kế thừa có thuộc tính con trỏ, phải kèm theo:
Hàm hủy: thu hồi bộ nhớ phần vỏ.
Hàm dựng sao chép: sao chép bộ nhớ phần vỏ.
Toán tử gán: sao chép bộ nhớ phần vỏ.
Kích hoạt “ba ông lớn” của lớp cơ sở:
Hàm hủy: tự động!!.
Hàm dựng sao chép: chỉ định
hàm dựng sao chép lớp cơ sở.
Toán tử gán: thực hiện toán tử gán
lớp cơ sở trước.
Hàm dựng sao chép
GVCN::GVCN( const GVCN &g): GiaoVien( g )
{
m_strLopCN = new char[strlen(g.strLopCN) + 1];
strcpy(m_strLopCN, g.strLopCN);
}
Toán tử gán trong kế thừa
Toán tử gán cho lớp con
Gọi toán tử gán của lớp cha
Gán các thành phần riêng của lớp con
Toán tử gán trong kế thừa – lớp chứa con trỏ
B& B::operator=(const B& src)
{
if (this == &src)
return *this;
A::operator=(src);
delete [] this->ptr;
this->iSize = src.iSize;
ptr = new int [this->iSize];
for (int i=0; iiSize; ++i)
this->ptr[i] = src.ptr[i];
return *this;
}
A
B
20
Tóm tắt
Hàm dựng trong kế thừa:
Đối tượng kế thừa được tạo lập từ lõi đến vỏ.
Hàm dựng lớp cơ sở gọi trước, tạo phần lõi.
Hàm dựng lớp kế thừa gọi sau, tạo phần vỏ.
Lớp kế thừa có thể chỉ định hàm dựng cơ sở.
Hàm hủy trong kế thừa:
Đối tượng kế thừa được hủy ngược lại với tạo lập.
Hàm hủy lớp kế thừa gọi trước, hủy phần vỏ.
Hàm hủy lớp cơ sở gọi sau, hủy phần lõi.
21
Tóm tắt
Ba vấn đề con trỏ trong kế thừa:
Lớp kế thừa có thuộc tính con trỏ:
Xây dựng “ba ông lớn” cho phần vỏ.
Kích hoạt “ba ông lớn” của phần lõi.
Bài tập 10.1
};
class A
{ public:
A(int iX) { } //(1)
};
class B: public A
{ public:
B(): A(0) { } //(2)
B(int iX, int iY): A(iX) { } //(3)
};
class C: public B
{ public:
C() { } //(4)
C(int iZ) { } //(5)
C(int iX, int iY, int iZ): B(iX, iY) { }
//(6)
Cho biết thứ tự gọi hàm dựng với:
a) void main() { C c(1, 2, 3); }
b) void main() { C c(4); }
c) void main() { C c; }
23
Bài tập 10.2
class GiaoVien
{
private:
string mHoTen;
float mMucLuong;
int mSoNgayNghi;
public:
float TinhLuong() { return mMucLuong – mSoNgayNghi * 10000; }
};
class GVCN: public GiaoVien
{
private:
string mLopCN;
};
Bài tập 10.2 (tt)
Xây dựng hàm dựng cho lớp GiaoVien, khởi tạo với:
- Họ tên cho trước, mức lương 500000, số ngày nghỉ 0.
- Họ tên, mức lương cho trước, số ngày nghỉ 0.
- Họ tên, mức lương, số ngày nghỉ cho trước.
Xây dựng hàm dựng cho lớp GVCN, khởi tạo với:
- Họ tên, lớp chủ nhiệm cho trước, mức lương 500000,
số ngày nghỉ 0.
- Họ tên, mức lương, lớp chủ nhiệm cho trước, số ngày
nghỉ 0.
- Họ tên, mức lương, số ngày nghỉ, lớp chủ nhiệm cho
trước.
Bài tập 10.3
Con vật:
Thông tin: tên, cân nặng, tuổi.
Hành động: di chuyển, ăn.
Mèo:
Thông tin: tên, cân nặng, tuổi, màu mắt.
Hành động: di chuyển, ăn, bắt chuột.
Gà:
Thông tin: tên, cân nặng, tuổi, màu lông.
Hành động: di chuyển, ăn, đẻ trứng.
Thiết kế và vẽ sơ đồ lớp cho các đối tượng trên
Bài 10.4
Công ty ABC cần xây dựng một ứng dụng quản lý nhân
sự và tính lương cho nhân viên trong công ty như sau:
Quản lý thông tin nhân viên (mã, họ tên, ngày sinh, địa chỉ)
Tính lương cho nhân viên
Hiện công ty có 3 loại nhân viên và cách tính lương như
sau:
Nhân viên sản xuất : số sản phẩm * 20.000 đ
Nhân viên công nhật : số ngày * 50.000 đ
Nhân viên quản lý : hệ số lương * lương cơ bản
Hãy viết chương trình quản lý và tính tổng lương nhân
viên (có thiết kế và vẽ sơ đồ lớp)
Thao khảo
Slide PPLTHDT của
Thầy Nguyễn Minh Huy
Thầy Đinh Bá Tiến
Tuần 07:
Hàm ảo – Đa xạ
Nội dung
Con trỏ đối tượng
Hàm ảo
Liên kết tĩnh – liên kết động
Đa xạ
Con trỏ tới đối tượng
Con trỏ đối tượng
Dùng từ khóa new để cấp vùng nhớ cho đối
tượng con trỏ
*
SinhVien *sv;
GiaoVien *gv;
SinhVien *sv = new SinhVien();
GiaoVien *gv = new GiaoVien(“Nguyen A”, 40);
Con trỏ tới đối tượng
Truy xuất tới các thuộc tính/ phương thức bằng
toán tử ->
Khi có new để xin vùng nhớ thì phải có delete
để dọn dẹp/ trả lại vùng nhớ
sv->Nhap();
sv->Xuat();
coutLayTen();
delete sv;
delete gv;
Sự tương thích kiểu đối tượng
Một biến đối tượng của lớp cha có thể giữ một
đối tượng của lớp con
-> Con trỏ của lớp cha có thể được gán bằng địa
chỉ của 1 đối tượng có kiểu dữ liệu là lớp con
Ví dụ:
DongVat *pDongVat;
Meo c;
pDongVat= &c; //OK
DongVat
Meo
Đổi kiểu ngầm định trong kế thừa
Việc truyền 1 biến có kiểu dữ liệu là lớp con vào
1 hàm có đối số là tham chiếu đến kiểu dữ liệu
của lớp cha là hợp lệ và việc chuyển đổi kiểu dữ
liệu là ngầm định
void DongVat::xuly(const DongVat &a);
Meo c;
DongVat::xuly(c); //không cần ép kiểu cho c
//Mèo là 1 con vật nên việc truyền vào như thế là hợp lệ
Liên kết tĩnh (static binding)
Xét trường hợp sau:
Class A có hàm print()
Class B viết lại hàm print()
Class C cũng viết lại hàm print()
int main() {
C c;
B b;
c.print();
c.B::print(); // print() của
b.print(); // print() của
}
A
void print();
B
void print();
C
void print();
Liên kết tĩnh (static binding)
Xét trường hợp sau:
Class A có hàm print()
Class B viết lại hàm print()
Class C cũng viết lại hàm print()
int main() {
C c;
B b;
c.print(); // print() của C
c.B::print(); // print() của B
b.print(); // print() của B
}
A
void print();
B
void print();
C
void print();
Liên kết tĩnh (tt)
Xét tiếp trường hợp sau
int main() {
A a;
B b;
C c;
A *pc, *pb;
pc = &c;
pb = &b;
pc->print(); // print() của
pb->print(); // print() của
}
A
void print();
B
void print();
C
void print();
Liên kết tĩnh (static binding)
Xét trường hợp sau:
Class A có hàm print()
Class B viết lại hàm print()
Class C cũng viết lại hàm print()
int main() {
C c;
B b;
c.print(); // print() của
c.B::print(); // print() của
b.print(); // print() của
}
A
void print();
B
void print();
C
void print();
Liên kết tĩnh (static binding)
Xét trường hợp sau:
Class A có hàm print()
Class B viết lại hàm print()
Class C cũng viết lại hàm print()
int main() {
C c;
B b;
c.print(); // print() của
c.B::print(); // print() của
b.print(); // print() của
}
A
void print();
B
void print();
C
void print();
Liên kết tĩnh (static binding)
Xét trường hợp sau:
Class A có hàm print()
Class B viết lại hàm print()
Class C cũng viết lại hàm print()
int main() {
C c;
B b;
c.print(); // print() của C
c.B::print(); // print() của B
b.print(); // print() của B
}
A
void print();
B
void print();
C
void print();
Hàm ảo (virtual function)
Hàm ảo: ở lớp cha, các hàm thành viên muốn
trở thành hàm ảo bằng cách thêm từ khóa
virtual vào trước tên hàm trong khai báo.
Ở các lớp con, nội dung các hàm ảo được định
nghĩa lại theo đặc thù của lớp đó.
Chú ý: có thể không cần viết lại từ khóa virtual
ở lớp con
virtual void print();
Liên kết động (dynamic binding)
Khi dùng hàm ảo, trình biên dịch sẽ đảm bảo việc sử dụng hàm
thành viên tương ứng với đối tượng đang sử dụng. Ví dụ nếu
print() là hàm ảo:
A
virtual void print();
B
void print();
C
void print();
}
int main() {
A a;
B b;
C c;
A *pc, *pb;
pc = &c;
pb = &b;
pc->print(); // print() của C
pb->print(); // print() của B
Lưu ý: hàm ảo & liên kết động
Ngoài việc sử dụng từ khóa virtual, liên kết
động chỉ xảy ra khi đối tượng được xử lý thông
qua biến con trỏ hoặc tham chiếu.
Riêng đối với lớp cha, hàm ảo vẫn được sử
dụng như 1 hàm bình thường.
Nếu ở lớp con, hàm ảo của lớp cha không được
định nghĩa lại thì trình biên dịch sẽ chọn hàm
tương ứng mà được định nghĩa gần nhất trong
dây chuyền kế thừa.
Tính chất đa xạ (polymorphism)
Thông qua 1 giao tiếp, có nhiều cách cài đặt
khác nhau cho 1 hàm
Hành động tương ứng của đối tượng sẽ được
gọi thực hiện
DongVat
Meo GaCho Bo
Ví dụ đa xạ
Hàm dựng ảo?; Hàm hủy ảo?
KHÔNG có hàm dựng ảo
Hàm hủy NÊN được viết là hàm ảo
Vì nếu lớp này được kế thừa nhưng hàm hủy vẫn sử
dụng theo liên kết tĩnh sẽ dễ dẫn đến các vấn đề về
xử lý bộ nhớ
A
virtual ~A();
B
virtual ~B();
C
virtual ~C();
int main() {
A *pc = new C();
A *pb = new B();
delete pc;
delete pb;
} hàm hủy của B và C sẽ
được gọi tương ứng
Hàm thuần ảo (pure virtual)
Xét lại bài DongVat, giả sử ta có hàm ảo:
virtual void Keu();
// thể hiện tiếng kêu của con vật
DongVat
Meo GaCho Bo
Hàm thuần ảo (tt)
Khi đó
keu() có thể được khai báo lại như sau:
int main()
}
{
DongVat a; //không thực tế
a.keu(); //không có ý nghĩa
virtual void keu() = 0;
Hàm thuần ảo & lớp trừu tượng
Hàm keu() trở thành hàm thuần ảo
Khi lớp chứa hàm thuần ảo thì được gọi là lớp
trừu tượng (abstract class) và đối tượng sẽ
không được khởi tạo từ lớp đó:
virtual void keu() = 0;
DongVat a; //báo lỗi biên dịch
Hàm thuần ảo
BÀI TẬP
Bài tập 11.1
class GiaoVien
{
private:
string mHoTen;
float mMucLuong;
int mSoNgayNghi;
public:
float TinhLuong() { return mMucLuong – mSoNgayNghi * 10000; }
};
class GVCN: public GiaoVien
{
private:
string mLopCN;
};
Xây dựng lớp trường học cho phép quản lý danh sách
các giáo viên (sử dụng đa xạ)
Nhập, xuất
Tính tổng lương, tìm giáo viên có lương cao nhất
Bài tập 11.2
Sử dụng đa xạ làm bài tập sau
Công ty ABC cần xây dựng một ứng dụng quản lý nhân
sự và tính lương cho nhân viên trong công ty như sau:
Quản lý thông tin nhân viên (mã, họ tên, ngày sinh, địa chỉ)
Tính lương cho nhân viên
Hiện công ty có 3 loại nhân viên và cách tính lương như
sau:
Nhân viên sản xuất : số sản phẩm * 20.000 đ
Nhân viên công nhật : số ngày * 50.000 đ
Nhân viên quản lý : hệ số lương * lương cơ bản
Hãy viết chương trình quản lý và tính tổng lương nhân
viên (có thiết kế và vẽ sơ đồ lớp)
Bài tập 11.3
Một nông trại chăn nuôi có 3 loại gia súc: bò, cừu, và dê.
Mỗi loại gia súc đều có thể sinh con, cho sữa và phát ra
tiếng kêu riêng của chúng. Khi đói, các gia súc sẽ phát ra
tiếng kêu để đòi ăn. Sau một thời gian chăn nuôi, người
chủ nông trại muốn thống kê xem trong nông trại có bao
nhiêu gia súc ở mỗi loại, tổng số lit sữa mà tất cả các gia
súc của ông đã cho.
Áp dụng kế thừa và đa hình, xây dựng chương trình cho
phép người chủ nông trại nhập vào số lượng gia súc ban
đầu ở mỗi loại.
Một hôm người chủ nông trại đi vắng, tất cả gia súc
trong nông trại đều đói. Hãy cho biết những tiếng kêu
nghe được trong nông trại.
Bài tập 11.3 (tt)
Chương trình sẽ đưa ra thống kê các thông tin
người chủ mong muốn (nêu trên) sau một lứa sinh
và một lược cho sữa của tất cả gia súc. Biết rằng:
Tất cả gia súc ở mỗi loại đều sinh con.
Số lượng sinh của mỗi gia súc là ngẫu nhiên.
Tất cả gia súc ở mỗi loại đều cho sữa.
Số lit sữa mỗi gia súc cho là ngẫu nhiên nhưng trong
giới hạn sau:
Bò: 0 – 20 lit.
Cừu: 0 – 5 lit.
Dê: 0 – 10 lit.
Thao khảo
Slide PPLTHDT của
Thầy Nguyễn Minh Huy
Thầy Đinh Bá Tiến