Dziedziczenie wielobazowe (wielodziedziczenie)
Jeśli klasa ma więcej niż jedną bezpośrednią klasę bazową, mówimy o wielodziedziczeniu (dziedziczenie wielobazowe, dziedziczenie wielokrotne, ang. multiple inheritance).
class Punkt { int x,y; public: Punkt(...) {...} ~Punkt(...) {...} drukuj() {...} };
|
class Kolor { short kolor; public: Kolor(...) {...} ~Kolor(...) {...} drukuj() {...} };
|
class Punktkol : public Punkt, public Kolor
{
public:
Punktkol(...) {...}
~Punktkol(...) {...}
drukuj() {...}
};
Kolejność wywoływania konstruktorów: najpierw z klas bazowych wg listy, następnie z klasy pochodnej
Przykład: każda z klas ma swoją funkcję drukuj(), której zadaniem jest wyświetlenie wartości zmiennych obiektu danej klasy
#include <iostream>
class Punkt
{
int x, y ;
public :
Punkt (int xx, int yy)
{ x=xx ; y=yy ; }
~Punkt () { }
void drukuj ()
{ cout << "Wspolrzedne: " << x << " " << y << endl ; }
};
class Kolor
{
short Kolor ;
public :
Kolor (int kk)
{ Kolor = kk ;}
~Kolor () { }
void drukuj ()
{ cout << "Kolor : " << Kolor << "\n" ; }
} ;
class PunktKolor : public Punkt, public Kolor
{
public :
PunktKolor (int, int, int) ;
~PunktKolor () { }
// w obydwu klasach bazowych zmienne są prywatne,
// dostęp do nich zapewniają funkcje
void drukuj ()
{ Punkt::drukuj () ; Kolor::drukuj () ; }
} ;
PunktKolor::PunktKolor(int xx, int yy, int kk) : Punkt (xx, yy),Kolor (kk)
{ }
main()
{
PunktKolor p(2,4,0) ;
cout << "------------\n" ;
p.drukuj () ; // funkcja drukuj() z Punktkol
cout << "------------\n" ;
p.Punkt::drukuj () ; // funkcja drukuj() z Punkt
cout << "------------\n" ;
p.Kolor::drukuj () ; // funkcja drukuj() z Kolor
cout << "------------\n" ;
}
Przykład: chcemy utworzyć listę do obsługi zbioru obiektów tego samego typu, np. punktów (tzw. lista kontenerowa).
// klasa Lista: obsługuje listę, // obiekty listy są wskazywane struct element { element *nastepny; // void * - lista może obsługiwać // zbiór obiektów różnego typu void *zawartosc; };
class Lista { element * glowa; public: Lista(); ~Lista(); // zwróć adres kolejnego obiektu void* kolejny(); // dodaj kolejny obiekt void dodaj(void *); ... }; |
// klasa Punkt jest przykładem // klasy obiektów przechowywanych // za pomocą listy class Punkt { int x,y; public: Punkt(); void drukuj(); };
void Punkt drukuj () { cout << x << " " << y << endl; }
|
public: Lista_punktow() {} void drukuj(); }; void Lista_punktow::drukuj () { ... while ( ! koniec() ) { Punkt * wsk = (Punkt *) kolejny() ; wsk->drukuj () ; } } |
Typ void
Nie ma obiektów typu void, tzn. nie można zadeklarować: void x;
Używa się go do wskazania, że funkcja nie zwraca żadnej wartości: void f(); można powiedzieć, że w tym wypadku void jest pseudotypem, wprowadzonym po to, aby gramatyka języka była bardziej regularna.
Używa się go również jako typu podstawowego dla wskaźników do obiektów nieznanego, dowolnego typu:
void* wsk; wskaźnik void* można bowiem jawnie przekształcić do innego typu.
Podstawowym zastosowaniem void* jest przekazywanie wskaźników do funkcji, w których nie można niczego zakładać o typie obiektu i przekazywanie z funkcji obiektów bez typu. Przed użyciem takiego obiektu, trzeba dokonać jawnej konwersji typu.
#include <iostream>
// klasa Lista
struct element // element listy
{ element * nastepny ; // wskaźnik na następny element listy
void * zawartosc ; // wskaźnik na przechowywany obiekt
} ;
class Lista
{ element * glowa ; // wskaźnik na pierwszy element listy
element * biezacy ; // wskaźnik na bieżący element listy
public :
Lista ()
{ glowa = NULL ; biezacy = glowa ; }
~Lista () ;
void dodaj (void *) ; // dodaj element na początek listy
void pierwszy () // ustaw się na pierwszym elemencie listy
{ biezacy = glowa ;
}
void * kolejny () // zwróć adres bieżącego obiektu,
// ustaw się na kolejnym elemencie listy
{ void * wsk_el = NULL ;
if (biezacy != NULL){ wsk_el = biezacy -> zawartosc ;
biezacy = biezacy -> nastepny ;
}
return wsk_el ;
}
int koniec () { return (biezacy == NULL) ; }
} ;
Lista::~Lista ()
{ element * nast ;
biezacy = glowa ;
while (biezacy != NULL )
{ nast = biezacy->nastepny ; delete biezacy ; biezacy = nast ; }
}
void Lista::dodaj (void * nowy)
{ element * wsk_el = new element ;
wsk_el->nastepny = glowa ; wsk_el->zawartosc = nowy ;
glowa = wsk_el ;
}
// klasa Punkt
class Punkt
{
int x, y ;
public :
Punkt (int xx=0, int yy=0) { x=xx ; y=yy ; }
void drukuj () { cout << "Wspolrzedne : " << x << " " << y << endl ; }
} ;
// klasa Lista_punktow
class Lista_punktow : public Lista, public Punkt
{ public :
Lista_punktow () {}
void drukuj () ;
} ;
void Lista_punktow::drukuj ()
{ pierwszy() ;
while ( ! koniec() )
{
Punkt * wsk = (Punkt *) kolejny() ;
wsk->drukuj () ;
}
}
int main()
{
Lista_punktow l ;
Punkt a(1,2), b(4,5), c(6,0) ;
l.dodaj (&a) ; l.drukuj () ; cout << "---------\n" ;
l.dodaj (&b) ; l.drukuj () ; cout << "---------\n" ;
l.dodaj (&c) ; l.drukuj () ; cout << "---------\n" ;
}
Przykład: chcemy utworzyć listę do obsługi zbioru obiektów różnego typu. Problem, który trzeba rozwiązać to możliwość różnego przetwarzania różnych obiektów. Wskazuje to na potrzebę zastosowania wiązania dynamicznego i funkcji wirtualnych.
{ element * nastepny ; void * zawartosc ; } ; class Lista { element * glowa ; public : Lista (); ~Lista () ; void * kolejny (); void dodaj (void *) ; void drukuj_Lista () ; } ; void Lista::drukuj_Lista () { Bazowa * wsk ; pierwszy() ; while ( ! koniec() ) { wsk = (Bazowa *) kolejny() ; wsk->drukuj () ; } } |
|
class Bazowa { public : virtual void drukuj () = 0 ; } ; |
|
class Punkt : public Bazowa { int x, y ; public : Punkt (); void drukuj () { cout << x << " " << y << endl; } ... }; |
class Zespolone : public Bazowa { double rzeczywista, urojona ; public : Zespolone (); void drukuj() { cout << rzeczywista << " + " << urojona << "i\n" ; } ... } ; |
#include <iostream>
class Bazowa
{
public :
virtual void drukuj () = 0 ;
} ;
struct element
{ element * nastepny ;
void * zawartosc ;
} ;
class Lista
{ element * glowa ;
element * biezacy ;
public :
Lista () { glowa = NULL ; biezacy = glowa ; }
~Lista () ;
void dodaj (void *) ;
void pierwszy () { biezacy = glowa ; }
void * kolejny ()
{ void * wsk_nast = NULL ;
if (biezacy != NULL){ wsk_nast = biezacy -> zawartosc ;
biezacy = biezacy -> nastepny ;
}
return wsk_nast ;
}
void drukuj_Lista () ;
int koniec () { return (biezacy == NULL) ; }
} ;
Lista::~Lista ()
{ element * nast ;
biezacy = glowa ;
while (biezacy != NULL )
{ nast = biezacy->nastepny ; delete biezacy ; biezacy = nast ; }
}
void Lista::dodaj (void * nowy)
{ element * wsk = new element ;
wsk->nastepny = glowa ;
wsk->zawartosc = nowy ;
glowa = wsk ;
}
void Lista::drukuj_Lista ()
{ Bazowa * wsk ;
pierwszy() ;
while ( ! koniec() )
{ wsk = (Bazowa *) kolejny() ;
wsk->drukuj () ;
}
}
class Punkt : public Bazowa
{ int x, y ;
public :
Punkt (int xx=0, int yy=0) { x=xx ; y=yy ; }
void drukuj ()
{ cout << "Wspolrzedne punktu : " << x << " " << y << endl ; }
} ;
class Zespolone : public Bazowa
{ double rzeczywista, urojona ;
public :
Zespolone (double r=0, double i=0) { rzeczywista=r ; urojona=i ; }
void drukuj ()
{ cout << "Zespolone : " << rzeczywista << " + " << urojona << "i\n" ; }
} ;
main()
{ Lista l ;
Punkt a(1,2), b(4,5) ;
Zespolone x(3.4,5.1), y(1.8,3.5) ;
l.dodaj (&a) ; l.drukuj_Lista () ;
cout << "---------\n" ;
l.dodaj (&x) ; l.drukuj_Lista () ;
cout << "---------\n" ;
l.dodaj (&y) ; l.drukuj_Lista () ;
cout << "---------\n" ;
l.dodaj (&b) ; l.drukuj_Lista () ;
cout << "---------\n" ;
}
1
8
Punkt
Kolor
Punkt kolorowy
Tutaj jawnie jest dokonana konwersja wskaźnika void* do wskaźnika Punkt *
klasa Punkt
klasa Lista
głowa
punkt 2
punkt 1
punkt 3
głowa
obiekt 1
obiekt 2
obiekt 3
Lista klas bazowych
Funkcja drukuj() klasy Punkt
Konwersja wskaźnika void * do wskaźnika Punkt *
Klasa Bazowa „zastępuje” klasy poszczególnych obiektów
Funkcja drukuj() jest wirtualna,
zostanie tutaj wywołana funkcja drukuj() właściwego obiektu