Destructorul
Destructorul eliberează resursele utilizate de obiect și șterge variabilele nestatice ale obiectului. El este apelat automat atunci când obiectul este șters. Ștergerea obiectului are loc în următoarele cazuri:
- când se încheie execuția blocului de cod în care au fost definite obiectele
- când este șters un container (de exemplu, un array) care conține obiecte
- când este șters un obiect care conține ca variabile alte obiecte
- obiectele create dinamic sunt șterse prin aplicarea operatorului delete pe pointerul către obiect
De fapt, destructorul este o funcție care poartă numele clasei (ca și constructorul), dar în fața lui se află tilde ~:
~nume_clasă()
{
// codul destructorului
}
Destructorul nu are tip de returnare și nu primește parametri. Fiecare clasă poate avea doar un singur destructor.
De obicei, destructorul nu este folosit prea des și este utilizat în principal pentru eliberarea resurselor asociate. De exemplu, un obiect al unei clase folosește un fișier, iar în destructor putem închide fișierul. Sau dacă în clasă se alocă memorie cu new, în destructor putem elibera acea memorie.
Mai întâi, să analizăm o definiție simplă de destructor:
#include <iostream>
class Person
{
public:
Person(std::string p_name)
{
name = p_name;
std::cout << "Person " << name << " created" << std::endl;
}
~Person()
{
std::cout << "Person " << name << " deleted" << std::endl;
}
private:
std::string name;
};
int main()
{
{
Person tom{"Tom"};
Person bob{"Bob"};
} // obiectele Tom și Bob sunt distruse
Person sam{"Sam"};
} // obiectul Sam este distrus
În clasa Person este definit un destructor care doar afișează un mesaj la distrugerea obiectului:
~Person()
{
std::cout << "Person " << name << " deleted" << std::endl;
}
În funcția main sunt create trei obiecte Person. Dintre ele, două sunt create într-un bloc de cod interior:
{
Person tom{"Tom"};
Person bob{"Bob"};
}
Acest bloc de cod definește limitele domeniului de vizibilitate în care există aceste obiecte. Și, când execuția blocului se termină, ambele variabile sunt distruse, iar pentru ambele obiecte sunt apelate destructorii.
După aceea este creat al treilea obiect – sam:
int main()
{
{
Person tom{"Tom"};
Person bob{"Bob"};
} // obiectele Tom și Bob sunt distruse
Person sam{"Sam"};
} // obiectul Sam este distrus
Deoarece el este definit în contextul funcției main, este distrus la finalul acestei funcții. În rezultat, obținem următoarea ieșire în consolă:
Person Tom created
Person Bob created
Person Bob deleted
Person Tom deleted
Person Sam created
Person Sam deleted
Un exemplu puțin mai practic. Să presupunem că avem un contor pentru obiectele Person sub forma unei variabile statice. Dacă în constructor, la fiecare obiect creat, contorul este mărit, în destructor putem scădea contorul:
#include <iostream>
class Person
{
public:
Person(std::string p_name)
{
name = p_name;
++count;
std::cout << "Person " << name << " created. Count: " << count << std::endl;
}
~Person()
{
--count;
std::cout << "Person " << name << " deleted. Count: " << count << std::endl;
}
private:
std::string name;
static inline unsigned count{}; // contorul obiectelor
};
int main()
{
{
Person tom{"Tom"};
Person bob{"Bob"};
} // obiectele Tom și Bob sunt distruse
Person sam{"Sam"};
} // obiectul Sam este distrus
Ieșirea în consolă a programului:
Person Tom created. Count: 1
Person Bob created. Count: 2
Person Bob deleted. Count: 1
Person Tom deleted. Count: 0
Person Sam created. Count: 1
Person Sam deleted. Count: 0
Este important de reținut că execuția destructorului nu elimină fizic obiectul. Eliminarea propriu-zisă are loc în etapa explicită de distrugere, care urmează execuției destructorului.
De asemenea, merită menționat că pentru orice clasă care nu definește propriul destructor, compilatorul generează automat unul. De exemplu, dacă clasa Person nu ar avea un destructor definit, compilatorul ar genera automat următorul destructor:
~Person(){}