Konstruktor i destruktor z dynamiczną alokacją

0

Witam.
Mam napisać program, który przy pomocy konstruktora stworzy dynamiczne pamięć a za pomocą destruktora zwolni ją.

Chciałbym Was zapytać, czy zrobiłem to dobrze:

 #include <iostream>
using namespace std;

class create
{
    private:
    int *wsk;
    public:
create(int ile)
{
wsk = new int(ile);

}
~create()
{
    delete wsk; // zwalnianie pamieci
}
void view()
{
    cout << *wsk << endl;
}


};

int main()
{
    int wielk;

cout << "Podaj wielkosc obiektu, ktory chcesz stworzyc: ";
cin >> wielk;

create obj(wielk); // tworze obiekt
obj.view(); //wyswietlam
obj.~create(); // kasowanie obiektu;



}

Proszę o ewentualne uwagi...
Nie jestem pewien czy dobrze to zrobiłem:

create(int ile)
{
wsk = new int(ile);

} 

Tutaj musi być ten nawias (ile) czy można to zrobić inaczej?

1

Zależy co chcesz zrobić. Chciałeś zarezerwować tyle pamięci ile ma parametr ile? Konstruktor int'a przyjmuje wartość jaką ma przybrać int po jego stworzeniu. Czyli linijka:

wsk = new int(10);

jest poprawna z tym, że rezerwujesz dla niej 4 bajty (int) i przypisujesz wartość 10.

0

Tak, mam int przypisać. Czyli mogę tak do oddać?

0

Tak, możesz jeszcze wyzerować wskaźnik, dla ścisłości: wsk = 0;.

0

Dlaczego jawnie wywołujesz destruktor? W twoim programie obj zostanie zniszczony dwa razy.

0

nie ma konstruktora kopiującego, przez co wywoływany jest automatyczny..
tutaj masz coś podobnego:
http://4programmers.net/Forum/C_i_C++/190622-operator_a_przypisanie?start=10

0

Tak się dzieje, że sam sie usuwa? Przecież jak dwa razy wywołam ten obiekt bez użycia destruktora to on się wypisze... a jak użyje destruktora to nie.

0

Esencją destruktora jest fakt, że jest wywoływany automatycznie kiedy obiekt jest niszczony.

0

To kiedy mój obiekt jest niszczony, skoro ja tego destruktora nie wywołuję?

0

‌ 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.

0

Gdy obiekt był na stosie, to jest niszczony jak wyjdzie z zakresu. W przypadku obiektów na stercie (alokowanych z użyciem new) destruktor wywoływany przy okazji delete.

0
Hipek napisał(a)

To kiedy mój obiekt jest niszczony, skoro ja tego destruktora nie wywołuję?

void funkcja()
{
    create c1 ;

    //coś tam coś tam

    {
        create c2 ;
        //coś tam coś tam kod
    } //   <---tu jest niszczony create c2

    //coś tam kod 3

} // <---tu jest niszczony c1

mam nadzieję, że zrozumiałeś.

0

Ale nawet jak nie mam zdefiniowanego destruktora to on się w jakiś magiczny sposób wywoła czy musi on być stworzony przeze mnie?

0

Rafał, tobie chodzi o zakres to rozumiem ;) Ale ja w swoim programie nie dawałem takich ficzerów

0

Jak nie zdefiniujesz to się nie wywoła, bo i po co? Destruktor służy do zwalniania zasobów, które nie zwolnią się automatycznie w momencie wywołania delete lub wyjścia z zasięgu obowiązywania zmiennej(jeżeli obiekt jest tworzony na stosie).

Jeżeli masz zdefiniowany destruktor to wywoła się sam automatycznie wtedy kiedy ma się wywołać - tak jak wyżej inni napisali.
Dodatkowo nigdy nie wolno wywoływać destruktora jawnie(poza jednym wyjątkiem).

0

No, a ja nie mam żadnego zasięgu więc? Wtedy mogę jawnie? Jak nie to kiedy?

0

Masz zasięg, który się kończy razem z wyjściem z funkcji main. Jawne wywołanie destruktora może być potrzebne przy dziedziczeniu wirtualnym.

0

Czyli w takim razie nie musze destruktora wywoływać tak?
Ale mogę to zostawić w celach...dydaktycznych?

0

Nie! Czego nie rozumiesz z: "nigdy nie wolno wywoływać destruktora jawnie(poza jednym wyjątkiem)"? Jak go wywołasz jawnie to wywoła się dwa razy i program może wylecieć w kosmos. De facto będziesz miał szczęście jak Ci się wywali, bo będziesz wiedział, że masz błąd, ale przez to może się wywalić w najmniej oczekiwanym momencie gdzieś tam kiedyś tam u kogoś tam.

0

w celach dydaktycznych to właśnie go nie wywołuj, a może dodaj komunikaty (jakieś cout<<"destruktor"<<endl) i będziesz wiedział kiedy co się wykonuje i w jakiej kolejności.

0

Żeby dodać ten komunikat to przecież i tak muszę napisać obj.~create
czy mam po prostu z def tego konstruktora wywalic te delete i dac cout<< ??

0

NIE.Pojmij wreszcie,że WYWOŁANIEM DESTRUKTORA W ODPOWIEDNIM MOMENCIE ZAJMUJE SIĘ SYSTEM a nie ty.

0
 #include <iostream>
#include <ctime>
using namespace std;
void zwloka(int ile_sekund)
{
    time_t czas_poczatkowy = time(NULL);
    while(time(NULL) - czas_poczatkowy < ile_sekund);
}
class create
{
    private:
    int *wsk;
    public:
create(int ile)
{
wsk = new int(ile);

}
~create()
{
    delete wsk; // zwalnianie pamieci
    cout << "Zwolniono..." << endl;
}
void view()
{
    cout << *wsk << endl;
}


};

int main()
{
    int wielk;

cout << "Podaj wielkosc obiektu, ktory chcesz stworzyc: ";
cin >> wielk;

create obj(wielk);
cout << "Gratulacje! Stworzyl Pan nowy obiekt, o wielkosci ";
obj.view();




}

tak okej?;>

1 użytkowników online, w tym zalogowanych: 0, gości: 1