W którym miejscu i jak używać destruktora?

0

Witam.
Siedzę przy listach jednokierunkowych.
I tu mam pytania.
Czy przykładowo można w metodzie void zwolnij_pamięć(), klasy np. class samochod, wywołać destruktor ~samochod() zwalniający pamięć zajmowaną przez wskaźniki dynamiczne?
Bo nie wiem czy można tak, czy lepiej zadeklarować destruktor w ciele klasy, a potem z zewnątrz odwołać się do destruktora samochod::~samochod(){}.
W moim programie, chce żeby destruktor został wywołany dopiero, gdy użytkownik wybierze opcję zakończenia pracy programu.
P.S. Co do metod, to zawsze do funkcji składowych klasy odwołuje się po za ciałem klasy.

`dodanie znaczników ``` - @furious programming

5

Po to istnieje destruktor abyś mógł zapomnieć o zwolnieniach.
Wywoła się automagicznie jak tylko zmienna przestanie być potrzebna.

Przeanalizuj to:

#include <iostream>
#include <string>
using namespace std;

struct Foo
  {
   string name;
   Foo(const string &name):name(name) { cout<<"create "<<name<<endl; }
   ~Foo() { cout<<"destroy "<<name<<endl; }
  };

int main()
  {
   cout<<"1:"<<endl;
   Foo a("a");
   cout<<"2:"<<endl;
   for(int i=0;i<2;++i)
     {
      cout<<"3:"<<endl;
      Foo b(string("b")+(char)('0'+i));
      cout<<"4:"<<endl;
     }
   cout<<"5:"<<endl;
   return 0;
  }

http://ideone.com/F7Z5Oz

0

Jeżeli pracujesz dla siebie to korzystaj lepiej z smart pointerów ( tj. unique_ptr i shared_ptr), które same zwolnią pamięć na wskazywany obiekt, gdy skończy się ich scope.
Jeżeli robisz to na uczelnię i koniecznie MUSISZ korzystać z raw pointerów, wtedy czyść całą pamięć w destruktorze. Tak samo jak wszystkie alokacje pamięci powinny znajdować się w konstruktorze. Dzięki temu, kolejna osoba która przejmie po tobie projekt, będzie w ten sposób wiedziała czego spodziewać się po danej klasie, a sama jej obsługa będzie zdecydowanie prostsza i bardziej intuicyjna.

0

Alokacja pamięci jest w konstruktorze.
I chodzi o sam destruktor.
Czyli jeśli mam tylko taki kod jak poniżej to destruktor zwolni pamięć automatycznie przy zakończeniu pracy programu?

samochod::~samochod(){
    delete []pierwszy;
    delete []nastepny;
}
1

Tak zwolni, ale lepiej korzystać z klas takich jak std::vector lub std::array do przechowywania tablic w klasie.

0

Nie wiem co ten destruktor zwolni bo nie podałeś co się przydziela (wygląda na totalny bezsens), zaś z całą pewnością ten destruktor wywoła się automatycznie tuż po zakończeniu okresu życia każdego obiektu typu samochod.

5
adrian.widzew napisał(a):

Alokacja pamięci jest w konstruktorze.
I chodzi o sam destruktor.
Czyli jeśli mam tylko taki kod jak poniżej to destruktor zwolni pamięć automatycznie przy zakończeniu pracy programu?

samochod::~samochod(){
    delete []pierwszy;
    delete []nastepny;
}

Powiedz mi proszę jak bardzo TWÓJ SAMOCHÓD jest zainteresowany SAMOCHODEM SĄSIADA oraz dlaczego przy niszczeniu TWOJEGO SAMOCHODU niszczone są samochody TWOICH SĄSIADÓW.

To jest jakiś bubel projektowy.

0

Chodziło mi o to, żeby zrobić coś jakby notatnik w telefonach. Użytkownik jak chce to dodaje następne obiekty tu akurat obiekty typu samochod, a jak chce wykasować wszystko to kasuje wszystko. Jeżeli w liście jednokierunkowej są deklarowane i inicjowane następne obiekty dynamicznie, zabierające dodatkową pamięć, to żeby na koniec program zwrócił pamięć zajmowaną przez te obiekty dynamiczne tworzone przez wskaźniki, a deklaracje wskaźników mam akurat w klasie. To jeżeli zniszczę tylko obiekt samochod, to chyba przez pozostałe dane tworzone dynamicznie, może nastąpić tzw. wyciek pamięci. Nie wiem czy dobrze to rozumiem. Ale jak mnie karcicie to chyba źle.

0

Destruktor obiektu musi zwolnić tą i tylko tą pamięć którą sam dla siebie przydzielił, (czasami tą za którą przekazano mu odpowiedzialność - ale to jest temat górnolotny więc na razie o tym zapomnij).
Żadnych sąsiadów nie zwalniamy.
Pokaż jak wygląda twoja klasa, z tego:

samochod::~samochod(){
    delete []pierwszy;
    delete []nastepny;
}

wynika dwie rzeczy:

  1. stworzyłeś jakiś straszny bubel projektowy
  2. nie zrozumiałeś składni delete
0

Oki. Sam się z tego nie wyplączę chyba.

#include<iostream>
#include<conio.h>
using namespace std;
class samochod{
	private:
		string marka,model;
		int rocznik,pojemnosc,;
		samochod *pierwszy;
		samochod *nastepny;
	public:
		void dodaj_samochod(string marka, string model, int rocznik, int pojemnosc);
		void wyswietl_calosc();
		void zwolnij_pamiec();
		samochod();
		~samochod();
};
samochod::samochod(){
	pierwszy=0;
	nastepny=0;
}
samochod::~samochod(){
	cout<<"\nzwolniono pamiec\n";
	delete [] pierwszy;
	delete [] nastepny;
}
void samochod::dodaj_samochod(string marka, string model, int rocznik, int pojemnosc){
	samochod *nowy=new samochod;
	nowy->marka=marka;
	nowy->model=model;
	nowy->pojemnosc=pojemnosc;
	nowy->rocznik=rocznik;
	if(pierwszy==0){
		pierwszy=nowy;
	}
	else{
		samochod *tmp=pierwszy;
		while(tmp->nastepny!=0){
			tmp=tmp->nastepny;
		}
		tmp->nastepny=nowy;
		nowy->nastepny=0;
	}
}
void samochod::wyswietl_calosc(){
	if(pierwszy==0){
		cout<<pierwszy->marka<<" "<<pierwszy->model<<" rocznik:"<<pierwszy->rocznik<<" pojemnosc:"<<pierwszy->pojemnosc<<endl;
	}
	else{
		samochod *tmp=pierwszy;
		while(tmp->nastepny!=0){
		cout<<nastepny->marka<<" "<<nastepny->model<<" rocznik:"<<nastepny->rocznik<<" pojemnosc:"<<nastepny->pojemnosc<<endl;
		tmp=tmp->nastepny;
		}
	}
}
int co_wykonac(){
	int i;
	cout<<"1 - dodaj samochod, 2 - wyswietl wszystko, 3 - zakoncz\n";
	cin>>i;
	return i;
}
void podaj_dane(string &marka, string &model, int &rocznik, int &pojemnosc){
	cout<<"Podaj marke samochodu: ";
	cin>>marka;
	cout<<"Podaj model samochodu: ";
	cin>>model;
	cout<<"Podaj rocznik: ";
	cin>>rocznik;
	cout<<"Podaj pojemnosc: ";
	cin>>pojemnosc;
}
int main(){
	samochod car;
	string marka, model;
	int rocznik, pojemnosc;
enum dzialanie{dodaj=1, wyswietl, zakoncz};
dzialanie Czynnosc;
do{
	Czynnosc=static_cast<dzialanie>(co_wykonac());
switch(Czynnosc){
	case dodaj:
		podaj_dane(marka,model,rocznik, pojemnosc);
		car.dodaj_samochod(marka,model,rocznik, pojemnosc);
	case wyswietl:
		car.wyswietl_calosc();
		break;
	case zakoncz:
		
		cout<<"\nKoniec programu\n";
		break;
		
}
}
while(Czynnosc!=zakoncz);
getch();
return 0;
}

Co do listy starałem się brać przykład ze strony http://www.p-programowanie.pl/cpp/lista-jednokierunkowa-c/, aczkolwiek to co tam jest w strukturach, to ja przeniosłem do klasy, i stworzyłem metodę do wyświetlania zawartości obiektów, chociaż nie którzy mówią, że klasy nie służą do interakcji z użytkownikiem, ale nie wiem czy to jakiś błąd.

1
  1. delete []pointer; - tak się zwalnia tablice które przydzielono za pomocą: typ *pointer=new typ[count];
  2. Jeżeli przydzielasz typ *pointer=new typ; to zwalniasz delete pointer;
  3. Skoro klasa samochod ma wskaźnik samochod *pierwszy; to nadawałoby się to na trzymanie drzewa hierarchicznego (wiele potomków).
  4. samochod *pierwszy; musi być przynajmniej składową statyczną, owszem nadal będzie to bublowate ale przynajmniej będzie reprezentować listę a nie drzewo.
1

Pojęcie jakieś masz, ale zrobiłeś to źle. Jak widzę masz implementację kuligu z samochodów. Jeden samochód ciągnie wszystkie, tak daleko nie zajedziesz. Lepiej żebyś zaimplementował pociąg do którego doczepiasz wagony i na nich stawiasz samochody. Czyli wracając na ziemię, obiekt samochodu powinien reprezentować JEDEN egzemplarz. Konstruktor powinien przyjmować dane i wpisywać do pól. Lista to powinna być osobna klasa, która przechowuje po jednym samochodzie w każdym elemencie. Ogólnie coś takiego:

class CarsListItem
{
    Car * item;
    CarListItem * next;
}

class CarsList
{
   CarListItem * first;
}
0

Jak wyżej lub:

class CarsList
{
  class CarsListItem
  {
    Car item; // bezpośrednio
    CarListItem *next;
  };
  CarsListItem *first;
};

W każdym razie to destruktor CarsList musi zwolnić wszystkie obiekty typu CarsListItem.
W przypadku wersji od @Sarrus dodatkowo destruktor CarsListItem musi zwolnić jeden (swój) obiekt Car;

0

Czyli tak czy siak, muszę to rozbić jeszcze na pół, że tak powiem. Wygodniej było trzymać wszystko w jednym.

2

pisałam kiedyś taką listę z kotami, masz przykład z samochodami:

#include <iostream>
#include <stdexcept>
#include <string>

using namespace std;

class Car {
private:
	string model;
public:
	Car() {}
	Car(string model) : model(model) {}
	string toString() {
		return "Samochod marki " + model;
	}
};

class CarLinkedList {
private:
	typedef struct el {
		struct el *next;
		struct el *prev;
		Car data;
	} el;
	el *first;
	el *last;
	size_t nof_elems;
public:
	CarLinkedList() :
		first(NULL), last(NULL), nof_elems(0) {}

	void push_back(Car car) {
		el* n = new el;
		n->next = NULL;
		n->prev = last;
		n->data = car;
		if (!first && !last)
			first = n;
		else
			last->next = n;
		last = n;
		++nof_elems;
	}

	Car& get(size_t index) {
		el *e = first;
		while (index && e) {
			--index;
			e = e->next;
		}
		if (index)
			throw std::out_of_range("index >= list.size() :(");
		return e->data;
	}

	size_t size() {
		return nof_elems;
	}

	~CarLinkedList() {
		el *ne, *e = first;
		while (e) {
			ne = e->next;
			delete e;
			e = ne;
		}
		nof_elems = 0;
	}

};

int main() {

	CarLinkedList listaSamochodow;
	listaSamochodow.push_back(Car("Mercedes"));
	listaSamochodow.push_back(Car("BMW"));
	listaSamochodow.push_back(Car("Audi"));

	cout << listaSamochodow.get(1).toString() << endl;
	cout << "elementow w liscie: " << listaSamochodow.size() << endl;

	return 0;
}

musisz to robić tak, że klasa samochód ma gdzieś to że będzie znajdować się w liście, drzewie czy nie wiem czym. Nie zawiera żadnych wskaźników do następnego/porzedniego samochdou, żadnych metod typu delete itd... Cały mechanizm listy robisz sobie w klasie Lista. kiedy już opanujesz takie lsity zrób sobie to na generykach.

0
adrian.widzew napisał(a):

Czyli tak czy siak, muszę to rozbić jeszcze na pół, że tak powiem. Wygodniej było trzymać wszystko w jednym.

Tak ci się tylko wydaje. Gdybyś miał rozbudowywać takie cudo (np. chciałbyś mieć klasę dziedziczącą po klasie samochód) to by się dopiero zaczęła jazda. Przy liście zaimplementowanej oddzielnie - nie ma problemu. Pomijam zasady OOP bo na to przyjdzie jeszcze czas.

0

Dobra. Spróbuje rozbić tą jedną klasę, na klasę i listę.

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