Wirtualny destruktor w C++

Wirtualny Destruktor W C



C++ to język, który jest używany do zapewnienia podstaw w zakresie podstawowej koncepcji programowania i wzmacnia logiczne myślenie programistów. W C++ OOP odgrywa istotną rolę, ponieważ OOP jest językiem zorientowanym obiektowo, który tworzy obiekty klas. W OOP badamy klasy i obiekty. Klasy zawierają elementy danych, które są zmiennymi różnych typów i różnych funkcji składowych. Za pomocą instancji uzyskujemy dostęp do danych dowolnej klasy. Każda klasa ma swojego konstruktora i destruktora podczas tworzenia klasy. Konstruktor jest wywoływany sam, gdy tworzony jest obiekt tej klasy. Możemy również zainicjować zmienne klasy wewnątrz konstruktora. Destruktory są również tworzone automatycznie za pomocą konstruktora, ale destruktory niszczą obiekt i jest to ostatnia funkcja wywoływana przed zniszczeniem obiektu. Tworzona jest nazwa klasy, na przykład klasa „Zawód”. Jej konstruktorem jest Profession(), a destruktorem ~Profession(). Cała trójka nosi to samo imię.

Po omówieniu OOP, konstruktorów i destruktorów, porozmawiajmy teraz o destruktorach wirtualnych. Wirtualne destruktory, jak sama nazwa wskazuje, niszczą obiekt. Mamy klasę bazową i klasę pochodną, ​​która jest pochodną klasy bazowej. Obie klasy mają swoje konstruktory i destruktory. Wirtualny destruktor uwalnia wspomnienia, które są przydzielane przez obiekt klasy pochodnej, jednocześnie usuwając obiekty klasy pochodnej za pomocą wskaźnika klasy bazowej ze słowem kluczowym „virtual”.

Dlaczego używamy wirtualnego destruktora?

Gdy wykonywanie funkcji składowych klasy jest zakończone lub wykonywanie metody main() zbliża się do końca, destruktor jest automatycznie wywoływany w celu zwolnienia pamięci przydzielonej podczas tworzenia obiektu. Dlaczego używamy wirtualnego destruktora? Po usunięciu klasy bazowej, która wskazuje na klasę pochodną, ​​w tym miejscu używany jest wskaźnik (*). Destruktor klasy bazowej jest wywoływany tylko podczas tego procesu. Destruktor klasy pochodnej nie jest wywoływany, co prowadzi do problemów. Jednym z nich jest problem wycieku pamięci. Aby uniknąć tego problemu i zabezpieczyć nasz kod, wirtualnie niszczymy obiekty, aby zwolnić miejsce w pamięci, które zostało przydzielone podczas tworzenia obiektów, usuwając destruktor klasy podstawowej.

Podstawowy przykład C++ bez wirtualnego destruktora

Zobaczmy, jak działa program bez wirtualnego destruktora z prostym programem usuwającym wskaźnik.

Kod:

#include

przy użyciu przestrzeni nazw std ;
klasa Klasa_rodzic0
{
publiczny :
Klasa_nadrzędna0 ( )
{ cout << „Konstruktor klasy nadrzędnej” << koniec ; }
~Klasa_rodzica0 ( )
{ cout << „Destruktor klasy nadrzędnej” << koniec ; }
} ;
klasa Dziecko_1 : publiczna klasa_rodzica0
{
publiczny :
Dziecko_1 ( )
{ cout << „Konstruktor klasy potomnej” << koniec ; }
~Dziecko_1 ( )
{ cout << „Destruktor klasy potomnej” << koniec ; }
} ;
int Główny ( )
{
Klasa_nadrzędna0 * wskaźnik = nowe Dziecko_1 ( ) ;
usuń wskaźnik ;
powrót 0 ;
}

Ten kod wyjaśnia, w jaki sposób kod jest wykonywany bez wirtualnego destruktora. Przede wszystkim utwórz klasę o nazwie „Parent_Class0”, która będzie klasą nadrzędną. Wewnątrz tej klasy utwórz konstruktor i destruktor. Jak wiemy, konstruktor i destruktor mają takie same nazwy jak klasa. Destruktor jest reprezentowany podobnie do konstruktora, ale ma symbol (~), który odróżnia go od konstruktora. Wewnątrz konstruktora i destruktora wypisz komunikat, używając „cout<<”. Teraz utwórz kolejną klasę, która jest „Child_1”. Ta klasa pochodzi od klasy nadrzędnej „Parent_Class0”. Klasa pochodna ma swojego konstruktora i destruktora, które zawierają komunikat do wydrukowania na ekranie wyjściowym.

W metodzie main() tworzymy instancję klasy „Parent_Class0” i przypisujemy jej klasę pochodną. Kluczową kwestią do zapamiętania w tym przypadku jest to, że używamy wskaźnika do pobrania klasy nadrzędnej. Kiedy wchodzi do klasy nadrzędnej, wykonuje konstruktor klasy nadrzędnej. Następnie przechodzi do klasy potomnej i wykonuje jej konstruktora. Przed wykonaniem destruktora klasy potomnej musi wykonać destruktor klasy nadrzędnej. Kompilator wykonuje destruktor klasy nadrzędnej i kończy działanie klasy bez wykonywania destruktora klasy podrzędnej. To jest problem; nie uwalnia pamięci klasy dziecka. Reprezentuje konstruktor klasy nadrzędnej, konstruktor klasy podrzędnej i destruktor klasy nadrzędnej. To pokazuje, że destruktor klasy potomnej nie jest wykonywany. Po tym wykonaniu usuwamy wskaźnik w funkcji main().

Wynik:

C++ Przykład z wirtualnym destruktorem

Omówmy wirtualny destruktor za pomocą prostego kodu, aby rozróżnić, jak działa z wirtualnym destruktorem i bez niego.

Kod:

#include

przy użyciu przestrzeni nazw std ;
klasa Klasa_rodzic0
{
publiczny :
Klasa_nadrzędna0 ( )
{ cout << „Konstruktor klasy nadrzędnej” << koniec ; }
wirtualna ~Klasa_rodzica0 ( )
{ cout << „Destruktor klasy nadrzędnej” << koniec ; }
} ;
klasa Dziecko_1 : publiczna klasa_rodzica0
{
publiczny :
Dziecko_1 ( )
{ cout << „Konstruktor klasy potomnej” << koniec ; }
wirtualny ~Dziecko_1 ( )
{ cout << „Destruktor klasy potomnej” << koniec ; }
} ;
int Główny ( )
{
Klasa_nadrzędna0 * wskaźnik = nowe Dziecko_1 ( ) ;
usuń wskaźnik ;
powrót 0 ;
}

Pierwszy program wyjaśnił problem, przed którym stoimy bez wirtualnego destruktora. Teraz ten kod rozwiąże ten problem za pomocą wirtualnego destruktora. Najpierw skopiuj pierwszy kod i po prostu dodaj jedno słowo kluczowe w dwóch miejscach w tym programie. Tym słowem jest „wirtualny”. Wstaw to słowo wraz z destruktorem klasy nadrzędnej „Parent_Class0”. Podobnie wspomnij o tym z destruktorem klasy potomnej, którym jest „Child_1”, który pochodzi od klasy nadrzędnej. To „wirtualne” słowo kluczowe wprowadza niewielką zmianę i najpierw wykonuje destruktor klasy potomnej „Child_1”. Następnie wykonuje destruktor klasy nadrzędnej „Parent_Class0”. Reszta programu działa tak samo, jak działa bez wirtualnego destruktora. Dodając ten mały fragment kodu, możemy uratować naszą pamięć przed wyciekiem. Teraz wyświetla cztery komunikaty na konsoli. Najpierw konstruktor klasy nadrzędnej, następnie konstruktor klasy podrzędnej, destruktor klasy podrzędnej i destruktor klasy nadrzędnej. Na koniec usuwamy wskaźnik w metodzie main().

Wynik:

C++ Przykład czystego wirtualnego destruktora

W tym kodzie będziemy mówić o czystym destruktorze wirtualnym, o tym, jak działa i czym różni się od destruktora wirtualnego.

Kod:

#include

klasa Rodzic_0 {
publiczny :
wirtualny ~Rodzic_0 ( ) = 0 ;
} ;
Rodzic_0 :: ~Rodzic_0 ( )
{
standardowe :: cout << „Cześć, jestem Pure Destructor. Wezwałeś mnie!” ;
}
klasa Dziecko_0 : publiczny rodzic_0 {
publiczny :
~Dziecko_0 ( ) { standardowe :: cout << „Destruktor pochodny jest tutaj \n ' ; }
} ;

int Główny ( )
{
Rodzic_0 * ptr_0 = nowe Dziecko_0 ( ) ;
usuń ptr_0 ;
powrót 0 ;
}

Klasa nadrzędna „Parent_0” jest tworzona w pierwszym kroku kodu. Wewnątrz niego utwórz wirtualny destruktor nadrzędny i przypisz mu wartość 0. To ustawia destruktor wirtualny na czysty destruktor wirtualny, co oznacza, że ​​klasa nadrzędna jest teraz abstrakcyjna i nie możemy tworzyć instancji tej klasy. Poza klasą nadrzędną „Parent_0” zdefiniuj destruktory i std::cout. Wymagany tekst jest wyświetlany przy użyciu std::cout. Następnie wyprowadź klasę „Child_0” z klasy nadrzędnej i zdefiniuj jej destruktor. Wewnątrz destruktora wypisz wiadomość. W funkcji main() utwórz wskaźnik klasy nadrzędnej i przypisz do niej klasę podrzędną.

Kompilator przechodzi do klasy nadrzędnej „Parent_0”. Kiedy tworzony jest wskaźnik, jego konstruktor jest automatycznie wywoływany. Następnie kompilator przechodzi do klasy potomnej, aby wywołać swojego konstruktora. Po pomyślnym wykonaniu konstruktora wykonuje destruktor klasy potomnej „Child_0”. Następnie wykonuje destruktor klasy nadrzędnej. W ten sposób możemy stworzyć czysty wirtualny destruktor. Nie zachęca się do jej używania, ponieważ stosując tę ​​​​metodę, klasa nadrzędna staje się abstrakcyjna, co czyni ją bezużyteczną. Najczęściej stosowaną metodologią jest wirtualny destruktor i jest to dobra praktyka.

Wynik:

Wniosek

O wirtualnym destruktorze uczyliśmy się od koncepcji OOP do konstruktorów i destruktorów. Po wyjaśnieniu tego wszystkiego szczegółowo omówiliśmy wirtualny destruktor z przykładami kodowania i czystym wirtualnym destruktorem. Zanim wyjaśnimy wirtualny destruktor, musimy wiedzieć o konstruktorach, destruktorach i dziedziczeniu. W przypadku dziedziczenia dziedziczymy klasy z klasy nadrzędnej. Klasy potomne mogą być więcej niż jedną, ale klasa nadrzędna jest tylko jedna. Wirtualne destruktory i czyste wirtualne destruktory są stosowane w dziedziczeniu, aby uchronić się przed wyciekiem pamięci. Od podstawowego do zaawansowanego przykładu, omówiliśmy wszystko, co powinieneś wiedzieć, aby zacząć używać i praktycznie niszczyć pamięć klasy pochodnej.