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

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()

pdf55 trang | Chia sẻ: thanhle95 | Lượt xem: 686 | Lượt tải: 1download
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
Tài liệu liên quan