[C++][STL] Pamięć się nie zwalnia, ale czemu?

Witam

Dostałem zadanie: zaimplementować algorytmy grafowe (na listach) i zmierzyć ich czas działania. Stworzyłem do tego własną klasę TLista (gdyż standardowe listy z STL’a nie spełniały pewnych, bardziej szczegółowych, warunków zadania) i dopisałem wszystkie potrzebne mi funkcję do obsługi tej klasy.

I teraz problem jest taki, że każdy z algorytmów jaki napisałem, po zakończeniu swojego działania, nie zwalnia pamięci, mimo że tam gdzie trzeba używam delete, erase(), czy clear(). Być może jeśli pokaże wam jedną przykładową funkcję algorytmu który stworzyłem, będzie potrafili mi wskazać linijkę która odpowiada za nie kasowanie pamięci, albo czego brakuje.

Oto przykładowy algorytm Kruskala wraz z kodem tych funkcji, które alokują dodatkową pamięć lub mogą być po części odpowiedzialne za problem (czyli bez sortowanieKruskal(), drukuj(), pobierzCzas()):

funkcja Kruskal

#include "Lista.h" //deklaracja klasy

#include "Kruskal.h"	//algorytmy pomocnicze, w tym algorytm sortowania

#include "Czas.h" //do funkcji pobierzCzas()

#include "Na_ekran.h"	//do funkcji drukuj() do formatowania tekstu i wypisywania na ekran

#include 

#include 

using namespace std;



double * TLista::Kruskal(int ileRazy)

{

	double * czas = new double[ileRazy];


	drukuj("Przygotowanie do algorytmu... ");

	double poczatekC, koniecC;

	TWezel * aktualny = NULL;

	vector * krawedzie = NULL;	//przechowuje zbior wszystkich krawedzi (w tym wagi oraz wierzcholki polaczone z krawedzia)

	int * numerDrzewa = NULL; //przechowuje informacje, do jakiego (numeru) drzewa nalezy i-ty wierzcholek

	int v; //licznik krawedzi

	int lewy, prawy;	//lewy, prawy wierzcholek danej krawedzi

	int i, j;


	while(ileRazy--) 

	{

		v = 0;


		krawedzie = new vector[E];

		numerDrzewa = new int[V];

		for(i = 0; i < V; i++)

			numerDrzewa[i] = i;	//zgodnie z algorytmem, na poczatku wszystkie drzewa sa jednowierzcholkowe i maja numer wierzholka


		//Poczatek algorytmu

		//--------------------------------------------------

		drukuj("Algorytm w toku... ", ileRazy);

		poczatekC = pobierzCzas();


		for(i = 0; i < V; i++)

		{

			aktualny = przeszukaj(i);

			for(j = 0; j < aktualny->Nastepny.size(); j++)

			{

				krawedzie[v].push_back(abs(aktualny->Waga[j]));

				krawedzie[v].push_back(aktualny->Index);

				krawedzie[v].push_back(aktualny->Nastepny[j]->Index);

				v++;

			}

		}


		sortowanieKruskal(krawedzie, v);


		for(i = 0; i < v; i++)

		{

			lewy = krawedzie[i][1];

			prawy = krawedzie[i][2];

			if(numerDrzewa[lewy] != numerDrzewa[prawy])

				this->dodajKrawedzDoDrzewa(numerDrzewa, lewy, prawy);

		}


		//Koniec algorytmu

		//---------------------------------------------

		koniecC = pobierzCzas();


		if(ileRazy > 0) drukuj("Przygotowanie do powtorzenia algorytmu... ");

		else drukuj("Konczenie algorytmu... ");


		for(i = 0; i < v; i++)

		{

			krawedzie[i].erase(krawedzie[i].begin(), krawedzie[i].end());

			krawedzie[i].clear();

		}


		delete [] krawedzie;

		krawedzie = NULL;

		delete [] numerDrzewa;

		numerDrzewa = NULL;


		czas[ileRazy] = koniecC - poczatekC;

	}


	drukuj("Algorytm Kruskala...Zakonczony ");


	return czas;

}

funkcja przeszukaj

#include "Lista.h"

#include 

using namespace std;


TLista::TWezel * TLista::przeszukaj(int index) //TWezel, to analogiczny wezel z list znanych z STL

{

	//zmienna 'znaleziono' jest statyczna, poniewaz pewne algorytmy wywoluja ta funkcje kilkukrotnie pod rzad dla tego samego 'index'

	//dlatego przetrzymywanie wartosci 'znaleziono' po wyjsciu z funkcji, moze przyspieszyc niektore przeszukiwania

	static TWezel * znaleziono = poczatek;	


	//celowe sprawdzenie przed tworzeniem innych obiektow funkcji i sprawdzenie czy 'znaleziono' nie wskazuje juz na 'index'

	if(znaleziono->Index == index)	

		return znaleziono;

	else znaleziono = NULL;

	//----------------------------------------------


	vector nastepniki;

	TWezel * aktualny = NULL;

	bool * odwiedzone = new bool[V];

	stack > stos;


	odwiedzone = new bool[V];

	for(int i = 0; i < V; i++)

		odwiedzone[i] = false;


	aktualny = poczatek;

	nastepniki.clear();

	nastepniki.push_back(aktualny);

	podajSasiadow(&nastepniki, aktualny);

	odwiedzone[aktualny->Index] = true;


	while(znaleziono == NULL)

	{


		while(nastepniki.size() > 1)	

		//'>1' z racji tego, ze nastepniki[0] == aktualny, a kazdy kolejny to szukani sasiedzi,

		//wiec jesli sa sasiedzi, to rozmiar 'nastepniki' musi byc >1

		{

			aktualny = nastepniki[nastepniki.size() - 1];


			if(aktualny->Index == index)

			{

				znaleziono = aktualny;

				break;

			}


			nastepniki.pop_back();


			if(odwiedzone[aktualny->Index] == false)

			{	

				stos.push(nastepniki);


				nastepniki.clear();

				nastepniki.push_back(aktualny);

				podajSasiadow(&nastepniki, aktualny);


				odwiedzone[aktualny->Index] = true;

			}

		}

		if(!stos.empty() && znaleziono == NULL)

		{

			nastepniki = stos.top();

			stos.pop();

		}

	}


	delete [] odwiedzone;

	odwiedzone = NULL;


	return znaleziono;

}

funkcja podajSasiadow

#include "Lista.h"


void TLista::podajSasiadow(vector * sasiedzi, TWezel * wezel)

{

	size_t rozmiar;

	int i;


	rozmiar = wezel->Nastepny.size();

	for(i = 0; i < rozmiar; i++)

		sasiedzi->push_back(wezel->Nastepny[i]);


	rozmiar = wezel->Poprzedni.size();

	for(i = 0; i < rozmiar; i++)

		sasiedzi->push_back(wezel->Poprzedni[i]);

}

funkcja dodajKrawedzDoDrzewa

#include "Lista.h"


void TLista::dodajKrawedzDoDrzewa(int * drzewo, int l, int p)

{

	int mniejszy, wiekszy;


	mniejszy = min(drzewo[l], drzewo[p]);

	wiekszy = max(drzewo[l], drzewo[p]);


	for(int i = 0; i < V; i++)

		if(drzewo[i] == wiekszy) drzewo[i] = mniejszy;

}

Proszę raczej nie analizować kodu pod kątem poprawności algorytmu, czy jego optymalizacji. Wolałbym o podpowiedzenie, gdzie może występować ów błąd, który sprawia, że po wyjściu z funkcji Kruskal() pamięć, którą alokuje i używa algorytm np. przy vector czy stack (przy przeszukiwaniu), nie jest zwalniana.


Zadanie wykonuje na Visual Studio 2008 Professional (ale po kompilacji, otwieram exe z dysku z folderu Release, nie przez VS, gdyż w ten sposób algorytmy działają szybciej), na systemie Windows 7 Professional 64-bit.

Czekam na jakieś podpowiedzi z waszej strony :lol:

Masz w kodzie:

   bool * odwiedzone = new bool[V]; // to się nie zwałniastackvectorTWezel*  stos;

Fakt tu nie zwalniałem pamięci. Głupie niedopatrzenie :stuck_out_tongue: I chociaż nie było to dużo, to jednak mniej więcej właśnie tyle zostawało, przy każdy wywołaniu tej funkcji przez algorytm. A jak wywoływałem ileś tam tysięczny raz z kolei bez wyłączenia programu, to w końcu się pamięć zapychała i to o to właśnie chodziło. Teraz działa dobrze.

Dzięki wielkie za pomoc :slight_smile: taka mała rzecz, a cieszy:P

Generalnie nie rozumiem czemu używasz dynamicznego przydzielenia pamięci na przemian z vector<>

Zamiast:

bool * odwiedzone = new bool[V];

można użyć:

vector odwiedzone(V);

Zamiast:

double * czas = new double[ileRazy];

można użyć:

vector czas(ileRazy); // funkcja zwraca vector przez wartość.

Zamiast:

vector * krawedzie;

krawedzie = new vector[E];

można użyć:

vector > krawedzie;

krawedzie.resize(E);

i tak dalej, nie będzie możliwości że gdzieś coś zapomnisz zwolnić.

a no bo, to moje pierwsze starcie z vectorami i ogólnie całym STL’em. I w między czasie się zorientowałem, że niektóre rzeczy lepiej będzie zrobić na wektorze. I tylko te niektóre rzeczy pozmieniałem.

no ale teraz po praktycznie skończeniu całego projektu, widzę że praca z STL’em ułatwia życie i nie trzeba aż tak bardzo martwić się o alokację dynamiczną.

Dodane 31.05.2010 (Pn) 12:59

Ciąg dalszy tematu. Ponieważ na wczoraj powinienem oddać to zadanie, a potrzebuje wygenerować jeszcze pewne wyniki, mam pytanie gdzie w tym algorytmie (ostatnim i jedynym który mi został), nie zwalniam pamięci. Jest to algorytm Prima, działający na listach (mojego pomysłu). Napiszę jak poprzednio serię funkcji, które wywołuje w tym algorytmie i proszę o pomoc, która linijka może być odpowiedzialna na niezwalnianie pamięci. Co dziwne…z praktycznie tej samej serii funkcji korzystają inne algorytmy w moim projekcie i tam pamięć jest zwalniania. Także nie bardzo rozumiem gdzie jest błąd. Prosiłbym o przejrzenie kodu i wychwycenie tej linijki. Będę bardzo wdzięczny :slight_smile:

Zastosowałem tam w między czasie bardzo złe posunięcie związane z alokacją pamięci (którą jednak zwalniam w funkcji kasuj() ), ale proszę tego nie komentować, bo nie mam już czasu na poprawki. Poza tym z tej samej funkcji (gdzie jest ta niefortunna alokacja) korzystają dwa inne algorytmy i one pamięć po sobie zwalniają, a Prim nie i nie mam pomysłu znowu skąd to się bierze, a przez to, nie mogę wygenerować zbyt dużych wyników, które odpowiadałby wymaganiom zadania.

Podobnie jak poprzednio funkcja drukuj() służy tylko do formatowania tekstu, więc nie może mieć wpływu na błąd (zważywszy na fakt, że jest wykorzystywana w wielu miejscach programu), sortowaniePrim(), to sortowanie przez kopcowanie, które też nie alokuje żadnej dodatkowej pamięci, operuje jedynie na tym co jest, pobierzCzas() tylko odczytuje czas.

Prim

#include "Lista.h"

#include "Czas.h"

#include "Prim.h"

#include "Na_ekran.h"

#include 

#include 

#include 

using namespace std;



double * TLista::Prim(int ileRazy)

{

	double * czas = new double[ileRazy]; //zwalniane przy koncu programu


	int s = 0;

	cout << "\nWybierz wierzcholek poczatkowy algorytmu [0; " << V - 1 << "]" << endl;

	do {

		cout << "-> ";

		cin >> s;

	}while(s < 0 || s > V - 1);

	//--------------------------------------------------------------------


	drukuj("Przygotowanie do algorytmu... ");

	double poczatekC, koniecC;

	TWezel * S = NULL; //wskaznik na poczatkowy wezel od ktorego zaczynamy algorytm

	vector Q; //wektor wierzcholkow nie bedacych w drzewie

	vector drzewo;	//zawiera adresy wezlow (wierzcholki) bedace w drzewie

	bool * wDrzewie = NULL;	//przechowuje informacje, czy i-ty wierzcholek nalezy do drzewa

	int i;


	S = przeszukaj(s);	//szuka wierzcholka poczatkowego


	while(ileRazy--)

	{

		Q.clear();

		drzewo.clear();


		wDrzewie = new bool[V];

		for(i = 0; i < V; i++)

			wDrzewie[i] = false;


		//Poczatek algorytmu

		//------------------------------------------

		drukuj("Algorytm w toku... ", ileRazy);

		poczatekC = pobierzCzas();


		drzewo.push_back(S);

		wDrzewie[S->Index] = true;


		while(drzewo.size() < V)

		{

			for(i = 0; i < drzewo.size(); i++)

				podajSasiadow(&Q, drzewo[i], wDrzewie);


			sortowaniePrim(&Q);


			i = Q[0][1];

			drzewo.push_back(przeszukaj(i));

			wDrzewie[i] = true;


			kasuj(&Q);	//w 'Q' jest zaalokowana dynamicznie pamiec i musi zostac zwolniona

		}


		//Koniec algorytmu

		//-------------------------------------------

		koniecC = pobierzCzas();


		if(ileRazy > 0) drukuj("Przygotowanie do powtorzenia algorytmu... ");

		else drukuj("Konczenie algorytmu... ");


		delete [] wDrzewie;

		wDrzewie = NULL;


		czas[ileRazy] = koniecC - poczatekC;

	}


	drukuj("Algorytm Prima...Zakonczony ");


	return czas;

}

podajSasiadow(vector, TWezel *, bool *)

void TLista::podajSasiadow(vector * sasiedzi, TWezel * wezel, bool * wDrzewie)

{

	vector pomoc;

	int i;

	size_t rozmiar;


	pomoc = podajSasiadow(wezel);

	rozmiar = pomoc.size();

	for(i = 0; i < rozmiar; i++)

	{

		if(!wDrzewie[pomoc[i][1]])

			sasiedzi->push_back(pomoc[i]);

	}

}

podajSasiadow(TWezel *)

vector TLista::podajSasiadow(TWezel * wezel)

{

	vector sasiedzi;


	int * nastepny = NULL;	//przechowuje w danym momencie wage krawedzi i numer wierzcholka incydentego

	size_t rozmiar;

	int i, j;

	TWezel * pomoc = NULL;


	if(skierowany)

	{

		rozmiar = wezel->Nastepny.size();

		for(i = 0; i < rozmiar; i++)

		{

			if(wezel->Waga[i] < 0) //czyli krawedz wychodzi z wezla (wierzcholka)

			{

				nastepny = new int[2]; //pamiec zwalniana w funkcji kasuj()

				nastepny[0] = abs(wezel->Waga[i]);	//potrzebna waga jest zawsze dodatnia ze wzgledu na sortowanie

				nastepny[1] = wezel->Nastepny[i]->Index;

				sasiedzi.push_back(nastepny);

				nastepny = NULL;

			}

		}


		rozmiar = wezel->Poprzedni.size();

		for(i = 0; i < rozmiar; i++)

		{

			pomoc = wezel->Poprzedni[i];

			for(j = 0; j < pomoc->Nastepny.size(); j++)

			{

				if(pomoc->Nastepny[j] == wezel && pomoc->Waga[j] < 0)

				{

					nastepny = new int[2];

					nastepny[0] = abs(pomoc->Waga[j]);

					nastepny[1] = pomoc->Index;

					sasiedzi.push_back(nastepny);

					nastepny = NULL;

					break;

				}

			}

		}

	}

	else

	{

		rozmiar = wezel->Nastepny.size();

		for(i = 0; i < rozmiar; i++)

		{

			nastepny = new int[2];

			nastepny[0] = wezel->Waga[i];

			nastepny[1] = wezel->Nastepny[i]->Index;

			sasiedzi.push_back(nastepny);	

			nastepny = NULL;

		}


		rozmiar = wezel->Poprzedni.size();

		for(i = 0; i < rozmiar; i++)

		{

			pomoc = wezel->Poprzedni[i];

			for(j = 0; j < pomoc->Nastepny.size(); j++)

			{

				if(pomoc->Nastepny[j] == wezel)

				{

					nastepny = new int[2];

					nastepny[0] = pomoc->Waga[j];

					nastepny[1] = pomoc->Index;

					sasiedzi.push_back(nastepny);

					nastepny = NULL;

					break;

				}

			}

		}

	}


	nastepny = NULL;


	return sasiedzi;

}

przeszukaj(int)

TLista::TWezel * TLista::przeszukaj(int index)

{

	//zmienna 'znaleziono' jest statyczna, poniewaz pewne algorytmy wywoluja ta funkcje kilkukrotnie pod rzad dla tego samego 'index'

	//dlatego przetrzymywanie wartosci 'znaleziono' po wyjsciu z funkcji, moze przyspieszyc niektore przeszukiwania

	static TWezel * znaleziono = poczatek;	


	//celowe sprawdzenie przed tworzeniem innych obiektow funkcji i sprawdzenie czy 'znaleziono' nie wskazuje juz na 'index'

	if(znaleziono->Index == index)	

		return znaleziono;

	//Warunek dodatkowy poniewaz dla argumentu 0 algorytm niepotrzebnie 

	//przeszukuje graf. Znamy przecierz bezposrednio wskazniki na ten element

	else if(index == 0)

		return poczatek;

	else znaleziono = NULL;

	//----------------------------------------------


	vector nastepniki;

	TWezel * aktualny = NULL;

	bool * odwiedzone = NULL;

	stack > stos;


	odwiedzone = new bool[V];

	for(int i = 0; i < V; i++)

		odwiedzone[i] = false;


	aktualny = poczatek;

	nastepniki.clear();

	nastepniki.push_back(aktualny);

	podajSasiadow(&nastepniki, aktualny);

	odwiedzone[aktualny->Index] = true;


	while(znaleziono == NULL)

	{


		while(nastepniki.size() > 1)	

		//'>1' z racji tego, ze nastepniki[0] == aktualny, a kazdy kolejny to szukani sasiedzi,

		//wiec jesli sa sasiedzi, to rozmiar 'nastepniki' musi byc >1

		{

			aktualny = nastepniki[nastepniki.size() - 1];


			if(aktualny->Index == index)

			{

				znaleziono = aktualny;

				break;

			}


			nastepniki.pop_back();


			if(odwiedzone[aktualny->Index] == false)

			{	

				stos.push(nastepniki);


				nastepniki.clear();

				nastepniki.push_back(aktualny);

				podajSasiadow(&nastepniki, aktualny);


				odwiedzone[aktualny->Index] = true;

			}

		}

		if(!stos.empty() && znaleziono == NULL)

		{

			nastepniki = stos.top();

			stos.pop();

		}

	}


	delete [] odwiedzone;

	odwiedzone = NULL;


	return znaleziono;

}

kasuj(vector *)

void TLista::kasuj(vector * wektor)

{

	if(wektor->empty()) return;


	size_t rozmiar = wektor->size();

	for(int i = 0; i < rozmiar; i++)

	{

		delete [] wektor[0][i];

		wektor[0][i] = NULL;

	}

	wektor->clear();

}

Myślałem na początku, że ta funkcja kasuj() coś źle zwalnia, ale jak prześledziłem kroki debugerem, to niby jest wszystko kasowane. Poza tym, jak mówiłem wcześniej, to inne funkcję też korzystają praktycznie w 100% z tego samego zestawu funkcji (łącznie z kasuj() ) i wszystko gra.

Help me :expressionless: