5.5. Konstruktory w klasach pochodnych
Konstruktory i destruktory nie są dziedziczone. Konstruktory oraz de-struktory można tworzyć dla klas bazowych, dla klas pochodnych oraz dla obu z nich jednocześnie. Gdy tworzony jest obiekt klasy pochodnej, wywo-ływany jest najpierw konstruktor klasy bazowej (o ile istnieje), a następnie konstruktor klasy pochodnej. Podczas tworzenia obiektu klasy pochodnej, aby zainicjować dane składowe klasy podstawowej musi zostać wywołany jej konstruktor. Jednak możliwe jest ich wywołanie odpowiednio w konstrukto-rach i operatokonstrukto-rach przypisania klasy pochodnej. Wywoływanie konstruktora klasy bazowej zilustrujemy przykładem.
Listing 5.5. klasa pochodna i konstruktory
1#include <i o s t r e a m . h>
5.5. Konstruktory w klasach pochodnych 99
23 private: double z ;
25 } ;
27 i n t main ( )
{punktB p1 ( 5 , 5 0 , 5 0 ) ;
29 p1 . pokaz ( ) ; g e t c h ( ) ;
31 return 0 ;
}
Po uruchomieniu tego programu mamy następujący wydruk:
x = 50 y = 50
z = 5
Klasa bazowa punkt ma postać:
c l a s s punkt // k l a s a bazowa
{
public:
punkt (double xx , double yy ) {x = xx ; y = yy ; } void pokazXY ( ) ;
private: double x , y ; } ;
Konstruktor klasy bazowej ma postać:
punkt ( double xx , double yy ) { x = xx ; y = yy ; }
i wymaga dwóch argumentów dla inicjacji składowych x i y. Klasa pochodna punktB zawiera dodatkową daną z i ma postać:
c l a s s punktB : public punkt // k l a s a pochodna {
public:
punktB (double k , double m, double n ) : punkt (m, n ) { z = k ; }
void pokaz ( ) { pokazXY ( ) ;
c o u t << " z ␣=␣ ␣ ␣ " << z << e n d l ; }
private: double z ; } ;
Konstruktor klasy pochodnej ma postać:
100 5. Dziedziczenie i hierarchia klas
punktB ( double k , double m, double n ) : punkt ( m, n )
{ z = k ; }
Konstruktor punktB musi wywołać konstruktor punkt, który jest odpowie-dzialny za zainicjowanie tej części obiektu klasy punktB, która pochodzi z klasy podstawowej punkt. W tym celu został wykorzystany tzw. inicjator składowej. Zastosowano rozszerzoną postać deklaracji konstruktora klasy pochodnej, która pozwala przekazywać argumenty do jednego lub kilku kon-struktorów klasy bazowej.
Formalna postać rozszerzonej deklaracji jest następująca:
konstruktor_klasa_pochodna ( l i s t a _ p a r a m e t r o w ) : nazwa_klasy_pochodnej_1 ( l i s t a argumentow ) , : nazwa_klasy_pochodnej_2 ( l i s t a argumentow ) ,
. . . . : nazwa_klasy_pochodnej_N ( l i s t a argumentow ) , {
// c i a l o k o n s t r u k t o r a k l a s y p o c h o d n e j }
Przy pomocy znaku dwukropka oddzielamy deklarację konstruktora klasy pochodnej od specyfikacji klasy bazowej. Gdy mamy więcej klas bazowych dziedziczonych przez klasę pochodną, używamy przecinka, do oddzielenia ich specyfikacji. W naszym przykładzie mamy tylko jedną klasę bazową, która ma dwa parametry, wobec czego mamy napis:
: punkt (m, n )
Jak wiec widzimy, nasz konstruktor klasy pochodnej jest funkcją o trzech argumentach, dwa argumenty muszą być przesłane do konstruktora klasy bazowej. W momencie utworzenia obiektu p1:
punktB p1 ( 5 , 5 0 , 50 ) ;
następuje przekazanie argumentów aktualnych 50 i 50 do konstruktora klasy bazowej i wykonanie konstruktora:
punkt ( xx , yy )
a następnie wykonanie konstruktora klasy pochodnej. Bardzo często zacho-dzi sytuacja, gdy tworzone są obiekty klasy pochodnej a klasa bazowa i klasa pochodna zawierają różne składowe. Jeżeli tworzony jest obiekt klasy pochodnej to kolejność wywoływania konstruktorów jest następująca:
— Wywoływane są konstruktory obiektów klasy bazowej
— Wywoływany jest konstruktor klasy bazowej
5.5. Konstruktory w klasach pochodnych 101
— Wywoływany jest konstruktor klasy pochodnej
— Destruktory wywoływane są w odwrotnej kolejności
Zagadnienie kolejności wywoływania konstruktorów zilustrujemy kolej-nym przykładem, w którym dwie klasy – bazowa i pochodna posiadają własne konstruktory i destruktory. Klasą bazową jest klasa punkt, której danymi chronionymi są współrzędne punktu, klasa pochodna kolo ma jedną daną prywatną, jest nią promień koła. Moment wywoływania konstrukto-rów i destruktokonstrukto-rów będzie wypisywany na ekranie w trakcie wykonywania funkcji testującej. Dla celów dydaktycznych, program składa się z 5 plików:
deklaracji klasy punkt, definicja klasy punkt, deklaracja klasy pochodnej kolo, definicja klasy kolo oraz funkcji testującej.
Zmienne x i y są danymi chronionymi klasy bazowej punkt. Definicja klasy pokazana jest na wydruku.
Listing 5.6. klasa pochodna - konstruktory
1 // p l i k " p u n k t . h"
// d e k l a r a c j a k l a s y p u n k t
3
#i f n d e f _PUNKT_H
5#define _PUNKT_H
7 c l a s s punkt {
9 public :
punkt (double = 0 . 0 , double = 0 . 0 ) ; // k o n s t r u k t o r domyslny
11 ~punkt ( ) ; // d e s t r u k t o r
protected:
13 double x , y ; } ;
15
#endif
Klasa posiada konstruktor domyślny i destruktor:
punkt ( double = 0 . 0 , double = 0 . 0 ) ; // k o n s t r u k t o r domyslny
~punkt ( ) ; // d e s t r u k t o r
W definicji klasy bazowej punkt, konstruktor jak i destruktor wyświetlają komunikat o obiekcie, na rzecz którego zostały wywołane. Deklaracja kla-sy punkt umieszczona jest umieszczona w odrębnym pliku i pokazana na wydruku.
Listing 5.7. klasa pochodna - konstruktory
// p l i k p u n k t . cpp
2 // d e f i n i c j a k l a s y p u n k t
102 5. Dziedziczenie i hierarchia klas
#include <i o s t r e a m . h>
4#include " punkt . h"
6 punkt : : punkt (double xx , double yy ) {
8 x = xx ; y = yy ;
c o u t << " k o n s t r u k t o r ␣ o b i e k t u ␣ k l a s y ␣ punkt ␣ ";
10 c o u t << " x=␣ " << x << " ␣ ␣ y=␣ " << y << e n d l ; }
12
punkt : : ~ punkt ( )
14 {
c o u t << " ␣ d e s t r u k t o r ␣ o b i e k t u ␣ k l a s y ␣ punkt ␣ ";
16 c o u t << " x=␣ " << x << " ␣ ␣ y=␣ " << y << e n d l ; }
Klasa pochodna kolo dziedziczy od klasy bazowej punkt i jej deklaracja pokazana jest na wydruku. Składowa klasy pr zadeklarowana została jako prywatna.
Listing 5.8. klasa pochodna - konstruktory
1 // p l i k " k o l o . h"
// d e k l a r a c j a k l a s y k o l o
3
#i f n d e f _KOLO_H
5#define _KOLO_H
7#include " punkt . h"
9 c l a s s k o l o : public punkt {
11 public :
k o l o (double r =0.0 , double xx =0.0 , double yy =0.0) ;
13 // k o n s t r u k t o r domyslny
~ k o l o ( ) ; // d e s t r u k t o r
15 private: double pr ;
17 } ;
19#endif
Klasa kolo posiada konstruktor i destruktor:
k o l o ( double r =0.0 , double xx =0.0 , double yy =0.0) ; // k o n s t r u k t o r domyslny
~ k o l o ( ) ; // d e s t r u k t o r
Na kolejnym wydruku pokazana jest definicja klasy pochodnej kolo.
5.5. Konstruktory w klasach pochodnych 103 Listing 5.9. klasa pochodna - konstruktory
1 // p l i k " k o l o . cpp "
Konstruktor klasy kolo wywołuje równocześnie konstruktor klasy punkt :
Listing 5.10. klasa pochodna; konstruktory; funkcja testująca
#include <i o s t r e a m . h>
2#include <c o n i o . h>
#include " punkt . h"
4#include " k o l o . h"
104 5. Dziedziczenie i hierarchia klas
}
16 g e t c h ( ) ;
return 0 ;
18 }
Przykładowy program pokazany na wydrukach 5.6 – 5.10 składa się z 5 plików, jego schemat fizyczny pokazano na rysunku. Fizyczną strukturę każdego programu C++ można określić jako zbiór plików. Niektóre z nich będą plikami nagłówkowymi(.h), a inne plikami implementacji (.cpp). Przyj-muje się, że komponent jest najmniejszym elementem projektu fizycznego.
Strukturalnie komponent stanowi niepodzielną jednostkę fizyczną, której części nie mogą być użyte niezależnie od pozostałych. Komponent składa się z jednego pliku nagłówkowego i jednego pliku implementacji. Graficzne przedstawienie komponentów znacznie ułatwia zbadanie wzajemnych rela-cji pomiędzy plikami i bibliotekami. W omawianym przykładzie, w funkrela-cji main() tworzony jest obiekt p1 klasy bazowej punkt. Następnie tworzone są kolejno dwa obiekty k1 i k2 klasy pochodnej kolo. Po uruchomieniu funkcji testującej mamy następujący wynik:
Na początku tworzony jest egzemplarz obiektu klasy punkt. Wywoływa-ny jest konstruktor a potem destruktor. Następnie tworzoWywoływa-ny jest obiekt k1 klasy pochodnej kolo. Wywoływany jest najpierw konstruktor klasy punkt, który pokazuje przekazane wartości a następnie wywołany jest konstruktor klasy kolo, który też pokazuje przekazane wartości. Kolejno wywoływany jest destruktor klasy koło i destruktor klasy punkt (wywoływanie destruk-torów odbywa się w odwrotnej kolejności). Następnie utworzony zostaje kolejny obiekt k2 klasy kolo.