Więc zacznijmy od początku. Konstruktor to specjalna metoda która wywołuje się w momencie tworzenia obiektu. To właśnie ona przygotowuje nasz obiekt do stanu użyteczności. W konstruktorze inicjalizujemy pola, alokujemy pamięć itp. Zaś destruktor to metoda która sprząta to co zostało po naszym obiekcie, głównie służy do zwalniania zarezerwowanych zasobów przez nasz obiekt w konstruktorze. Z zasady destruktor może być tylko jeden zaś konstruktorów może być mnóstwo (poczytaj o przeciążaniu metod).
Weźmy taki o to kod:
#include <iostream>
using namespace std;
class Klasa1
{
public:
Klasa1()
{
cout<<"Konstruktor Klasa1"<<endl;
}
~Klasa1()
{
cout<<"Destruktor Klasa1"<<endl;
}
};
int main() {
Klasa1 A;
return 0;
}
Po wykonaniu naszego kodu na standardowe wyjście zostaną wypisane dwa napisy:
Konstruktor Klasa1
Destruktor Klasa1
Jest to jak najbardziej ok, tworzymy obiekt następnie z momentem zakończenia funkcji main obiekt ten jest usuwany.
Do naszego kodu dopiszmy kolejną klasę. Nazwijmy ją Klasa2. Następnie wprowadźmy relacje dziedziczenia. Klasa2 dziedziczy publicznie po Klasie1. Prezentuję tą sytuację poniższy kod:
#include <iostream>
using namespace std;
class Klasa1
{
public:
Klasa1()
{
cout<<"Konstruktor Klasa1"<<endl;
}
~Klasa1()
{
cout<<"Destruktor Klasa1"<<endl;
}
};
class Klasa2:public Klasa1
{
public:
Klasa2()
{
cout<<"Konstruktor Klasa2"<<endl;
}
~Klasa2()
{
cout<<"Destruktor Klasa2"<<endl;
}
};
int main() {
Klasa2 A;
return 0;
}
Uruchamiając ten kod naszym oczom ukażą się następujące napisy.
Konstruktor Klasa1
Konstruktor Klasa2
Destruktor Klasa2
Destruktor Klasa1
Jak widać najpierw wywoływany jest konstruktor klasy bazowej, a dopiero później klasy której obiekt tworzymy. Jest to jak najbardziej logiczne. Ponieważ klasa bazowa (Klasa1) to taki fundament na którym budujemy nasz dom to jest (Klase2). Natomiast rozbiórkę tak wybudowanego domu nie można zacząć od fundamentu (Klasa1) bo wszystko się nam na głowę zawali. Najpierw niszczymy to co zbudowaliśmy na naszym fundamencie czyli wywołujemy destruktor klasy Klasa2 zaś później dopiero sam fundament (Klasa1).
UWAGA!!!
Przeanalizujmy poniższy kod:
#include <iostream>
using namespace std;
class Klasa1
{
public:
Klasa1()
{
cout<<"Konstruktor Klasa1"<<endl;
}
~Klasa1()
{
cout<<"Destruktor Klasa1"<<endl;
}
};
class Klasa2:public Klasa1
{
public:
Klasa2()
{
cout<<"Konstruktor Klasa2"<<endl;
}
~Klasa2()
{
cout<<"Destruktor Klasa2"<<endl;
}
};
int main() {
Klasa1 *A=new Klasa2();
delete A;
return 0;
}
Powyższy kod względem swojego poprzednika różni się zawartością funkcji main. Uruchamiając ten kod na naszym wyjściu nie zobaczymy wywołania dwóch destruktorów lecz tylko jednego. Tego z klasy bazowej.
Konstruktor Klasa1
Konstruktor Klasa2
Destruktor Klasa1
Dzieje się tak dlatego że nasz obiekt jest typu Klasa1. Do tak stworzonej zmiennej zostaje przypisany obiekt klasy potomnej. Jest to częsty przypadek kiedy chcemy wykorzystać tzw polimorfizm. Jeśli chcieli byśmy aby i w tym przypadku destruktory wywoływały się poprawnie należy zdefiniować w klasie bazowej (Klasa1) tak zwany destruktor wirtualny.
#include <iostream>
using namespace std;
class Klasa1
{
public:
Klasa1()
{
cout<<"Konstruktor Klasa1"<<endl;
}
virtual ~Klasa1()
{
cout<<"Destruktor Klasa1"<<endl;
}
};
class Klasa2:public Klasa1
{
public:
Klasa2()
{
cout<<"Konstruktor Klasa2"<<endl;
}
~Klasa2()
{
cout<<"Destruktor Klasa2"<<endl;
}
};
int main() {
Klasa1 *A=new Klasa2();
delete A;
return 0;
}
Uruchamiając ten kod zobaczymy na standardowym wyjściu następujące dane.
Konstruktor Klasa1
Konstruktor Klasa2
Destruktor Klasa2
Destruktor Klasa1