Bài giảng môn Lập trình hướng đối tượng - Chương 5: Sự kế thừa (inheritance)

pdf 50 trang phuongnguyen 2080
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng môn Lập trình hướng đối tượng - Chương 5: Sự kế thừa (inheritance)", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfbai_giang_mon_lap_trinh_huong_doi_tuong_chuong_5_su_ke_thua.pdf

Nội dung text: Bài giảng môn Lập trình hướng đối tượng - Chương 5: Sự kế thừa (inheritance)

  1. Ch ươ ng 5: S k th a (inheritance) 5.1. Gi i thi u • S k th a là m t khái ni m c a l p trình h ưng đ i t ưng (LTH ðT), nĩ cho phép ta thêm các thành ph n m i vào các l p cĩ s n và/ho c s a đ i hành vi c a các ph ươ ng th c cĩ s n trong m t l p đ t o thành m t l p m i. L p m i này th a h ưng (k th a) m i s c m nh c a l p c ũ, đ ng th i cĩ thêm m t s tính tăng m i mà ta v a thêm vào cho nĩ. Nh ư v y cơ ch k th a giúp vi c nâng c p ch ươ ng trình tr nên d dàng và thu n ti n h ơn: đ t ăng s c m nh c a m t l p cĩ sn (l p này cĩ th d ng file .obj), ta ch c n khai báo m t l p m i đưc k th a t l p cĩ s n, sau đĩ thêm vào l p m i nh ng tính n ăng m i đ t o thành m t l p va cĩ t t c các tính n ăng c a l p c ũ, va cĩ thêm các tính n ăng m i. • Nu khơng cĩ c ơ ch k th a, các l p ch đơn gi n là vi c đĩng gĩi các c u trúc d li u cùng v i các thao tác trên c u trúc d li u đĩ đ t o thành m t ki u d li u tr u t ưng. Tr u t ưng hố d li u m i ch đem l i m t phn s c m nh cho ph ươ ng pháp LTH ðT. Mt ph n s c m nh khác c a LTH ðT n m khái ni m k th a. Chính nh c ơ ch k th a, ta m i cĩ kh n ăng m r ng các l p và s dng l i source code c a các l p. ðĩ chính là m t trong nh ng m c tiêu c a ph ươ ng pháp l p trình h ưng đ i t ưng: giúp vi c nâng c p ch ươ ng trình tr nên d dàng và thu n ti n h ơn (kh n ăng này làm cho LTH ðT cĩ s c m nh v ưt tr i h ơn h n so v i l p trình h ưng th t c). • S k th a cũng là n n t ng c a c ơ ch đa x (polymorphism ), m t c ơ ch r t mnh và đưc dùng r t nhi u c a LTH ðT. • Mi khi cĩ nhu c u c n thêm ho c c n thay th m t s tính n ăng cho m t l p cĩ sn, ta nên ngh ĩ ngay đ n vi c s d ng c ơ ch k th a vì hai lý do sau đây: o Nu l p cĩ s n (l p cha) d ng file object (.obj), ta khơng th hi u ch nh tr c ti p l p đĩ, và do v y c n ph i t o m t l p m i k th a t l p cĩ s n, và thêm tính n ăng ho c hi u ch nh tính n ăng cho l p m i (overriding). Trong tr ưng h p này ta c n cĩ file .h đ nh ngh ĩa l p cĩ s n. o Nu l p cha d ng source code (file .cpp), khi đĩ ta cĩ th thao tác tr c ti p trên l p cĩ s n đ thay đ i tính n ăng cho nĩ. Tuy nhiên làm nh ư v y s nh h ưng đ n nh ng ch ươ ng trình hi n đang s d ng l p này. Cách làm t t h ơn là t o m t l p m i k th a t l p cĩ s n và thay đi tính n ăng cho l p m i, sao cho phù h p v i nhu c u m i. Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 1
  2. 5.2. Xây d ng lp m i v i c ơ ch k th a 5.2.1. Các thu t ng • Khi t o m t l p m i A1 bng cách k th a các tính n ăng t m t l p A cĩ s n, ta nĩi A1 là m t lp d n xu t (derived class ) t l p A. Ta c ũng cĩ th nĩi A1 là lp con (child class , subclass ) c a l p A. Trong tài li u này, các thu t ng subclass, child class, derived class đưc dùng v i ngh ĩa t ươ ng đươ ng. • Lp A đưc g i là lp cha (parent class , super class ) hay lp c ơ s (base class ) ca l p A1. Trong tài li u này, các thu t ng parent class, super class, base class đưc dùng v i ngh ĩa t ươ ng đươ ng. 5.2.2. M r ng l p c ũ (l p cha) • M r ng l p c ũ (l p cha) ngh ĩa là thêm các tính n ăng vào lp cha b ng cách cung cp thêm các ph ươ ng th c m i, các thành ph n d li u m i cho l p cha. • ð làm điu đĩ, ta đ nh ngh ĩa m t l p m i (l p con) và báo cho trình biên d ch bi t r ng l p con này đưc k th a (d n xu t) t l p cha đã cĩ s n. • Lp con s cĩ t t c nh ng gì (data members và methods) mà l p cha cĩ. • Khi đnh ngh ĩa l p con, ta khơng c n ph i đ nh ngh ĩa l i các ph ươ ng th c và thành ph n d li u đưc k th a lp cha, mà ch đ nh ngh ĩa nh ng gì thu c riêng v l p con. 5.2.2.1. Cú pháp base-spec : (spec là vi t t t c a specification) : base-list (chú ý d u hai ch m) base-list : base-specifier base-list , base-specifier base-specifier : complete-class-name virtual access-specifier opt complete-class-name access-specifier virtual opt complete-class-name access-specifier : private protected public • access-specifier : o Ch đ nh m c đ truy xu t (level of access) vào các thành ph n k th a t lp c ơ s (base class) t bên ngồi l p con . o Cĩ th đ ng tr ưc ho c sau t khĩa virtual . o public (k th a public) :  Tt c các thành ph n public c a l p c ơ s tr thành các thành ph n public c a l p con.  Protected c a l p c ơ s tr thành protected c a l p con.  Private c a l p c ơ s tr thành private c a l p con. Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 2
  3. o protected (k th a protected) :  Tt c các thành ph n public và protected c a l p c ơ s tr thành các thành ph n protected c a l p con.  Private c a l p c ơ s tr thành private c a l p con. o private (k th a private) : Tt c các thành ph n c a l p c ơ s (public, protected, private) tr thành các thành ph n private c a l p con. o Ta cĩ th tĩm t t s nh h ưng c a lo i k th a và thu c tính truy xu t ca l p cha lên thu c tính truy xu t c a l p con trong b ng sau: Bng 5.1 : Thu c tính truy xu t public protected private trong l p cha Lo i k t th a public  public  protected  private protected  protected  protected  private private  private  private  private • Mc đ truy xu t mc đ nh vào các thành ph n đưc k th a t l p c ơ s ca l p con đưc m c đ nh là private đi v i classes và public đi v i structures. Unions khơng th cĩ l p c ơ s . • Bên trong subclass, ta cĩ th truy xu t m i thành ph n public và protected ca superclass, nh ưng khơng th truy xu t đưc thành ph n private ca l p superclass (lý do c a điu này là đ tránh vi c ng ưi ta t o ra m t l p con ch đ nh m can thi p vào tính riêng t ư c a các thành phn private bên trong m t l p nào đĩ). • Ví d : // L p cha (l p c ơ s ) class A{ int m_nPrivate; // Th.ph n private protected : int m_nProtected; public : int m_nPublicFromA; }; // L p con (l p d n xu t) struct A1: private A { int m_nPublicFromA1; // Th.phn public void Method() { m_nPrivate = 5; //Error. Can't access //parent's private member m_nProtected = 5; // OK } }; int main() { A1 obj; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 3
  4. obj.m_nPublicFromA = 5; // Error. m_Pub is //private member of A1 class obj.m_nPublicFromA1 = 5; // OK return 0; } • Ta cĩ th đ nh ngh ĩa m t l p th ba là con c a l p con (t c là cháu c a l p cha) và quá trình cĩ th kéo dài khơng gi i h n. • Ta c ũng cĩ th đ nh ngh ĩa nhi u l p con cùng đưc d n xu t t m t l p cha. Hình 5.1 a) b) c) 5.2.2.2. Các ví d // L p cha class CSuper { public : CSuper(); void SomeMethod(); protected : int m_nProtected; private : int m_nPrivate; }; // L p con (ví d 1) class CSub_A : public CSuper { public : CSub_A(); void SomeOtherMethod_A(); }; // L p con (ví d 2) class CSub_B : virtual public CSub_A { public : CSub_B(); void SomeOtherMethod(); }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 4
  5. // L p con (ví d 3) class CSub_C : public virtual CSuper { public : CSub_C(); void SomeOtherMethod(); }; // Lp con (ví d 4) class CSub_D : private virtual CSuper { public : CSub_D(); void SomeOtherMethod(); }; // L p con (ví d 5) class CSub_E : protected virtual CSuper { public : CSub_E(); void SomeOtherMethod(); }; // L p con (ví d 6) class CSub_F : virtual CSuper { public : CSub_F(); void SomeOtherMethod(); }; // L p con (ví d 7) class CSub_G : CSuper { public : CSub_G(); void SomeOtherMethod(); }; int main( int argc, char argv) { CSub_D MySub; MySub.SomeMethod(); // Error MySub.SomeOtherMethod(); // OK CSuper MySuper; MySuper.SomeOtherMethod(); // Error CSuper* SuperPointer = new CSub_D(); // OK return 0; } Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 5
  6. Câu h i: 1) Hãy ki m tra l i, trong các ví d t 1 đn 7, cĩ ví d nào khơng đúng vi cú pháp khơng? Gi i thích? 2) Hãy cho bi t các thành ph n public, protected, private ca các l p sau: CSub_G, CSub_E, CSub_A, CSub_D, CSub_B? 3) Gi i thích vì sao các câu l nh trong hàm main() trên đây là l i ho c khơng li? 4) ðon ch ươ ng trình sau cĩ b l i khi d ch khơng? void CSub_B::SomeOtherMethod() { cout<< "I can access the superclass data member m_nProtected." ; cout<< " Its value is" << m_nProtected << endl; } 5) ðon ch ươ ng trình sau cĩ b l i khi d ch khơng? void CSub_A::SomeOtherMethod_A() { cout<< "The value of m_nPrivate is" <<m_nPrivate; } 5.2.3. ðnh ngh ĩa đè ph ng th c (overriding methods) • ðnh ngh ĩa đè mt ph ươ ng th c (overriding methods) cĩ ngh ĩa là hi u ch nh l i cách hành x c a ph ươ ng th c đưc k th a t l p cha . • ð th c hi n vi c overriding mt ph ươ ng th c trong C++, ta dùng t khĩa virtual . • Ch nh ng ph ươ ng th c đưc khai báo v i t khố virtual trong l p cha (ho c lp t tiên cao h ơn) mi cĩ th đưc đưc đ nh ngh ĩa đè trong l p con. T khĩa virtual ph i đưc đ t đ u ca khai báo. Ph ươ ng th c đưc khai báo v i t khố virtual đưc g i là ph ươ ng th c o hay ph ươ ng th c virtual . • ð đnh ngh ĩa đè mt ph ươ ng th c, ta khai báo l i ph ươ ng th c đĩ trong l p con gi ng h t v tên, giá tr tr v và tham s nh ư đã khai báo nĩ trong l p cha (cĩ th b t khĩa virtual, nh ưng nên dùng đ ch ươ ng trình rõ ràng và sáng s a hơn), sau đĩ ta đnh ngh ĩa l i ph ươ ng th c đĩ trong l p con. • Ta khơng dùng li t khĩa virtual trong ph n đnh ngh ĩa ph ươ ng th c, t khố virtual ch đưc đ t trong ph n khai báo ph ươ ng th c trong đ nh ngh ĩa l p. • Ch các ph ươ ng th c ca m t l p m i đưc khai báo v i t khố virtual , ta khơng th dùng t khố virtual cho các hàm tồn c c ho c hàm t ĩnh. T khố virtual cũng khơng đưc phép dùng cho các thành ph n d li u c a m t l p. • Ví d : o Ph n đ nh ngh ĩa l p ca CSuper và CSub : // ðnh nghĩa l p CSuper class CSuper { Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 6
  7. public : CSuper(); virtual void SomeMethod(); protected : int m_nProtected; private : int m_nPrivate; }; // ðnh ngh ĩa l p CSub class CSub : public CSuper { public : CSub(); virtual void SomeOtherMethod(); }; o Trong ví d này, lp CSuper ch a ph ươ ng th c SomeMethod() . ðnh ngh ĩa c a ph ươ ng th c này nh ư sau (chú ý khơng l p l i t khố virtual khi đnh ngh ĩa ph ươ ng th c): void CSuper::SomeMethod() { cout<< "This is CSuper’s version of SomeMethod()." ; } o Nu mu n đ nh ngh ĩa đè ph ươ ng th c SomeMethod() trong l p con CSub (đưc k th a t l p cha CSuper ), ta ph i khai báo l i ph ươ ng th c này trong ph n đ nh ngh ĩa c a l p CSub nh ư sau (gi ng h t v tên, giá tr tr v, tham s nh ư khai báo nĩ trong l p cha CSuper ): class CSub : public CSuper { public : CSub(); // Overrides CSuper’s SomeMethod() virtual void SomeMethod(); virtual void SomeOtherMethod(); }; o Ti p theo, ta đ nh ngh ĩa l i ph ươ ng th c này trong l p CSub nh ư sau (chú ý khơng dùng l i t khố virtual ): void CSub::SomeMethod() { cout << "This is CSub’s version of SomeMethod()" ; } o Sau khi th c hi n vi c đ nh ngh ĩa l i ph ươ ng th c SomeMethod() trong lp con CSub nh ư trên, ta cĩ th g i ph ươ ng th c này qua đi t ưng thu c Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 7
  8. lp con CSub ho c CSuper , và tùy theo ta dùng đi t ưng nào đ g i, mà ta cĩ cách hành x t ươ ng ng v i đ i t ưng đĩ: // G i ph ươ ng th c SomeMethod() thơng qua l p cha CSuper int main() { CSuper MySuper; // Calls CSuper’s version of SomeMethod(). MySuper.SomeMethod(); } //K t q a trên màn hình: This is CSuper’s version of SomeMethod(). // G i ph ươ ng th c SomeMethod() thơng qua l p con CSub int main() { CSub MySub; MySub.SomeMethod(); //CSub’s version of someMethod() } //K t q a trên màn hình: This is CSub’s version of someMethod(). • Mt con tr (ho c tham chi u) cĩ th tr t i (ho c tham chi u t i) đ i t ưng ca m t l p cha hay m t l p con b t k ỳ c a nĩ (khi l p con k th a protected ho c private thì ph i ép ki u t ưng minh). Nu ta g i overriden method (ph ươ ng th c đã đưc đ nh ngh ĩa đè) thơng qua con tr (ho c tham chi u) này thì tu ỳ theo đi t ưng đang đưc tr t i (ho c tham chi u t i) thu c l p nào, mà phiên b n ca overriden method t ươ ng ng v i l p đĩ s đưc g i. Ví d , n u ta khai báo mt tham chi u đ n l p cha CSuper và gán tham chi u này cho tham chi u t i l p con CSub , khi đĩ vi c g i ph ươ ng th c SomeMethod() thơng qua tham chi u này s d n đ n vi c thi hành phiên b n SomeMethod() ca l p con CSub . Trong tài li u này, ta t m dùng thu t ng “kh n ăng truy xu t thơng tin c a lp con” đ ch đ c tính này. int main() { CSub MySub; CSuper& ref = MySub; //Calls CSub’s version of SomeMethod() ref.SomeMethod(); return 0; } • Ta khơng th truy xu t các thành ph n c a l p con thơng qua con tr (ho c tham chi u) t i l p cha, n u nh ư trong lp cha khơng đ nh ngh ĩa thành ph n đĩ. ðon code sau s b l i khi d ch vì ta g i ph ươ ng th c SomeOtherMethod() thơng qua Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 8
  9. tham chi u đ n l p cha CSuper nh ưng l p cha CSuper li khơng cĩ ph ươ ng th c này (m c dù ph ươ ng th c này đã đưc đ nh ngh ĩa trong l p CSub, và bi n tham chi u đang tr t i đ i t ưng thu c l p CSub ). int main() { CSub MySub; CSuper& ref = MySub; MySub.SomeOtherMethod(); // This is fine. ref.SomeOtherMethod(); // BUG return 0; } • Kh n ăng truy xu t thơng tin c a l p con ch đúng đ i vi con tr và tham chi u, khơng đúng v i các đ i t ưng bình th ưng: Ta cĩ th ép ho c gán mt đ i t ưng ca l p con CSub ti đ i t ưng ca l p cha CSuper , tuy nhiên sau khi gán, đi tưng s khơng cịn kh n ăng truy xu t đ n các thành ph n c a l p con: int main() { CSub MySub; // Assign CSub to a CSuper. CSuper AssignedObject = MySub; // Calls CSuper’s version of SomeMethod() AssignedObject.SomeMethod(); return 0; } • Hi n t ưng b m t kh n ăng truy xu t thơng tin ca lp con nh ư trên đưc g i là slicing . Slicing x y ra khi ta gán m t đ i t ưng c a l p con cho m t đ i t ưng ca l p cha. Slicing khơng x y ra khi ta gán m t con tr (ho c tham chi u) t i đ i tưng c a l p con cho con tr (ho c tham chi u) ti đ i t ưng thu c l p cha. • Khi m t bi n con tr /tham chi u ti mt đ i t ưng thu c lp cha đang th c s tr /tham chi u ti mt đ i t ưng thu c lp con, n u mu n g i phiên b n t ươ ng ng v i l p cha c a overidden method, ta cĩ th dùng tên c a l p cha và tốn t ly ph m vi. Ví d : #include using namespace std; class CBase { public : virtual void Func(){cout<< "Func From CBase" <<endl;} }; class CDerived : public CBase { public : int Func(){cout<< "Func From CDerived" <<endl; return 1;} }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 9
  10. int main() { CDerived d; CBase *pb = &d; pb->Func(); pb->CBase::Func(); // G i phiên b n c a l p cha CBase &rb = d; d.CBase::Func(); // G i phiên b n c a l p cha } Kt qu xu t ra màn hình : Func From CDerived Func From CBase Func From CBase • Ph ươ ng th c virtual trong l p cha s tr thành ph ươ ng th c virtual trong l p con, cho dù trong l p con ta cĩ đ nh ngh ĩa đè ph ươ ng th c virtual đĩ hay khơng. Khi đnh ngh ĩa đè m t ph ươ ng th c virtual, ph ươ ng th c m i trong l p con c ũng tr thành m t ph ươ ng th c virtual cho dù khi khai báo l i ph ươ ng th c virtual này trong l p con ta cĩ dùng t khố virtual hay khơng. Ví d : #include using namespace std; class CBase { public : virtual void Func1(){cout<< "Func1 From CBase" <<endl;}; virtual void Func2(){cout<< "Func2 From CBase" <<endl;} virtual void Func3(){cout<< "Func3 From CBase" <<endl;} }; class CDerived : public CBase { public : // Func1 vn là ph ươ ng th c o ca CDerived // cho dù khơng cĩ t khố virtual đng tr ưc void Func1(){cout<< "Func1 From CDerived" <<endl;} virtual void Func2(){cout<< "Func2 From CDerived" <<endl;} // M c dù khơng đinh ngh ĩa đè Func3 , nh ưng nĩ v n là ph ươ ng // th c o c a CDerived . }; class CSubDerived : public CDerived { // Func1 vn là ph ươ ng th c o ca CSubDerived // cho dù khơng cĩ t khố virtual đng tr ưc void Func1(){cout<< "Func1 From CSubDerived" <<endl;} // Func3 vn là ph ươ ng th c o ca CSubDerived // cho dù khơng cĩ t khố virtual đng tr ưc void Func3(){cout<< "Func3 From CSubDerived" <<endl;} }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 10
  11. int main() { CDerived d; CBase *pb = &d; pb->Func1(); // Calls CDerived's Func1 pb->Func2(); // Calls CDerived's Func2 pb->Func3(); // Calls CBase's Func3 CSubDerived sd; CDerived *pd = &sd; pd->Func1(); // Calls CSubDerived's Func1 pd->Func2(); // Calls CDerived's Func2 pd->Func3(); // Calls CSubDerived's Func3 return 0; } // Output: Func1 From CDerived Func2 From CDerived Func3 From CBase Func1 From CSubDerived Func2 From CDerived Func3 From CSubDerived 5.2.4. Mt ví d hồn ch nh: M r ng l p CWeatherPrediction Sinh viên hãy đc hi u ví d này nh ư m t bài t p v nhà. 5.2.4.1. The WeatherPrediction Class • Imagine that you are given the task of writing a program to issue simple weather predictions. Weather predictions may be a little out of your area of expertise as a programmer, so you obtain a third-party class library that was written to make weather predictions based on the current temperature and the present distance between Jupiter and Mars (hey, it’s plausible). This third-party package is distributed as a compiled library to protect the intellectual property of the prediction algorithms, but you do get to see the class definition. The class definition for CWeatherPrediction is shown here: // WeatherPrediction.h / * Predicts the weather using proven new-age * techniques given the current temperature * and the distance from Jupiter to Mars. If * these values are not provided, a guess is * still given but it’s only 99% accurate. */ class CWeatherPrediction { public : virtual void SetCurrentTempFahrenheit( int nTemp); virtual void SetPositionOfJupiter( int nDistanceFromMars); / * Gets the prediction for tomorrow’s temperature Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 11
  12. */ virtual int GetTomorrowTempFahrenheit(); / * Gets the probability of rain tomorrow. 1 means * definite rain. 0 means no chance of rain. */ virtual double GetChanceOfRain(); / * Displays the result to the user in this format: * Result: x.xx chance. Temp. xx */ virtual void ShowResult(); protected : int m_nCurrentTempFahrenheit; int m_nDistanceFromMars; }; • This class solves most of the problems for your program. However, as is usually the case, it’s not exactly right for your needs. First, all the temperatures are given in Fahrenheit. Your program needs to operate in Celsius as well. Also, the ShowResult() method doesn’t produce a very user-friendly result. It would be nice to give the user some friendlier information. 5.2.4.2. Adding Functionality in a Subclass • Fundamentally, your program needs something just like the CWeatherPrediction class but with a few extra bells and whistles. Sounds like a good case for inheritance to reuse code. To begin, define a new class, CMyWeatherPrediction , that inherits from CWeatherPrediction . // MyWeatherPrediction.h class CMyWeatherPrediction : public CWeatherPrediction { }; • For the first modification, you might want to add knowledge of the Celsius scale to the class. There is a bit of a quandary here because you don’t know what the class is doing internally. If all of the internal calculations are made using Fahrenheit, how do you add support for Celsius? One way is to use the subclass to act as a go-between, interfacing between the user, who can use either scale, and the superclass, which only understands Fahrenheit.: • The first step in supporting Celsius is to add new methods that allow clients to set the current temperature in Celsius instead of Fahrenheit and to get tomorrow’s prediction in Celsius instead of Fahrenheit. You will also need protected helper methods that convert between Celsius and Fahrenheit. These methods can be static because they are the same for all instances of the class. // MyWeatherPrediction.h class CMyWeatherPrediction : public CWeatherPrediction { public : virtual void SetCurrentTempCelsius( int nTemp); Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 12
  13. virtual int GetTomorrowTempCelsius(); protected : static int ConvertCelsiusToFahrenheit( int nCelsius); static int ConvertFahrenheitToCelsius( int nFahrenheit); }; • To set the current temperature in Celsius, you need to convert the temperature first and then present it to the parent class in units that it understands: void CMyWeatherPrediction::SetCurrentTempCelsius( int nTemp) { int nFahrenheitTemp = ConvertCelsiusToFahrenheit(nTemp); SetCurrentTempFahrenheit(nFahrenheitTemp); } • Similarly, the implementation of GetTomorrowTempCelsius() uses the parent’s existing functionality to get the temperature in Fahrenheit, but converts the result before returning it. int CMyWeatherPrediction::GetTomorrowTempCelsius() { int nFahrenheitTemp = GetTomorrowTempFahrenheit(); return ConvertFahrenheitToCelsius(nFahrenheitTemp); } 5.2.4.3. Replacing Functionality in a Subclass • The other major technique for subclassing is replacing existing functionality. The ShowResult() method in the CWeatherPrediction class is in dire need of a facelift. CMyWeatherPrediction can override this method to replace the behavior with its own implementation. • The new class definition for CMyWeatherPrediction is shown below. // MyWeatherPrediction.h class CMyWeatherPrediction : public CWeatherPrediction { public : virtual void SetCurrentTempCelsius( int nTemp); virtual int GetTomorrowTempCelsius(); virtual void ShowResult(); protected : static int ConvertCelsiusToFahrenheit( int nCelsius); static int ConvertFahrenheitToCelsius( int nFahrenheit); }; • A possible new user-friendly implementation follows. void CMyWeatherPrediction::ShowResult() { cout<< "Tomorrow’s temperature will be " << GetTomorrowTempCelsius()<< " degrees Celsius (" << Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 13
  14. GetTomorrowTempFahrenheit() 0.5) { cout<< "Bring an umbrella!" <<endl; } } • CMyWeatherPrediction has emerged as a new class with new functionality tailored to a more specific purpose. 5.2.5. Lp c s tr u t ng (abstract base class) • Ph ươ ng th c o thu n túy (pure virtual method ) là ph ươ ng th c o đưc khai báo v i pure specifier (= 0 ). Ví d : class CDocument { public : // Requirements for derived classes: They must implement // these functions. virtual char *Identify() = 0; // Ph ươ ng th c o thu n tuý virtual char *WhereIs() = 0; // Ph ươ ng th c o thu n tuý }; • Mt l p đưc g i là lp c ơ s tr u t ưng (hay g i t t là lp tr u t ưng ) khi nĩ th a m t trong hai điu ki n sau: o Cĩ ch a ít nh t mt ph ươ ng th c o thu n tuý o Hoc k th a ít nh t mt ph ươ ng th c o thu n túy t m t lp cơ s tr u tưng khác và khơng cung c p m t đ nh ngh ĩa đè cho ph ưng th c o thu n túy đưc k th a. Ví d l p CDocument trên là m t l p c ơ s tr u tưng vì nĩ ch a hai ph ươ ng th c o thu n tuý: Identify() và WhereIs() . • Lp c ơ s tr u t ưng ch đưc dùng làm c ơ s cho các l p khác. Ta khơng th to ra bt k ỳ đ i t ưng nào c a l p c s tr u t ưng (khơng th khai báo m t bi n thu c l p c ơ s tr u t ưng). Nĩ ch đưc dùng đ đ nh ngh ĩa m t khái ni m t ng quát, chung cho các l p khác. • Mc dù khơng th t o ra m t đ i t ưng c a l p c ơ s tr u t ưng, nh ưng ta cĩ th khai báo m t con tr ho c m t tham chi u đ n l p c ơ s tr u t ưng. • Bt k ỳ m t l p nào d n xut t l p c ơ s tr u t ưng, n u mu n là mt l p bình th ưng, nĩ ph i khai báo l i các ph ươ ng th c o (thu n túy) và ph i cung c p m t đnh ngh ĩa cho các ph ươ ng th c o này. Ví d : // L p c ơ s tr u t ưng class CAbstract Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 14
  15. { public : virtual void Print() = 0; // pure virtual method virtual void Process() = 0; // pure virtual method int Status(); // normal method }; // ð CDerived khơng tr thành l p tr u t ưng, // nĩ ph i cung c p đ nh ngh ĩa cho các ph ươ ng th c // o thu n túy đưc k th a t l p tr u t ưng CAbstract class CDerived: public CAbstract { public : virtual void Print() { // Do something } virtual void Process() { // Do something } }; 5.2.5.1. Các h n ch c a l p tr u t ưng • Mt l p tr u t ưng khơng th đưc dùng cho: o Khai báo bi n ho c khai báo data member c a m t l p. o Ki u c a tham s c a các hàm. o Giá tr tr v c a các hàm. o Xác đnh ki u trong các tốn t ép ki u. • Nu constructor/destructor cho l p tr u t ưng g i ph ươ ng th c o thu n túy, ho c tr c ti p ho c gián ti p, thì k t q a s khơng xác đnh. Tuy nhiên, constructors và destructors cho l p tr u t ưng cĩ th g i các ph ươ ng th c khác. • Ta cĩ th cung c p m t đ nh ngh ĩa cho ph ươ ng th c o thu n tuý, nh ưng đ g i ph ươ ng th c o mt cách t ưng minh (khơng ph i đưc gi bng trình biên d ch), ta ph i dùng cú pháp sau ( đưc g i là fully qualified member-function name ): tên-lp-tr u-tưng :: tên-ph ươ ng-th c-o( ) • Destructor c a l p c s luơn đưc trình biên d ch g i trong quá trình h y b m t đi t ưng thu c l p d n xu t. Vì th , khi c n t o m t cu trúc th b c các l p, n u trong h th ng này cĩ nh ng l p c ơ s tr u t ưng vi destructor o thu n tuý (destructor là ph ươ ng th c o thu n tuý), ta ph i cung c p đ nh ngh ĩa cho destructor o này. Xét ví d sau: o Source code: // Khai báo m t l p c ơ s tr u t ưng v i destructor o thu n //tuý. class CBase { public : Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 15
  16. CBase() {} virtual ~CBase()=0; }; // Cung c p đ nh ngh ĩa cho destructor. CBase::~CBase() { } class CDerived: public CBase { public : CDerived() {} ~CDerived(){} }; int main() { CDerived *pDerived = new CDerived; delete pDerived; } o Nh n xét:  Khi delete đi t ưng đưc tr b i con tr pDerived , destructor c a lp CDerived đưc g i b i trình biên d ch, ti p theo destructor ca lp c ơ s CBase đưc g i. Vì th ta ph i cung cp đ nh ngh ĩa cho destructor ~CBase (m c dù đây là ph ươ ng th c o thu n túy).  Trong ví d này, destructor c a l p c ơ s ~CBase đưc g i m t cách ng m đ nh (implicitly) b i trình biên d ch ngay sau khi destructor cho l p c ơ s ~CDerived đưc g i. Ta cĩ th gi m t cách t ưng minh (explicitly) ph ươ ng th c o thu n tuý ~CBase bng cách dùng fully qualified member-function name : CBase::~CBase() . 5.3. S t ươ ng tác gi a l p cha và l p con • Khi t o các l p con, ta c n chú ý đ n nh ng t ươ ng tác gi a l p cha và l p con như: o Th t g i constructor c a l p con và l p cha. Khi khai báo m t đ i t ưng thu c l p con, các thành ph n c a l p cha đưc kh i t o tr ưc hay các thành ph n trong l p con đưc kh i t o tr ưc. o Th t g i destructor c a l p con và l p cha. o Vi c chuy n ki u gi a l p con và l p cha. Vi c thi u hi u bi t v nh ng v n đ nêu trên cĩ th d n đ n nh ng l i r t khĩ phát hi n trong ch ươ ng trình. 5.3.1. Th t g i các constructor • C++ qui đnh th t kh i t o đ i t ưng nh ư sau: Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 16
  17. 1. Các thành ph n đưc k th a t l p c ơ s (n u cĩ) đưc kh i t o tr ưc (do v y constructor c a l p c ơ s đưc g i tr ưc). 2. Ti p theo là các thành ph n khơng t ĩnh (khơng đưc khai báo v i t khố static ). Nu cĩ nhi u thành ph n khơng t ĩnh , thành ph n nào đưc khai báo tr ưc s đưc kh i t o tr ưc (do vy constructor ca thành ph n đưc khai báo tr ưc s đưc g i tr ưc). 3. Cu i cùng là thân hàm ca constructor c a đ i t ưng đang đưc khai báo đưc th c hi n. • Lu t này đưc áp d ng đ qui, ngh ĩa là n u cĩ l p cha c a l p cha (t c l p ơng) thì constructor ca l p ơng đưc g i tr ưc, ri đ n l p cha, v.v N u cĩ t tiên cao h ơn thì quá trình c ũng t ươ ng t : đ u tiên là l p t tiên cao nh t, r i đ n l p con c a l p đĩ, v.v cu i cùng là constructor c a l p đang cĩ đi t ưng đưc khai báo đưc th c hi n. • Constructor c a l p cha đưc g i t đ ng bi trình biên d ch. Nu l p cha cĩ constructor m c đ nh, trình biên d ch C++ s t đ ng g i constructor m c đ nh ca l p cha. Ng ưc l i n u l p cha khơng cĩ constructor m c đ nh, ho c n u cĩ nh ưng ta khơng mu n trình biên d ch g i constructor m c đ nh, ta cĩ th ch đ nh constructor (c a l p cha) đưc g i (b i trình biên d ch) bng cách đưa constructor đĩ vào danh sách kh i t o (initializer list - nu quên khái ni m này, sinh viên cn xem l i ph n nĩi v lp bao ). • Trong ví d dưi đây, CSuper khơng cĩ constructor mc đ nh. Khi đĩ, ta ph i ch đnh rõ cho trình biên d ch bi t ta mu n nĩ g i constructor nào c a l p cha CSuper (m c dù CSuper ch cĩ m t constructor) đ kh i t o các thành ph n đưc k th a t l p cha trong l p con, b ng cách vi t constructor đĩ cùng v i đ i s ca nĩ vào ph n initilizer list c a constructor c a l p con. // Super.h class CSuper { int m_nX; public : CSuper( int i); }; // Sub.h class CSub : public CSuper { public : CSub(); }; // Sub.cpp CSub::CSub() : CSuper(7) { // Do CSub’s other initialization here. } Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 17
  18. Câu h i: 1) Thành ph n static ca m t l p đưc kh i t o khi nào? 2) ðon ch ươ ng trình sau s xu t ra màn hình: a. 123 c. 12 e. 2 b. 321 d. 3 f. 1 #include using namespace std; class CSomething { public : CSomething() { cout using namespace std; class A{ int m_nA; public : A( int a){m_nA = a; cout<< "1" ;} }; class B: public A{ int m_nB; A m_ComposedObjA; public : B( int a, int b); }; B::B( int a, int b):A(a), m_ComposedObjA(a + 5), m_nB(b) Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 18
  19. { cout using namespace std; class CSomething { public : CSomething() { cout << "2" ; } virtual ~CSomething() { cout << "2" ; } }; class CParent { public : CParent() { cout << "1" ; } virtual ~CParent() { cout << "1" ; } }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 19
  20. class CChild : public CParent { public : CChild() { cout using namespace std; class CSomething { public : CSomething() { cout << "2" ; } ~CSomething() { cout << "2" ; } }; class CParent { public : CParent() { cout << "1" ; } ~CParent() { cout << "1" ; } }; class CChild : public CParent { public : CChild() { cout << "3" ; } ~CChild() { cout << "3" ; } protected : CSomething m_DataMember; }; int main( int argc, char argv) { CParent* ptr = new CChild(); delete ptr; return 0; } Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 20
  21. 5.3.3. Constructor sao chép & tốn t gán c a l p cha & lp con • Nu ta khơng đ nh ngh ĩa constructor sao chép/tốn t gán cho l p con, trình biên dch s t t o constructor sao chép/tốn t gán cho l p con. Phiên b n do trình biên d ch t o ra s g i constructor sao chép/tốn t gán c a lp cha. • Nu ta t đ nh ngh ĩa constructor sao chép/tốn t gán cho l p con, ta ph i nh g i constructor sao chép/tốn t gán c a l p cha trong constructor sao chép/tốn t gán c a l p con do ta đ nh ngh ĩa. #include using namespace std; class CSuper { private : double m_dX; public : CSuper( double d = 0){m_dX = d;}; CSuper( const CSuper& src){m_dX = src.m_dX;} CSuper& operator = ( const CSuper& rhs) { m_dX = rhs.m_dX; return *this ; } virtual void Print(){cout<<m_dX;} }; class CSub : public CSuper { private : int m_nY; public : CSub( int i = 0){m_nY = i;}; CSub( const CSub& src); CSub& operator = ( const CSub& rhs); virtual void Print() { cout<< "(" ; CSuper::Print(); cout<< ", " <<m_nY<< ")" <<endl; } }; CSub::CSub( const CSub& src) : CSuper(src) // G i ctor c a l p cha { m_nY = src.m_nY; } CSub& CSub:: operator =( const CSub& rhs) { if (&rhs == this ){ return *this ; } m_nY = rhs.m_nY; CSuper:: operator =(rhs); // G i tốn t gán c a l p cha return (* this ); } Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 21
  22. int main() { CSub MySub1(6); CSub MySub2(MySub1); CSub MySub3 = MySub1; MySub1.Print(); MySub2.Print(); MySub3.Print(); return 0; } // Output: (0, 6) (0, 6) (0, 6) 5.3.4. Truy xu t thành ph n c a l p cha • ð truy xu t vào thành ph n c a l p cha cĩ cùng tên v i l p con, ta dùng tốn t ly ph m vi k t h p v i tên c a l p cha. Ví d : // Gi s : // 1. CSub là con c a CSuper // 2. Trong CSub ta đang đnh ngh ĩa đè ph ươ ng th c //DoSomething() CSub::DoSomething() { cout << "In Sub’s version of doSomething()" << endl; CSuper::DoSomething(); // call the parent version. } Câu h i: 1) Cho bi t k t qu c a ph ươ ng th c sau : CSub::DoSomething() { cout << "In CSub’s version of DoSomething()" << endl; DoSomething(); } 5.3.5. Ép ki u (cast) gi l p cha và l p con 5.3.5.1. Ép ki u lên (up-casting) • Khi th c hi n vi c ép ki u t l p con thành l p cha, ta g i là ép ki u lên . • Ép ki u lên cĩ th th c hi n đưc trên đi t ưng, tham chi u và con tr . • Ép ki u lên cĩ th đưc th c hi n t đ ng (implicit conversion), khơng c n g i tưng minh tốn t chuy n ki u. • Khi ép ki u lên, cĩ th xu t hi n slicing ho c khơng xu t hi n slicing: Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 22
  23. o Nu th c hi n vi c ép ki u trên đi t ưng, s b slicing. o Nu th c hi n vi c ép ki u trên con tr ho c tham chi u, s khơng b slicing. • Ví d : class CParent { int m_nX; // }; class CChild : public CParent { int m_nY; // }; void Func() { CChild* pChild = new CChild(); // Ép ki u lên đưc th c hi n t đ ng (implicit) CParent* pParent = pChild; // No SLICE } 5.3.5.2. Ép ki u xu ng (down-casting) • Khi th c hi n vi c ép ki u t lp cha thành l p con, ta g i là ép ki u xu ng . • Ép ki u xu ng ch th c hi n đưc trên tham chi u và con tr . • Ép ki u xu ng ph i đưc th c hi n tưng minh (explicit conversion), t c ph i đưc th c hi n thơng qua tốn t ép ki u. • Ví d class CParent { int m_nX; // }; class CChild : public CParent { int m_nY; // }; void Func() { CParent* pParent = new CChild();// Khơng rõ l m nh ưng OK CParent* pParent2 = new CParent(); // T t // OK: vì pParent th c ch t tr t i CChild CChild* pChild = dynamic_cast (pParent); // pParent2 tr t i CParent ch khơng ph i CChild // ðây là phép ép ki u sai, do đĩ pChild2 == NULL CChild* pChild2 = dynamic_cast (pParent2); Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 23
  24. if (pChild2 != NULL) { // Do something here } } 5.4. ða x (tươ ng ng b i - polymorphism) ða x là khái ni m r t quan tr ng và là m t trong nh ng đ c tính đưc s d ng nhi u nh t c a C++. ð tìm hi u khái ni m này, ta hãy xét v n đ sau đây ( trích đ thi cao h c ngành CNTT, ðH KHTN TP.HCM, n ăm 2005 ). 5.4.1. ðt v n đ Ga s ta cĩ sn các l p CRectangle (l p hình ch nh t), CCircle (l p hình trịn), CTriangle (l p tam giác), CEllipse (l p e-lip), m i l p đ u cĩ ph ươ ng th c riêng đ tính di n tích cho lo i hình c a mình. Hãy đư a ra gi i pháp cho các vn đ sau: • Tìm t ng di n tích cho m t t p hình bao g m các hình nêu trên (các đi t ưng thu c các l p nêu trên), m i lo i hình cĩ s l ưng b t k ỳ. • Tìm hình ( đi t ưng) cĩ di n tích l n nh t trong t p hình nêu trên và cho bi t đĩ là hình gì (tam giác, trịn, ch nh t hay e-lip v.v ). • Ta khơng ph i thay đ i source code gi i quy t 2 câu h i nêu trên khi ta đnh ngh ĩa thêm các l p hình m i (ví d l p hình vuơng, l p hình thang, l p hình bình hành v.v ) và t p hình nêu trên cĩ th cĩ thêm m t s l ưng b t k ỳ các lo i hình m i này. Tt nhiên cĩ th cĩ các cách khác gi i quy t v n đ nêu trên, đây xin trình bày gi i pháp d a trên cơ ch t ươ ng ng b i ca C++, cho phép gi i quy t c ba v n đ nêu trên. 5.4.2. Gi i pháp d a trên kh n ăng đa x ca C++ 5.4.2.1. Nguyên lý Gi i pháp này d a trên các kh n ăng sau đây c a C++: • Kh n ăng đ nh ngh ĩa đè (override) ph ươ ng th c. • Mt con tr pParent tr t i l p cha (ví d cĩ tên CShape ), cĩ th đưc dùng đ tr t i m t l p con b t k ỳ c a l p cha CShape . • Gi s CShape cĩ ph ươ ng th c Area() và mi l p con c a CShape đu cĩ b n đnh ngh ĩa đè ph ươ ng th c Area() cho riêng mình. N u ta dùng con tr pParent đ g i ph ươ ng th c Area() thì tùy theo con tr pParent này đang tr t i đ i t ưng thu c l p con nào, mà phiên b n Area() tươ ng ng v i l p con đĩ s đưc th c hi n. Kh n ăng này c a C++ đưc g i là tính tươ ng ng b i (t ươ ng ng m t- nhi u) hay đa x (polymorphism ). ða x (vi t gn g n ca đa-ánh-x) đưc hi u theo ngh ĩa, v i cùng m t câu l nh g i ph ươ ng th c Area() thơng qua con tr pParent Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 24
  25. pParent->Area() ; trình biên d ch s ánh x đ n các phiên b n khác nhau c a ph ươ ng th c Area() tươ ng ng v i các l p con khác nhau c a CShape , tu ỳ theo hi n th i con tr pParent đang tr t i đ i t ưng thu c l p con nào. 5.4.2.2. Cài đt c th Ta gi s các l p CRectangle , CTriangle , CCircle , CEllipse đu cĩ ph ươ ng th c Area() đ tính di n tích và các l p này đưc mơ t t ươ ng ng trong các file Rectangle.h , Rectangle.cpp , Triangle.h , Triangle.cpp , Circle.h , Circle.cpp , Ellipse.h , Ellipse.cpp . Bưc 1 : Xây d ng l p CShape (l p hình) là m t l p tr u t ưng g m m t ph ươ ng th c o thu n tuý Area() : // File Shape.h class CShape { public : virtual double Area() = 0; }; Bưc 2 : Hi u ch nh các file Rectangle.h , Triangle.h , Circle.h , Ellipse.h nh ư sau: • Khai báo l p CShape là cha c a các l p hình ch nh t (CRectangle ), hình tam giác (CTriangle ), hình trịn (CCircle ), hình e-líp (CEllipse ). • Thêm t khố virtual vào khai báo ph ươ ng th c Area() . CShape CRectangle CTriangle CCircle CEllipse Hình 5.2 // File Rectangle.h : ch a đ nh ngh ĩa l p CRectangle #include "Shape.h" class CRectangle : public CShape // thêm ": public CShape" t i đây { // public : virtual double Area(); // thêm t khố virtual // }; // File Triangle.h : ch a đ nh ngh ĩa l p CTriangle Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 25
  26. #include "Shape.h" class CTriangle : public CShape // thêm ": public CShape" t i đây { // public : virtual double Area(); // thêm t khố virtual // }; // File Circle.h : ch a đ nh ngh ĩa l p CCircle #include "Shape.h" class CCircle : public CShape // thêm ": public CShape" t i đây { // public : virtual double Area(); // thêm t khố virtual // }; // File Ellipse.h : ch a đ nh ngh ĩa l p CEllipse #include "Shape.h" class CEllipse : public CShape // thêm ": public CShape" t i đây { // public : virtual double Area(); // thêm t khố virtual // }; Bưc 3 : Vi t hàm tính t ng di n tích các hình và hàm tìm hình cĩ di n tích l n nh t // File Utilities.cpp : ch a các hàm ti n ích (helpers) // This function calculates sum of areas of shapes #include "Shape.h" #include double SumArea(vector shapes) { double dSum = 0; vector ::iterator Iterator; for (Iterator = shapes.begin(); Iterator != shapes.end(); Iterator++) dSum += (*Iterator)->Area(); return dSum; } // File Utilities.cpp : ch a các hàm ti n ích (helpers) // This function finds the bigest shape and returns the pointer to it. CShape* FindBigestShape(vector shapes) { Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 26
  27. double dCurrentBigest = -1; vector ::iterator IteratorToCurrentBigest; vector ::iterator Iterator; for (Iterator = shapes.begin(); Iterator != shapes.end(); Iterator++) { double dCurrentArea = (*Iterator)->Area(); if (dCurrentArea > dCurrentBigest) { IteratorToCurrentBigest = Iterator; dCurrentBigest = dCurrentArea; } } return *IteratorToCurrentBigest; } Bưc 4 : ð tr l i cho câu h i “hình cĩ di n tích l n nh t là hình gì”, ta thêm hàm virtual GetClassName() vào các l p nh ư sau: // File Shape.h #include class CShape { public : virtual double Area() = 0; virtual string GetClassName() = 0; //Thêm p.th c o GetClassName() }; // File Rectangle.h : ch a đ nh ngh ĩa l p CRectangle #include "Shape.h" class CRectangle : public CShape { // public : virtual double Area(); virtual string GetClassName() //Thêm p.th c o GetClassName() { return "CRectangle"; } // }; // File Triangle.h : ch a đ nh ngh ĩa l p CTriangle #include "Shape.h" class CTriangle : public CShape { // public : virtual double Area(); virtual string GetClassName() //Thêm p.th c o GetClassName() { return "CTriangle" ; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 27
  28. } // }; // File Circle.h : ch a đ nh ngh ĩa l p CCircle #include "Shape.h" class CCircle : public CShape { // public : virtual double Area(); virtual string GetClassName() //Thêm p.th c o GetClassName() { return "CCircle" ; } // }; // File Ellipse.h : ch a đ nh ngh ĩa l p CEllipse #include "Shape.h" class CEllipse : public CShape { // public : virtual double Area(); virtual string GetClassName() //Thêm p.th c o GetClassName() { return "CEllipse"; } // }; Sau đây là ví d v cách s d ng các hàm trên: // File main.cpp #include "Rectangle.h" #include "Triangle.h" #include "Circle.h" #include "Ellipse.h" int main() { // Kh i t o các đ i t ưng hình tam giác, ch nh t, v.v // Gi s các constructors sau là h p l CRectangle rec1(5, 6), rec2(1, 2); CTriangle tri1(10, 5), tri2(5, 9); CCircle cir1(5, 20), cir2(20, 1); CEllipse elip1(9, 10), elip2(20, 30); Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 28
  29. // M ng các hình vector shapes; // ðư a các hình vào m ng shapes.push_back(&rec1); shapes.push_back(&rec2); shapes.push_back(&tri1); shapes.push_back(&tri2); shapes.push_back(&cir1); shapes.push_back(&cir2); shapes.push_back(&elip1); shapes.push_back(&elip2); // Tìm t ng di n tích các hình cout GetClassName()<<endl; return 0; } 5.4.2.3. Nh n xét • ð t n d ng t t tính đa x c a C++, khi thi t k các l p, hãy c g ng thi t k theo cu trúc hình cây: trong c u trúc cây này, nh ng l p t ươ ng t nên đưc d n xu t t m t l p cha. • Nu sau này ta cĩ thêm l p, ví d CTrapezium (hình thang), ta ch cn khai báo lp này k th a t l p CShape và đnh ngh ĩa đè ph ươ ng th c Area(), GetClassName() trong l p CTrapezium , mà khơng c n ph i s a b t k ỳ dịng code nào trong các hàm SumArea() và FindBigestShape() . • Vì đang đ c p đ n tính đa x , nên ta xét đn gi i pháp thêm ph ươ ng th c GetClassName() đ tr l i cho câu h i “hình l n nh t là hình gi”. Ngồi cách này, ta cịn cĩ th dùng tốn t typeid đ đt đưc cùng k t qu nh ư sau: int main() { // // Tìm hình cĩ di n tích l n nh t và cho bi t nĩ là hình gì cout<< "Kind of the bigest shape is: "<< typeid (*FindBigestShape(shapes)).name()<<endl; return 0; } Câu h i: • Hãy suy ngh ĩ, ngồi cách gi i quy t nêu trên, cịn cách nào t t h ơn khơng? Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 29
  30. 5.5. K th a t nhi u l p (multiple Inheritance) • Mt l p cĩ th cĩ nhi u l p c ơ s , điu này đưc g i là s k th a nhi u l p • ð đ nh ngh ĩa m t l p cĩ nhi u l p c ơ s , ta li t kê các l p c ơ s sau d u hai ch m nh ư sau: class CCellPhone { // }; class CCamera { // }; class CCameraCellPhone : public CCellPhone, public CCamera { // New members }; CCellPhone CCamera CCameraCellPhone Hình 5.3 • Bng cách ch đ nh l p CCellPhone và CCamera là c ơ s c a l p CCameraCellPhone nh ư trên, ta s cĩ các đ c tính sau: o CCameraCellPhone s k th a các thành ph n c a CCellPhone và CCamera . Các thành ph n ca CCameraCellPhone đưc k th a t CCellPhone và CCamera s cĩ thu c tính truy xut đưc xác đ nh tùy thu c vào lo i k th a (k th a public, protected hay private) gi ng nh ư k th a t m t l p. o CCameraCellPhone cĩ th truy xu t vào các thành ph n public và protected c a c CCellPhone và CCamera , nh ưng khơng truy xu t đưc vào các thành ph n private c a các l p c ơ s . o Mt đ i t ưng/con tr /tham chi u đ n đ i t ưng thu c l p CCameraCellPhone cĩ th đưc ép ki u lên (ng m đ nh) thành m t đ i tưng/con tr /tham chi u đ n đ i thu c l p CCamera ho c CCellPhone . o Mt con tr / tham chi u đ n đi t ưng thu c l p CCamera /CCellPhone cĩ th đưc ép ki u xu ng (m t cách t ưng minh) thành con tr /tham chi u đ n đ i t ưng thu c l p CCameraCellPhone . o Vi c t o m t đ i t ưng thu c l p con CCameraCellPhone s d n đ n vi c gi t đ ng các constructor m c đnh c a các l p cha CCamera và CCellPhone . Th t g i các constructor c a các l p cha chính là th t xu t hi n c a các l p cha trong danh sách các l p c ơ s . Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 30
  31. o Vi c h y m t đ i t ưng thu c l p con CCameraCellPhone s d n đ n vi c g i t đ ng các destructor ca các l p cha, v i th t ng ưc v i th t xu t hi n các l p cha trong danh sách các l p c ơ s . Câu h i: • ðon ch ươ ng trình sau s : a) Xu t ra màn hình: gau gau! meo meo! b) Báo l i khi d ch class CDog { public : virtual void Bark() { cout << "gau gau! " ; } }; class CCat { public : virtual void Cry() { cout << "meo meo!" << endl; } }; class CDogCat : public CDog, public CCat { }; int main( int argc, char argv) { CDogCat MyConfusedAnimal; MyConfusedAnimal.Bark(); MyConfusedAnimal.Cry(); return 0; } 5.5.1. S ti ngh ĩa v tên (ambigous names) • Khi các l p c ơ s cĩ các thành ph n d li u cĩ tên gi ng nhau ho c ph ươ ng th c cùng tên và cùng đi s , l p con đưc k th a t các l p c s này s cĩ nhi u thành ph n d li u cùng tên ho c nhi u ph ươ ng th c cùng tên và cùng ki u d li u. ðiu này d n đ n s m ơ h khi ta truy xu t vào các thành ph n cùng tên này. • ð truy xu t vào các thành ph n cĩ cùng tên đưc k th a t các l p c ơ s khác nhau, ta ph i dùng tốn t l y ph m vi kèm theo tên l p c ơ s đ trình biên d ch cĩ th bi t đưc ta mu n truy xu t vào thành ph n đưc k th a t l p c ơ s nào. • Ví d : class CDog { public : virtual void Bark() { cout << "gau gau! " ; } virtual void Eat() { cout << "The dog has eaten." << endl; } }; class CCat { public : Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 31
  32. virtual void Cry() { cout (MyConfusedAnimal).Eat(); return 0; } • Ta c ũng cĩ th đ nh ngh ĩa cho chính l p CDogCat mt ph ươ ng th c Eat() sau đĩ gi ph ươ ng, nh ư v y, m i l n g i ph ương th c Eat() trên đi t ưng CDogCat thì ph ươ ng th c Eat() này s đưc th c thi. • Nu trong ph ươ ng th c Eat() ca CDogCat , ta cĩ nhu c u g i ph ươ ng th c Eat() ca l p c ơ s , ta s làm nh ư sau: void CDogCat::Eat() { CDog::Eat(); // Explicitly call CDog’s version of Eat() // ho c CCat::Eat(); } 5.5.2. Lp c s o (virtual base class) • Cĩ nh ng tr ưng h p trong đĩ m t l p A đưc làm c ơ s cho 2 l p A1 và A2, sau đĩ, l p A1 và A2 l i làm c ơ s cho l p B. Khi đĩ x y ra tình tr ng l p A đưc làm c ơ s (gián ti p) 2 ln. ðiu này làm lãng phí b nh vì trong l p B, các thành ph n c a l p A đưc l p l i 2 l n, và cĩ th d n đ n s t i ngh ĩa n u ta mu n truy xu t vào m t thành ph n c a l p A. Ví d (xem hình 5.4 a và 5.4 b): Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 32
  33. class Queue { public : int m_nX; }; class CashlerQueue : public Queue { }; class LunchQueue : public Queue { }; class LunchCashierQueue:public CashlerQueue, public LunchQueue { }; int main() { LunchCashierQueue obj; // L i: ambigous access, khơng bi t //truy xu t vào CCashlerQueue::m_nX hay CLunchQueue::m_nX obj.m_nX = 1; obj.LunchQueue::m_nX = 1; // OK return 0; } Hình 5.4 a) và 5.4 b) • ð gi i quy t v n đ này, ta dùng t khố virtual đ khai báo m t l p c ơ s là lp c ơ s o. V i cách khai báo nh ư v y, ch cĩ m t phiên b n c a các thành ph n trong l p Queue cĩ m t trong l p LunchCashierQueue (xem hình 5.5). o Ví d 1: class Queue { // Member list }; class CashierQueue : virtual public Queue Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 33
  34. { // Member list }; class LunchQueue : virtual public Queue { // Member list }; class LunchCashierQueue : public LunchQueue, public CashierQueue { // Member list }; Hình 5.5 o Ví d 2: class A{}; class B: virtual public A{}; class C: public A, public B{}; // L i void main() { C c; } • Mt l p cĩ th v a cĩ lp c ơ s o, v a cĩ l p c ơ s bình th ưng (hình 5.6 và 5.7). Ví d : Hình 5.6 Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 34
  35. Sau đây là hình nh trong b nh và source code ví d tươ ng ng v i các l p trong sơ đ các l p hình 5.6 : Hình 5.7 #include using namespace std; class CQueue { public : int m_nX, m_nY; CQueue( int x, int y):m_nX(x), m_nY(y){} }; class CCashierQueue : virtual public CQueue { public : CCashierQueue( int x, int y); void Print() { cout<<endl<< "CCashierQueue: x = " << m_nX<< ", y = " <<m_nY; } }; CCashierQueue::CCashierQueue( int x, int y):CQueue(x, y){} class CLunchQueue: virtual public CQueue { public : CLunchQueue( int x, int y):CQueue(x, y){} void Print() { cout<<endl<< "CLunchQueue: x = " << m_nX<< ", y = " <<m_nY; } }; class CLunchCashierQueue: public CCashierQueue, public CLunchQueue { public : CLunchCashierQueue( int x, int y):CQueue(x, y), CCashierQueue(x - 1, y + 1), CLunchQueue(x - 2,y + 2){} void Print() { cout<<endl<< "CLunchCashierQueue: x = " << m_nX<< ", y = "<<m_nY; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 35
  36. } }; class CTakeoutQueue: public CQueue { public : CTakeoutQueue( int x, int y): CQueue(x, y){} void Print() { cout<<endl<< "CTakeoutQueue: x = " << m_nX<< ", y = " <<m_nY; } }; class CLunchTakeoutCashierQueue: public CLunchCashierQueue, public CTakeoutQueue { public: CLunchTakeoutCashierQueue( int x, int y): CLunchCashierQueue::CQueue(x + 2, y + 2), CLunchCashierQueue(x, y), CTakeoutQueue(x, y){} void Print1() { cout<<endl<< "CLunchTakeoutCashierQueue: x1 = "<<CLunchCashierQueue::m_nX<< ", " << "y1 = " <<CLunchCashierQueue::m_nY; } void Print2() { cout<<endl<< "CLunchTakeoutCashierQueue: x2 = "<<CTakeoutQueue::m_nX<< ", " << "y2 = " <<CTakeoutQueue::m_nY; } }; int main(){ CLunchTakeoutCashierQueue obj(5, 6); obj.CCashierQueue::Print(); obj.CLunchQueue::Print(); obj.CTakeoutQueue::Print(); obj.Print1(); obj.Print2(); obj.CLunchCashierQueue::m_nX += 3; obj.CLunchCashierQueue::m_nY += 4; obj.CTakeoutQueue::m_nX -= 3; obj.CTakeoutQueue::m_nY -= 4; cout<<endl; obj.CCashierQueue::Print(); obj.CLunchQueue::Print(); obj.CTakeoutQueue::Print(); obj.Print1(); obj.Print2(); cout<<endl; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 36
  37. CLunchCashierQueue obj2(20, 30); obj2.Print(); cout<<endl; return 0; } // ðon ch ươ ng trình này s in trên màn hình nh ư sau: CCashierQueue: x = 7, y = 8 CLunchQueue: x = 7, y = 8 CTakeoutQueue: x = 5, y = 6 CLunchTakeoutCashierQueue: x1 = 7, y1 = 8 CLunchTakeoutCashierQueue: x2 = 5, y2 = 6 CCashierQueue: x = 10, y = 12 CLunchQueue: x = 10, y = 12 CTakeoutQueue: x = 2, y = 2 CLunchTakeoutCashierQueue: x1 = 10, y1 = 12 CLunchTakeoutCashierQueue: x2 = 2, y2 = 2 CLunchCashierQueue: x = 20, y = 30 Câu h i: • Hãy gi i thích k t q a trong ví d mơ t hình 5.6 5.5.3. Quá trình kh i t o đ i t ng trong Microsoft C++ • Khi kh i t o m t đ i t ưng, constructor th c hi n mt lo t các cơng vi c khác nhau mà ta khơng th y đưc, ngay c khi ta khơng vi t b t k ỳ dịng l nh nào cho constructor. Tt c các cơng vi c này là đ nh m t o thành m t đ i t ưng hồn ch nh c a m t l p. • Trong Microsoft C++ (và m t s implement khác c a C++), đ kh i t o đi tưng c a m l p A, mt constructor ca nĩ đưc g i (b i ng ưi l p trình ho c trình biên d ch). Và ngay sau khi constructor c a l p A đưc g i, các b ưc sau s đưc th c hi n l n l ưt (và đ qui) : 1. Kh i t o con tr /các con tr tr t i đ i t ưng con/các đi t ưng con (subobject/subojects) ch a các thành ph n thu c l p c ơ s o/các l p c s o c a lp A. Con tr này đưc g i là con tr c ơ s o (virtual base pointer ), vi t t t vbptr . Bưc này đưc th c hi n ch n u l p A cĩ m t ho c nhi u l p c ơ s o. 2. Gi các constructor c a các lp cha ca lp A đ kh i t o các thành ph n thu c các lp cha (theo th t khai báo các l p cha). 3. Gi các constructors đ kh i t o các đi t ưng thành ph n (composed objects) ca lp A (theo th t khai báo). Các đi t ưng thành ph n là các đi t ưng đưc khai báo nh ư các data member c a m t l p (khi đĩ, l p này đưc g i là lp bao ). Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 37
  38. 4. Kh i t o con tr tr t i virtual table (xem ph n 5.6 ). Con tr này đưc gi là con tr hàm o (virtual function pointers ), vi t t t vfptr . Bưc này ch đưc th c hi n khi l p A cĩ ít nh t m t ph ươ ng th c o ho c k th a ít nh t m t ph ươ ng th c o t m t l p cha. 5. Th c hi n code trong thân hàm c a chính nĩ (constructor c a đ i t ưng đang đưc kh i t o). • Sau khi các b ưc trên hồn thành, vùng nh đưc c p phát và kh i t o trong quá trình trên s tr thành m t đ i t ưng thu c l p A. 5.5.4. S chuy n ki u t i ngh ĩa (Ambiguous Conversions) • Gi s ta cĩ 4 l p A, B, C, D trong đĩ B và C là con c a A, D là con c a B và C nh ư hình sau: Hình 5.8 • Nu ta khai báo d là m t đ i t ưng thu c D thì: o &d (l y đ a ch c a d) s tr v m t con tr tr t i đ a ch c b n c a đ i tưng d (t c là đa ch c a thành ph n A trong l p B). o Vi c ép ki u (A*)&d s gây l i vì TBD khơng bi t s tr t i vùng nào trong hai vùng A c a đi t ưng d (xem hình sau). Hình 5.9 o ð th c hi n vi c ép ki u con tr (&d) sang ki u A* (con tr tr t i A), ta ph i th c hi n nh ư sau: (A *)(B *)&d // Use B subobject. (A *)(C *)&d // Use C subobject. Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 38
  39. • Ví d : class A{}; class B:public A{}; class C: public A{}; class D: public B, public C{}; int main() { D d; A *pa = (A*)&d; // L i! vì cĩ 2 thành ph n A trong d pa = (A*)(B*)&d; // OK: tr t i thành ph n A trong B pa = (A*)(C*)&d; // OK: tr t i thành ph n A trong C return 0; } 5.5.5. Truy xu t đa thu c tính (Multiple access) • Nu m t l p k th a t nhi u l p c ơ s , và các l p c ơ s đ u cĩ chung m t l p c ơ s o (virtual base classes), khi đĩ các thành ph n trong l p c ơ s o cĩ th đưc truy xu t (reached) qua các con đưng khác nhau. Các cách truy xu t này cĩ các thu c tính truy xu t khác nhau (xem hình d ưi). Trình biên d ch s ch n cách truy xu t cĩ thu c tính truy xu t m nh nh t đ truy xu t đ n các thành ph n thu c l p c ơ s o. Hình 5.10 • Trong hình 5.10 , cĩ 2 con đưng đ truy x t vào các thành ph n c a l p VBase t đi t ưng thu c l p Derived : con đưng bên trái (thơng qua l p LeftPath ) và con đưng bên ph i (thơng qua l p RightPath ). Do l p RightPath k th a public t lp VBase trong khi đĩ l p LeftPath k th a private t l p VBase nên con đưng bên ph i s m nh h ơn (d truy xu t h ơn) con đưng bên trái. Vì v y khi ta truy xu t đ n các thành ph n thu c l p VBase t đ i t ưng thu c l p Derived , trình biên d ch s ch n cách truy xu t vào các thành ph n thu c l p VBase thơng qua lp RightPath . • Ví d : class CVirtualBase { public : Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 39
  40. int m_nPublicFromVBase; protected : int m_nProtectedFromVBase; private : int m_nPrivateFromVBase; }; class CBase1: virtual public CVirtualBase {}; class CBase2: virtual protected CVirtualBase {}; class CBase3: virtual private CVirtualBase {}; class CDerived1: public CBase1, public CBase2, public CBase3 {}; class CDerived2: protected CBase1, public CBase2, private CBase3 {}; class CDerived3: private CBase1, private CBase2, public CBase3 {}; int main() { CDerived1 d1; CDerived2 d2; CDerived3 d3; d1.m_nPrivateFromVBase = 1; // Error: Access to private member d1.m_nProtectedFromVBase = 1; //Error: Access to protected member d1.m_nPublicFromVBase = 1; // OK: Access to public member. d2.m_nPrivateFromVBase = 1; // Error: Access to private member d2.m_nProtectedFromVBase = 1; //Error: Access to protected member d2.m_nPublicFromVBase = 1; // Error: Access to protected member d3.m_nPrivateFromVBase = 1; // Error: Access to private member d3.m_nProtectedFromVBase = 1; //Error: Access to private member d3.m_nPublicFromVBase = 1; // Error: Access to private member return 0; } 5.6. Cơ ch ho t đ ng c a hàm virtual • Gi s CBase là m t l p cĩ ch a ph ươ ng th c virtual. Khi đĩ m i đ i t ưng thu c l p CBase s cĩ m t con tr 4 byte tr t i mt vùng nh đưc g i là virtual table , vi t ng n g n là vtable . Con tr này đưc g i là con tr hàm o, vi t t t vfptr , và chi m 4 byte đ u tiên c a vùng nh dành cho đi t ưng. vtable là m t mng các con tr hàm, m i con tr cĩ kích th ưc 4 byte, tr t i đ a ch ch a ph n đnh ngh ĩa c a hàm virtual tươ ng ng. Như v y n u l p cĩ N ph ươ ng th c virtual thì vtable tươ ng ng v i mt đi t ưng thu c l p này s s cĩ kích th ưc 4*N Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 40
  41. byte và ch a N con tr hàm: N con tr hàm này s tr t i N vùng nh , m i vùng nh này ch a ph n đ nh ngh ĩa c a m t ph ươ ng th c virtual c a l p. • Gi s ta cĩ 2 l p đưc khai báo nh ư đon code d ưi đây, thì hình nh v các virtual table ca 2 đi t ưng thu c 2 lp này s nh ư hình 5.8 . class CBase { public : virtual void Method1(){cout<< "Method1 From CBase" <<endl;} virtual void Method2(){cout<< "Method2 From CBase" <<endl;} virtual void Method3(){cout<< "Method3 From CBase" <<endl;} }; class CDerived : public CBase { public : void Method1(){cout<< "Method1 From CDerived" <<endl;} virtual void Method2(){cout<< "Method2 From CDerived" ;} }; ði t ưng thu c l p CBase Implement of Pointer to vtable Method1 Method1 Implement of Method2 Method2 Method3 Implement of Method3 ði t ưng thu c l p CDerived Pointer to vtable Method1 Implement of Method1 Method2 Implement of Method2 Method3 Hình 5.11 . Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 41
  42. • Trong hình trên, ph ươ ng th c Method3 khơng đưc đ nh ngh ĩa đè trong l p CDerived , do đĩ con tr hàm t ươ ng ng v i Method3 ca đi t ưng thu c l p CDerived s tr t i cùng vùng code th c thi Method3 ca đ i t ưng thu c l p cha CBase . • Khi g i ph ươ ng th c thơng qua con tr ho c tham chi u, tùy theo con tr đang tr ti đ i t ưng thu c l p nào mà b ng vtable t ươ ng ng v i l p đĩ đưc s d ng đ gi hàm thích h ơp. • Khi ta g i ph ươ ng th c thơng qua đ i t ưng, vtable tươ ng ng v i đ i t ưng đĩ s đưc dùng đ g i ph ươ ng th c. 5.7. Khai báo using (using declaration) • Khai báo using (using declaration ) khác v i ch d n using (using directive ): Khai báo using cho phép tng tên riêng đưc s d ng mà khơng c n qualification, trong khi ch d n using cho phép t t c các tên trong m t namespace đưc s dng mà khơng c n qualification. • Sinh viên hãy t tìm hi u khái ni m này nh ư m t bài t p v nhà. 5.8. Bài tp 1) Mt ph ươ ng th c mu n đưc đ nh ngh ĩa đè thì ph ươ ng th c đĩ ph i là ph ươ ng th c o đưc khai báo v i t khố virtual đ u khai báo. Ph ươ ng th c virtual cho phép l p trình viên cĩ th đinh ngh ĩa đè khi h mu n. Gi i thích t i sao trình biên dch C++ khơng t bi n các ph ươ ng th c thành ph ươ ng th c virtual (gi ng nh ư Java) đ ng ưi l p trình cĩ quy n l a ch n đ nh ngh ĩa đè ho c khơng đ nh ngh ĩa đè sau này. 2) Nh ng ng ưi l p trình cĩ kinh nghi m th ưng khai báo destructor c a m t l p là mt ph ươ ng th c o. Hãy gi i thích và cho ví d . 3) Cĩ nên đnh ngh ĩa đè các constructor khơng? Gi i thích? 4) Hãy cho bi t s khác nhau gi a hài tốn t : dynamic_cast và static_cast . 5) ðon ch ươ ng trình sau s (hiding): a. In ra màn hình: 21 b. In ra màn hình: 12 c. In ra màn hình: 11 d. In ra màn hình: 22 e. Bi l i khi d ch #include using namespace std; class CSuper { public : void Go() { cout << "1" ; } }; class CSub : public CSuper { public : Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 42
  43. void Go() { cout using namespace std; class CSuper { public : virtual void Method() { cout using namespace std; class CSuper { public : virtual void Method() { cout << "1" ; } }; class CSub : public CSuper { protected : Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 43
  44. virtual void Method() { cout using namespace std; class CSuper { protected : virtual void Method() { cout using namespace std; class CSuper { public : virtual void Method( int x = 0) { cout << 5; } }; class CSub : public CSuper { public : virtual void Method( int y = 0) { cout << 10; } }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 44
  45. int main() { CSub MySub; CSuper &ref = MySub; ref.Method(); return 0; } 10) ðon ch ươ ng trình sau s : a. In ra màn hình s 5 (đáp án) b. In ra màn hình s 10 c. B l i khi d ch #include using namespace std; class CSuper { public : virtual void Method( int x = 5) { cout using namespace std; class CSuper { public: virtual void Method( int x = 5) { cout<< "CSuper" << x; } }; class CSub : public CSuper { public : virtual void Method( int y = 10) { cout<< "CSub" << y; } }; Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 45
  46. int main() { CSub MySub; CSuper &ref = MySub; ref.Method(); return 0; } 12) ðon ch ươ ng trình sau s: a. In ra màn hình: CSub ( đáp án) b. In ra màn hình: CSuper c. B l i khi d ch #include using namespace std; class A{}; class A1: public A{}; class CSuper{ public : virtual A* Speak(){cout Speak(); delete pSuper; return 0; } 13) ðon ch ươ ng trình sau s : a. In ra màn hình: CSuper b. In ra màn hình: CSub c. B l i khi d ch (đáp án: Error C2555: 'CSub::Speak': overriding virtual function return type differs and is not covariant (hi p bi n) from 'CSuper::Speak' ). #include using namespace std; class CSuper{ public : virtual int Speak(){cout<< "CSuper" <<endl; return 1;} }; class CSub: public CSuper{ public : virtual double Speak(){cout<< "CSub" <<endl; return 1.0;} }; int main(){ CSuper *pSuper = new CSub(); Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 46
  47. pSuper->Speak(); delete pSuper; return 0; } 14) ðon ch ươ ng trình sau s : a. In ra màn hình: CSuper b. In ra màn hình: CSub c. B l i khi d ch #include using namespace std; class CSuper{ public : virtual int Speak( int x){cout Speak(5); delete pSuper; return 0; } 15) ðon ch ươ ng trình sau s : a. In ra màn hình: CSuper b. In ra màn hình: CSub c. B l i khi d ch (đáp án) #include using namespace std; class CSuper{ public : virtual static void Speak(){cout Speak(); delete pSuper; return 0; } 16) ðon ch ươ ng trình sau s : a. In ra màn hình: 3222 Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 47
  48. b. In ra màn hình: 1232 c. In ra màn hình: 3232 d. B l i khi d ch t i dịng: “ sub.Speak(10); ” (đáp án) e. B l i khi d ch t i dịng: “ sub.CSuper::Speak(10); ” #include using namespace std; class CSuper{ public : virtual int Speak(){cout Speak(); pSuper->Speak(10); CSub sub; sub.Speak(10); sub.CSuper::Speak(10); delete pSuper; return 0; } 17) ðon ch ươ ng trình sau s : a. In ra màn hình: 3222 (đáp án) b. B l i khi d ch t i dịng: “ using CSuper::Speak; ” c. In ra màn hình: 1222 d. B l i khi d ch t i dịng: “sub.Speak(10); ” e. B l i khi d ch t i dịng: “ sub.CSuper::Speak(10); ” #include using namespace std; class CSuper{ public : virtual int Speak(){cout Speak(); pSuper->Speak(10); CSub sub; sub.Speak(10); Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 48
  49. sub.CSuper::Speak(10); delete pSuper; return 0; } 18) ðon ch ươ ng trình sau s : a. In ra màn hình: 43432 ( đáp án) b. In ra màn hình: 12432 c. B l i khi d ch ti dịng: “pSuper->Speak(10); ” d. B l i khi d ch t i dịng: “ sub.Speak(10); ” #include using namespace std; class CSuper{ public : virtual int Speak(){cout Speak(); pSuper->Speak(10); CSub sub; sub.Speak(); sub.Speak(10); sub.CSuper::Speak(10); delete pSuper; return 0; } 19) ðon ch ươ ng trình sau s : a. B l i khi d ch (đáp án) b. In ra màn hình: 14 c. In ra màn hình: 44 d. In ra màn hình: 41 #include using namespace std; class CSuper{ private : virtual int Speak(){cout<< "1" ; return 0;} }; class CSub: public CSuper{ public : virtual int Speak(){cout<< "4" ; return 0;} }; int main(){ Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 49
  50. CSuper *pSuper = new CSub(); pSuper->Speak(); CSub sub; sub.Speak(); delete pSuper; return 0; } 20) ðon ch ươ ng trình sau s : a. In ra màn hình s 4 (đáp án) b. B l i khi dch #include using namespace std; class CSuper{ public : virtual int Speak(){cout Speak(); delete pSuper; return 0; } 21) fd Biên so n: ð ng Thanh D ũng, khoa CNTT - ðH SPKT TP.HCM 50