Destruktor i pamięć dynamiczna

0
 
#include <cstdlib>
#include <iostream>

using namespace std;

class Q
{public:
	int x;
	char y;
	int z;
	~Q()
	{
	
	}
};
int main(int argc, char *argv[])
{
Q *a=new Q;
a->x=9;
a->y='T';
a->z=4;
cout<<a->x<<"   "<<a->y<<"   "<<a->z<<endl;
delete a;


cout<<a->x<<"   "<<a->y<<"   "<<a->z<<endl;
	system("pause");
	return 0;
}

Witam, mam pytanko.
Nie rozumiem do końca jak operować pamięcią dynamiczną i jak ją potem usuwać.
W kodzie mamy przykładową klasę Q z trzema polami.
Czy tworząc obiekt klasy przez wskaźnik tak jak w kodzie jest to przydział pamięci dynamicznej ?
Jeśli nie to jak powinienem to zrobić?
I jak skutecznie usunąć potem cały obiekt bo w ten sposób usuwa mi tylko pierwsze z jego pól i nie wiem jak dostać się do pozostałych. Pewnie za pomocą destruktora ale nie chce mi to działać i nie wiem jak się do tego zabrać dlatego jest on pusty.
Prosił bym o pomoc, wytłumaczenie mi tego i uzupełnienie kodu.

0
TalibX napisał(a):

Czy tworząc obiekt klasy przez wskaźnik tak jak w kodzie jest to przydział pamięci dynamicznej ?

Tak.

TalibX napisał(a):

I jak skutecznie usunąć potem cały obiekt bo w ten sposób usuwa mi tylko pierwsze z jego pól i nie wiem jak dostać się do pozostałych.

Pamięć dla pól została przydzielona domyślnie przez konstruktor, zostanie zwolniona również domyślnie przez destruktor.</quote>

TalibX napisał(a):
delete a; 
cout<<a->x<<"   "<<a->y<<"   "<<a->z<<endl;

Nie możesz odwoływać się do pól obiektu przez wskaźnik, którego pamięć już zwolniłeś!

0
  1. Tragiczne formatowanie kodu
  2. Pola w klasie raczej nie powinny być publiczne - na przyszłość
  3. Odpowiadając na pytanie - tak jest dynamiczny przydział pamięci, nie przydział pamięci dynamicznej - nie ma czegoś takiego.
  4. Dupa... Usuwasz wszystkie jego pola. Po prostu to co robisz w linijce z drugim cout to czytanie śmieci. I to co tam będzie to czysty przypadek. U mnie np jest taki wynik:

9 T 4
4074936 ě 4
Czwórka na końcu to czysty przypadek. Na czytanie śmieci system Ci pozwoli. Spróbuj natomiast coś tam zapisać po wywołaniu delete np
a->x = 3 to dostaniesz po łapach od systemu.

  1. W destruktorze nie robisz nic bo zasadniczo nic on tu robić nie powinien. Obiekt tworzony w mainie usuwasz operatorem delete i to jest ok. Destruktor natomiast jest potrzebny gdy w samym ciele klasy przydzielasz gdzieś pamięć dynamicznie np:
#include <cstdlib>
#include <iostream>

using namespace std;

class Q
{

    public:
    Q(int a, char b, int c)
    {
        x =a;
        y = b;
        z = c;
    }
        int x;
        char y;
        int z;
        ~Q()
        {

        }
};
int main(int argc, char *argv[])
{
Q a(1, 'V' ,3);




cout<<a.x<<"   "<<a.y<<"   "<<a.z<<endl;
        system("pause");
        return 0;
}
 

To nie potrzebuje destruktora bo nie ma dynamicznego przydzielania pamięci WEWNĄTRZ klasy.
To już tak:

#include <cstdlib>
#include <iostream>

using namespace std;

class Q
{

    public:
    Q(int a, char b, int c)
    {

        x = new int;
        y = new char;
        z = new int;
        *x =a;
        *y = b;
        *z = c;
    }
        int* x;
        char* y;
        int* z;
        ~Q()
        {
            cout<<"Destruktor";
            delete x;
            delete y;
            delete z;
        }
};
int main(int argc, char *argv[])
{
Q a(1, 'V' ,3);




cout<<(*a.x)<<"   "<<(*a.y)<<"   "<<(*a.z)<<endl;
        system("pause");
        return 0;
}
 
0

delete po prostu zwalnia pamięć, nie czyści jej, bo i po co - skoro jest zwolniona to może za chwilę być przypisana komuś innemu. Nie wolno się odwoływać do niezaalokowanej/zwolnionej pamięci, w najlepszym wypadku wywali Ci aplikację, w najgorszym aplikacja zacznie się dziwnie zachowywać.

0

Ok dzięki wszystkim za pomoc, przydała się.

0

Przepraszam za podwójny post ale nie mogę edytować.

Malootki napisał(a):
  1. Dupa... Usuwasz wszystkie jego pola. Po prostu to co robisz w linijce z drugim cout to czytanie śmieci. I to co tam będzie to czysty przypadek. U mnie np jest taki wynik:

9 T 4
4074936 ě 4
Czwórka na końcu to czysty przypadek. Na czytanie śmieci system Ci pozwoli. Spróbuj natomiast coś tam zapisać po wywołaniu delete np
a->x = 3 to dostaniesz po łapach od systemu.

 
#include <cstdlib>
#include <iostream>

using namespace std;

class Q
{
public:
	int x;
	string y;
	int z;
};
int main(int argc, char *argv[])
{
	Q *a=new Q;
	a->x=9;
	a->y='T';
	a->z=4;
	cout<<a->x<<"   "<<a->y<<"   "<<a->z<<endl;
	delete a;
	a->x=1;
	a->y="NIE DOSTALEM PO LAPACH!!!";
	a->z=2;
	cout<<a->x<<"   "<<a->y<<"   "<<a->z<<endl;
	system("pause");
	return 0;
}

Jak widać nie dostałem po łapach.
Więc jak to jest z tym dynamicznym przydziałem i zwalnianiem skoro nadal mogę używać obiektu tak sprawnie jak wcześniej

0

Mam jeszcze jedno pytanie.
Mam do wykonania zadanie w którym mam 2 konstruktory(koniecznie muszą być prywatne), oraz kilka metod publicznych.
Jedna z nich porównuje do siebie 2 koła pod względem pola, promienia i koloru.
Z tego co wiem jeśli konstruktory są prywatne to nie można za ich pomocą stworzyć obiektu w main.
Więc skąd mam wziąć takie obiekty żeby móc je porównać za pomocą metody klasy?

0

http://i50.tinypic.com/34ecizb.jpg

No to pogratulować kompilatora który Ci na to pozwolił. TO jest błędne działanie i nie udowadniaj, że jest inaczej.

@up

Do metod prywatnych dostęp ma tylko obiekt tej klasy - fakt. Rozwiązaniem jest wywołanie na klasie statycznej metody która zwróci Ci ten obiekt. Rozwiązanie stosowane np w singletonie. http://pl.wikisource.org/wiki/Singleton_(wzorzec_projektowy)/kod

0

@Malootki: Po pierwsze to nie kwestia kompilatora, co najwyżej build debug może mieć zaszyte w sobie dodatkowe assercje sprawdzające czy nie mieszasz po niezwolnionej pamięci. Release już takich rzeczy nie robi, bo to mocno wpływa na prędkość działania kodu.

Po drugie: użycie operatora delete nie gwarantuje, że system od razu odbierze te zasoby aplikacji(to zależy od systemu). Więc nie można powiedzieć, że po zwolnieniu pamięci i od razu pisaniu po niej dostanie się crash'a(niestety).

Wywalenie aplikacji ze względu na pogwałcenie uprawnień czy to czytania czy pisania po pamięci to jest zadanie systemu, kompilator nie ma tu nic do gadania.

0

@Malootki
Singleton raczej się tutaj nie przyda, gdyż projekt zakłada, iż będziemy porównywać różne obiekty klasy Koło.

Należy skorzystać z wzorca projektowego Factory. Coś w tym guście ( na brudno ):

class KoloFactory; 
 
class Kolo() { 
private: 
   Kolo(int r = 1) : m_radius(r) {}; 
   int m_radius; 
public: 
   bool operator==(Kolo const& _k) { return _k.m_radius == this->m_radius; } 
friend class KoloFactory; 
};

class KoloFactory { 
   public:
      static Kolo* getNewKolo(int r = 1) { return new Kolo(r);}
};

int main() { 
   Kolo* a = KoloFactory::getNewKolo(1);
   Kolo* b = KoloFactory::getNewKolo(2);
   assert( a == b ); // crash! 
}

@TalibX:
" Jak widać nie dostałem po łapach. "
Good for you!
I pamiętaj: Im więcej razy udowodnisz kompilatorowi, że się myli, tym więcej wartości mają twoje aplikacje.
Nie, poważnie, nie wolno Ci tego robić. Gdzieś tutaj ( http://www.parashift.com/c++-faq-lite/ ) jest to dobrze wytłumaczone.

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