Jeśli tworzysz obiekt na stosie (a nie na stercie czyli przez wskaźnik i new/delete), to obiekt jest niszczony w momencie, gdy „wylatuje z zasięgu”. W twoim przypadku nie trzeba wywoływać destruktora, bo i tak się wykona. Zobacz na ten przykład:
int main()
{
create obj(wielk); // wywołanie konstruktora create::create(int)
obj.view(); // ok
if (true) // jakiś warunek, dla uproszczenia — zawsze prawdziwy
{
create obj2 = obj; // wywołanie konstruktora kopiującego.
// jeśli takiego nie ma, wywoła się domyślny,
// który skopiuje tylko wskaźnik zamiast wszystkich wskazywanych danych.
obj2.view(); // ok
obj.view(); // ok
} // skończył się zasięg zmiennej obj2. wykona się destruktor dla obj2, a w nim delete na wskaźniku.
obj.view(); // BUM! odwołanie do wskaźnika tego samego, który został zwolniony w destruktorze obj2.
} // skończył się zasięg zmiennej obj, wykona się destruktor.
Rozwiązaniem są:
a) zablokowanie konstruktora kopiującego i operatora kopiowania-przypisania, czyli zadeklarowanie ich jako prywatne (i nie definiowanie ich),
private:
create::create(const create&);
create& operator=(const create&);
wtedy niemożliwe staje się przypisanie obj2=obj.
b) zdefiniowanie prawidłowych:
public:
create(const create& other)
{
// kopiowanie wszystkich pól o typach prostych:
ile = other.ile; // musisz dodać to pole do klasy i wypełniać je w konstruktorze!
// alokacja pamięci w nowym obiekcie:
wsk = new int(ile);
// kopiowanie właściwych danych:
memcpy(wsk, other.wsk, ile * sizeof(int));
}
create& operator=(const create &other)
{
if (this != &other) // zabezpieczenie przed przypisaniem do samego siebie
{
ile = other.ile; // j.w.
wsk = new int(ile);
memcpy(wsk, other.wsk, ile * sizeof(int));
}
return *this; // bo tak.
}
Tak, jest to upierdliwe — zwłaszcza w klasach, w których często dodajesz nowe pola. Wtedy trzeba pamiętać, by je dodać do tego konstruktora i operatora. Cóż, C++ to nie jest język w którym pisze się przyjemnie.
Ja w przypadku większych klas przyjmuję punkt a)
, czyli zabraniam kopiowania, a operuję tylko na wskaźnikach (czyli u ciebie create *obj=new create(wielk)
, pamiętać o delete
). Wtedy mój przykład będzie wyglądał tak:
int main()
{
create *obj = new create(wielk); // wywołanie konstruktora create::create(int)
obj.view(); // ok
if (true)
{
create *obj2 = obj; // tylko skopiowanie wskaźnika
obj2.view(); // ok
obj.view(); // ok
// delete obj2; // możemy to tu zrobić, ale nie musimy. jednak spowodowałoby to też zniszczenie obj.
} // skończył się zasięg zmiennej obj2. nic się nie dzieje.
obj.view(); // ok
delete obj; // wywołanie destruktora.
} // skończył się zasięg zmiennej obj, nic się nie dzieje (dlatego trzeba pamiętać o delete)
Jak widać, w C++ obiekt albo sam się zniszczy, albo robimy to ręcznie, ale przez delete
. Ręczne wywoływanie destruktora jest praktycznie niepotrzebne.