Jak zwolnić pamięć?

0

Witam. Jak zwolnić pamięć w przypadku takiego kodu?

#include <iostream>
using namespace std;

class Napoj {
	public:
		virtual double koszt() = 0;
		virtual ~Napoj();
};

class Kawa : public Napoj {
	public:
		double koszt() {
			return 1.20;
		}

		~Napoj()
		{
			cout << "Usuwam KAWE" << endl;
		}
};

class Mleko : public Napoj {
	public:
		Napoj *napoj;

		Mleko(Napoj *n) { napoj = n; }

		double koszt() {
			return napoj->koszt() + 0.20;
		}

		~Napoj()
	 	{
				cout << "Usuwam MLEKO" << endl;			
				delete this->napoj;
		}
};

int main() {
	Napoj *napoj = new Kawa;
	napoj = new Mleko(napoj);
	napoj = new Mleko(napoj);

	cout << napoj->koszt() << endl;

	delete napoj;
	
	return 0;
}

Z góry dziękuje za pomoc.

0

Tsk tsk...lista twych przewinień jest długa...no ale od początku.

-pamięć zaalokowana przez program przeważnie jest zwalniana z automatu przez system po zakończeniu się programu.Niemniej,taki sposób pisania programów to najlepsza droga do wyrobienia sobie nawyków pisania przepięknych memory leaków :] Dlatego patrz niżej:
-wskaźnikowi *napoj alokujesz co rusz to nowe obszary pamięci,tracąc zarazem dostęp do poprzednich-a zatem i możliwość ich dealokacji deletem.To jest tak baardzo zue,że aż ciary przechodzą :p
Poprawnie powinieneś stworzyć 2 nowe wskaźniki na te 2 kawy z mlekiem.
-to delete napoj tak jak piszę wyżej załatwi ci dealokację wyłącznie aktualnie wskazywanego prze *napoj obszaru pamięci,tj 2 kawy z mlekiem.Kawa z mlekiem nr 1 i sama Kawa nie zostaną ruszone i dopiero system podczas zakończenia programu powinien zwolnić pamięć przez nie zajmowaną.
-Generalnie twój projekt tych klas taki nie najszczęśliwszy trochę,ot co na przykład w sytuacji kiedy ktoś zażyczy sobie kawy z mlekiem i ajerkoniakiem?
-Widzę wywołanie delete napoj w destruktorze klasy Mleko.Tsk tsk...to jest samoistne proszenie się o klopoty,takie kasowanie wskaźnikow stworzonych na zewnątrz i przekazanych do klasy.

1
MasterBLB napisał(a)

-pamięć zaalokowana przez program przeważnie jest zwalniana z automatu przez system po zakończeniu się programu.

bullshit. takie cos mogl napisac tylko student informatyki, i to mierny student.

MasterBLB napisał(a)

Niemniej,taki sposób pisania programów to najlepsza droga do wyrobienia sobie nawyków pisania przepięknych memory leaków .

proba rehalibitacji ale smrod i tak zostal...

0

Doprawdy?Bo ja widzę potwierdzenie moich wiadomości:
http://forum.warsztat.gd/index.php?topic=9981.0
http://forum.ks-ekspert.pl/topic/84953-cinne-zwalnianie-pamieci/
czyli że (mowa tu o Windows) zaalokowana przez program pamięć jest zwalniana przez system kiedy program się skończy.Przeważnie

No ale,pogrzebię na MSDNie,to pewniejsze źródło.

0

Poprawnie powinieneś stworzyć 2 nowe wskaźniki na te 2 kawy z mlekiem.

To jest ciągle ta sama kawa tylko udekorowana dodatkami. Próbuje uporać z dekoratorem w C++. W C#/Javie nie ma problemów, bo pamięć zwolni się sama a tutaj potrzebuje w sposób rekurencyjny zwolnić pamięć którą wcześniej zaalokowałem. Ale destruktory się nie wykonują w ten sposób w jaki próbowałem.

0

W tym rzecz,że to NIE JEST ta sama kawa-powstają 2 NOWE obiekty klasy Mleko,zaś nie przechowujesz wskaźników do oryginalnej kawy i mleka,zatem nie masz możliwości ich zwolnienia.Ja bym te klasy zrobił innaczej:

Class Napoj
{
   List<Napoj*> dodatki;

public:
   void dodatek(Napoj *n)
   {
      dodatki.append(n);
   }
   virtual double koszt()=0;
   virtual ~Napoj()
   {
       for(int cnt=0;cnt<dodatki.size();cnt++) delete dodatki[cnt];
   }
};

Class Kawa
{
public:
  double koszt(void)
  {
     double cena=ileś_tam;
     for(int cnt=0;cnt<dodatki.size();cnt++)
     {
        cena+=dodatki[cnt]->koszt();
     }
     return cena;
  }
};

a potem w kodzie używasz w łatwy sposób tworząc przenajróżniejsze kombinacje napojów:

Kawa *kawa_z_mlekiem=new Kawa;
kawa_z_mlekiem->dodatek(new Mleko);

itd;

UWAGA od dłuższego czasu piszę przy użyciu kontenerów Qt,zatem jak wygląda użycie standardowej listy to tak dokładnie nie pamiętam.Jakby co,to na stronie C++ reference jest dokładny opis

0

Przy dodawaniu dodatku do napoju przekazuje w konstruktorze wskaźnik do poprzedniego obiektu:

class Mleko : public Napoj {
        public:
                Napoj *napoj; // tu mam wskaźnik do poprzedniego 'napoju'
 
                Mleko(Napoj *n) { napoj = n; }
 
                double koszt() {
                        return napoj->koszt() + 0.20;
                }
 
                ~Napoj()
                 {
                                cout << "Usuwam MLEKO" << endl;                        
                                delete this->napoj;
                }
};

Przy wywołaniu metody koszt() w rekurencyjny sposób obliczam cene za napój i w ten sam sposób chciałbym zwalniać pamięc, ale niestety destruktory się nie wywołują.

0

@MasterBLB: przekombinowałeś ;)

@michal1879: kod jest prawie dobry, poza tym, że nie powinien się skompilować a potem nawet zlinkować. W klasach Mleko i Kawa zmień nazwy destruktorów na poprawne(takie same jak nazwa klasy). W klasie napój musisz napisać definicję destruktora(musi oczywiście zostać jako wirtualny), może być pusty. Kontrolnie wrzuć sobie tam np. cout << "usuwam NAPÓJ" << endl;, zobaczysz wtedy jak te destruktory są wywoływane.

0

Hmm jeśli się uczyć wzorców,to imo należy porządnie.W tym modelu autora pomijając już błędy implementacyje są poważne błędy koncepcyjne:
1)Sprzeczne z intuicją zapytanie o cenę.Żeby otrzymać prawidłowy koszt napoju trzeba się zapytać o cenę mleka.No powiedz byku_guzio czy jak zamawiasz sobie kawę z dodatkiem mleka to jak się pytasz o cenę,"ile za kawę" czy "ile za mleko"?
2)Błąd nieco bardziej technicznej natury-z kodu autora wnioskuję,że chciał on utworzyć kawę z podwójnym mlekiem.Niestety,z uwagi na taką a nie inną implementację nie może się spytać po prostu kawa->koszt() tylko musi poprzez owo z życi wzięte mleko->koszt().I tu jest haczyk,bo nie dość że musi zrobić to 2 razy,to w przypadku sumowania otrzymanych wartości mleko->koszt() (a musi to zrobić) będzie jeszcze musiał uwzględniać iż cena kawy została już wliczona podczas pierwszego wywołania.

Co zaś do nauki wzorca-w nim nie ma niczego takiego co by trzeba intensywnie ćwiczyć,ot dostaje się obiekt do dekoracji i wywołuje jego metody.Generalnie jego istotą jest to,że nie ma powiązania na zasadzie dziedziczenia między dekoratorem a dekorowanym;jeśli zaś są,to jak autor widzi dochodzi to podobnych kwiatków jakie stworzył :P

0

Dziękuje Wam za pomoc:) Teraz już kod działa. Co do przykładu to pochodzi on z książki Head First Design Patterns i jest akurat dostepny w darmowym rozdziale: http://pdf.helion.pl/hfdepa/hfdepa-3.pdf . Nie wiem jak dla innych ale dla mnie, jako początkującego świetnie ilustruje ten wzorzec.

Co zaś do nauki wzorca-w nim nie ma niczego takiego co by trzeba intensywnie ćwiczyć

Hmm, wzorców może nie trzeba ćwiczyć, książke czytałem za pierwszym razem, implementowałem je w Javie i problemów nie było, natomiast próbując je teraz przenieść do C++ mam problemy jak widać z samym językiem :)

Pozdrawiam i dziękuje za odpowiedzi :-)

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