9.1 Ukrywanie metod, metody nadpisane... 1 9.2 Metody wirtualne, wirtualny destruktor... 2 9.3 Metody czysto wirtualne... 6 9.4 Klasy abstrakcyjne... 7 9.5 Wielodziedziczenie... 9 9.1 Ukrywanie metod, metody nadpisane Metoda z klasy bazowej jest zasłonięta przez metodę z klasy pochodnej. To znaczy, Ŝe obiekty klasy pochodnej nie mają dostępu do ukrytych metod z klasy bazowej. Nadpisanie metody (method overriding) z klasy bazowej oznacza zdefiniowanie w klasie pochodnej metody o takiej samej sygnaturze. Zwracany typ metody nadpisanej moŝe być taki, jak metody nadpisanej lub kowariantny z klasą bazową. Przykład 1. Metoda z klasy B zasłania obiektom typu B metodę z klasy A (w09-01-ukrywaniemetod.cpp). A(){cout << "A()" << endl; virtual ~A(){cout << "~A()" << endl; void metoda() const { cout << "metoda() w A" << endl; class B : public A { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; void metoda() const { cout << "metoda() w B" << endl; A a; B b; a.metoda(); b.metoda(); // metoda() z klasy A // metoda() z klasy B // obiekt b nie ma dostępu do metoda() z A // metoda() z A jest zasłonięta przez metoda() z B b.a::metoda(); // metoda() z klasy A A * p = new B; p->metoda(); // metoda() z klasy A 1
9.2 Metody wirtualne, wirtualny destruktor Definicja metody wirtualnej: virtual <zwracanytyp> nazwafunkcji(<typarg1> <nazwaarg1>, ){ <instrukcje>; Metoda wirtualna jest zdefiniowana i zaimplementowana w klasie bazowej. W klasie pochodnej jest ponownie zaimplementowana. Uwaga: metody wirtualne nie wymuszają implementacji w klasie pochodnej, poniewaŝ mają domyślną implementację w klasie bazowej. Klasa która zawiera metodę wirtualną nazywa się klasą polimorficzną. Metody statyczne nie mogą być deklarowane jako wirtualne. Nie moŝna definiować funkcji wirtualnych, tzn. funkcja musi naleŝeć do jakieś klasy aby być wirtualną. Jak kompilator odnajduje wirtualne metody w klasach? Kompilator tworzy tablicę, zwaną VTABLE, do której zapisuje adresy wirtualnych metod danej klasy. KaŜda klasa zawierająca wirtualną metodę posiada wskaźnik (vpointer) który wskazuje instancji klasy na adres tablicy VTABLE (zawierającą adresy wirtualnych metod). Przykład 1. Metody wirtualne (w09-02-metodywirtualne.cpp). virtual void metoda() const { cout << "metoda() w A" << endl; // void metoda() const { cout << "metoda() w A" << endl; class B : public A { void metoda() const { cout << "metoda() w B" << endl; void funk(a& a) { a.metoda(); B b; A *pa; pa= &b; pa->metoda(); // metoda() z B b.metoda(); A a; pa= &a; pa->metoda(); // metoda() z A a.metoda(); funk(b); // metoda() w B // gdy metoda() w A nie jest wirtualna, to // funk(b); // metoda() w A 2
W przypadku definiowania obiektów na stercie za pomocą upcast, naleŝy zadeklarować wirtualnego destruktora. Przykład 2. Definiowanie obiektów upcast. class B : public A { A *p= new B; // upcast // Przykład 3. Wywołanie wirtualnego destruktora przy usuwaniu obiektu ze sterty (w09-03-wirtualnydestruktor.cpp). #include<iostream> A(){ cout << "A()" << endl; virtual ~A(){ cout << "~A()" << endl; // ~A(){cout << "~A()" << endl; class B :public A { B(){ cout << "B()" << endl; ~B(){ cout << "~B()" << endl; A *p = new B; // upcast //---------------------------------------------------+ // Wynik działania programu ze zwykłym destruktorem /* A() B() ~A() // brak destruktora ~B() */ //---------------------------------------------------+ //---------------------------------------------------+ // Wynik działania programu z wirtualnym destruktorem /* A() B() ~B() ~A() */ //---------------------------------------------------+ 3
Przykład 4. Metody wirtualne, wirtualny destruktor, tworzenie obiektów na stercie upcast (w09-04-metodywirtualne.cpp). A(){cout << "A()" << endl; virtual ~A(){cout << "~A()" << endl; void metoda1() const { cout << "metoda1() w A" << endl; virtual void metoda2() const { cout << "metoda2() w A" << endl; class B : public A { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; void metoda1() const { cout << "metoda1() w B" << endl; void metoda2() const { cout << "metoda2() w B" << endl; A *p = new B; p->metoda1(); p->metoda2(); // upcast // wywołana jest metoda() z klasy A // wywołana jest metoda() z klasy B p->a::metoda2(); // wywołana jest metoda() z klasy A 4
Deklaracja metody wirtualnej nie musi zawierać specyfikatora virtual. Metoda zdefiniowana jako wirtualna w klasie bazowej, nadpisana w klasie pochodnej jest teŝ wirtualna. Przykład 5. (w09-05-metodywirtualnebezvirtual.cpp) A(){cout << "A()" << endl; virtual ~A(){cout << "~A()" << endl; virtual void metoda() const { cout << "metoda() w A" << endl; //void metoda() const { cout << "metoda() w A" << endl; class B : public A { B(){cout << "B()" << endl; virtual ~B(){cout << "~B()" << endl; // metoda() jest wirtulna void metoda() const { cout << "metoda() w B" << endl; class C : public B { C(){cout << "C()" << endl; ~C(){cout << "~C()" << endl; void metoda() const { cout << "metoda() w C" << endl; A *p = new B; p->metoda(); // wywołana jest metoda() z klasy B p->a::metoda(); B *pp = new C; pp->metoda(); // wywołana jest metoda() z klasy C pp->b::metoda(); A *ppp = new C; ppp->metoda(); // wywołana jest metoda() z klasy C delete pp; delete ppp; 5
Przykład 6. Metoda fun(int) z klasy B zasłania metodę z fun() klasy A (w09-06-metodywirtualne-f(int).cpp). A(){cout << "A()" << endl; virtual ~A(){cout << "~A()" << endl; virtual void metoda() const { cout << "metoda() w A" << endl; virtual void fun() const { cout << "fun() w A" << endl; class B : public A { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; void metoda() const { cout << "metoda() w B" << endl; void fun(int x) const { cout << "fun(int) w B, x = " << x << endl; B b; // b.fun(); // błąd, fun(int) z B zasłania fun() z A b.fun(30); A *p = new B; p->metoda(); p->a::metoda(); // wywołana jest metoda() z klasy B // wywołana jest metoda() z A p->fun(); // wywołana jest fun() z klasy A // p->fun(10); // błąd, nie moŝna wywołać fun(int) z B // p->b::fun(20); // błąd, nie moŝna wywołać fun(int) z B 9.3 Metody czysto wirtualne Definicja metody czysto wirtualnej: virtual <zwracanytyp> nazwafunkcji(<typarg1> <nazwaarg1>, ) = 0; Przykład 1. Definicja metody czysto wirtualnej. class Abstrakcyjna { virtual void metoda() const = 0; 6
9.4 Klasy abstrakcyjne Klasa jest abstrakcyjna jeŝeli zawiera lub dziedziczy przynajmniej jedną metodę czysto wirtualną. Klasa abstrakcyjna nie moŝe być dziedziczona, tzn. moŝe być tylko klasą od której się dziedziczy. Nie moŝna utworzyć instancji (obiektu) klasy abstrakcyjnej. MoŜna definiować obiekty typy referencyjnego lub wskaźniki klasy abstrakcyjnej. Uwaga: deklaracja metody czysto wirtualnej, definiowanie klasy abstrakcyjnej, wymusza w klasie pochodnej implementację metody czysto wirtualnej. Przykład 1. Definicja klasy abstrakcyjna (w09-07-klasaabstrakcyjna.cpp) class Abs{ Abs(){cout << "Abs()" << endl; virtual ~Abs(){cout << "~Abs()" << endl; virtual void funkcja() const = 0; void func() { cout << "func() w Abs" << endl; // implementacja metody czysto wirtualnej w klasie abstrakcyjnej // implementacja nie inline void Abs::funkcja() const { cout << "funkcja() w Abs" << endl; class B : public Abs { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; // funkcja czysto wirtualna musi być implemtowana w klasie B // jeŝeli nie, to klasa B będze rownieŝ abstrakcyjna void funkcja() const { cout << "funkcja() w B " << endl; void func() { cout << "func() w B" << endl; void main( ) { // Abs a; // błąd, nie moŝna utworzyć instancji klasy abstakcyjnej B b; b.func(); // func() z klasy B b.funkcja(); // funkcja() z klasy B Abs * p = new B; p->func(); // func() z klasy Abs 7
Brak implementacji metody czysto wirtualnej w klasie pochodnej powoduje, Ŝe klasa pochodna jest klasą abstrakcyjną Przykład 2. Brak implementacji w klasie B metody() z klasy Abs powoduje, Ŝe B jest klasą abstrakcyjną (w09-08-klasyabstrakcyjne.cpp) class Abs { Abs(){cout << "Abs()" << endl; ~Abs(){cout << "~Abs()" << endl; virtual void metoda() = 0; // metoda czysto wirtualna class A : virtual public Abs { A(){cout << "A()" << endl; ~A(){cout << "~A()" << endl; private: void metoda(){ // implementacja metody czysto wirtualnej class B : virtual public Abs { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; // brak implementacji metody czysto wirtualnej class C : public A, public B { C(){cout << "C()" << endl; ~C(){cout << "~C()" << endl; private: void metoda(){ // implementacja metody czysto wirtualnej void main() { A a; // B b; // B jest klasą abstrakcyjną C c; 8
9.5 Wielodziedziczenie Przykład 1. Wielodziedziczenie typu public. class B { class C : public A, public B { Przykład 2. Klasy A i B są bazowa dla klasy C (w09-09-wielodziedziczenie.cpp). A(){ cout << "A()" << endl; ~A(){ cout << "~A()" << endl; class B { B(){ cout << "B()" << endl; ~B(){cout << "~B()" << endl; class C : public A, public B { C(){ cout << "C()" << endl; ~C(){cout << "~C()" << endl; C c; //-------------------------------------------+ // Wynik działania programu /* A() B() C() ~C() ~B() ~A() */ //-------------------------------------------+ 9
Przykład 3. Wielodziedziczenie biblioteka iostream. 10
Przykład 4. Wirtualne dziedziczenie (w09-10-wirtualnedziedziczenie.cpp). A(){cout << "A()" << endl; ~A(){cout << "~A()" << endl; // class B : public A { class B : virtual public A { B(){cout << "B()" << endl; ~B(){cout << "~B()" << endl; // class C : public A { class C : virtual public A { C(){cout << "C()" << endl; ~C(){cout << "~C()" << endl; class D : public B, public C { D(){ cout << "D()" << endl; ~D(){ cout << "~D()" << endl; void main() { D d; 11
//-----------------------------------------------------+ // Wynik działania programu z wirtualnym dziedziczeniem /* A() B() C() D() ~D() ~C() ~B() ~A() */ //-----------------------------------------------------+ //-----------------------------------------------------+ // Wynik działania programu ze zwykłym dziedziczeniem /* A() B() A() C() D() ~D() ~C() ~A() ~B() ~A() */ //-----------------------------------------------------+ 12