Eine Frage betreffend Speicherallozierung

UncleBob

Mitglied
Ich habe in meinem Programm gerade ein erhebliches Problem gelöst, aber ich weiss nicht genau wie bzw. was für Nebeneffekte dass das haben kann. Möchte daher gerne schnell eine Theoretische Frage stellen.

Nehmen wir an ich habe einen Pointer auf eine Classe mit einem dynamischen Array drinn:

class MyClass
{
public:
int *MyArray;
}

MyClass *Ptr;

nehmen wir auch an das Array wurde bereits korrekt initialisiert.

Nun mache ich eine Funktion an die ein Pointer auf MyArray übergeben wird und das Array darin neu angeordnet wird:

void MyFunction( MyClass *mPtr)
{
mPtr->MyArray = NULL;
delete [] mPtr->MyArray;
//hinterher wird das Array neu initialisiert und gefüllt
}

Nun weiss ich nicht ob, wenn ich MyArray erst auf NULL setze, der Ursprüngliche Speicherbereich überhaupt freigegeben wird. Wenn er geschlossen bleibt habe ich gerade ein ziemlich grosses Speicherloch produziert, wenn er Freigegeben wird ist alles in Ordnung.
Was ich weiss ist das der Versuch MyArray zu löschen ohne es zu nullen zu einem Absturz führt, und das der Versuch mPtr zu löschen eine Heap-corruption zur Folge hat, die zwar eine Fortführung des Programms erlaubt, jedoch zu allen möglichen unvorhersehbarkeiten führt.

Kann mir jemand sagen ob das richtig ist so wie oben beschrieben, Es scheint sehr gut zu Funktionieren, aber wenn ich ein Speicherleck habe ohne es zu merken wird das nach einer halben Stunde betrieb oder so ebenfalls zum Absturz führen.
 
Normal darfst du es erst nach delete nullen, wenn das Fehler bringt hast du es nicht ordentlich allokiert
 
Hmmm... ich verstehe jetzt warum der Absturz passiert. Die ganze Sache sieht so aus:

MyClass PtrSave = *mPtr;

//die Daten die vorhanden sind werden erst in ein anderes Array kopiert um anschliessend mit den ergänzten zurückkopiert zu werden

Wenn ich jetzt mPtr->MyArray NULLe und dann lösche, bleibt der Speicherbereich bestehen, d.h. PtrSave ist immer noch voll. Wenn ich mPtr->MyArray lösche ohne zu nullen, greift hinterher auch PtrSave ins lehre.

Nun schiene es mir am besten wenn die Daten die noch gebraucht werden gar nie im Speicher herumkopiert werden müssten. Das scheint im Moment der Fall zu sein, aber ich bin mir nicht sicher.

Denn nach dem löschen definiere ich

mPtr->MyArray = new int[xy]

Damit wird wohl, wenn ich das richtig verstehe, ein neuer Speicherbereich belegt, in den dann PtrSave->MyArray hineinkopiert wird (und anschliessend noch ein bisschen mehr). Was passiert dan mit dem Speicherbereich auf den nun PtrSave zeigt (lokal deklariert)? bleibt er geschlossen (sprich Memory-leck) oder wird er freigegeben?
 
Hi.

So wie es aussieht hast du die Regel der großen 3 (rule of the big three) nicht beachtet. D.h. du hast einen Fehler bei der Implementation des Destruktors und/oder Kopierkonstruktors und/oder Zuweisungsoperators gemacht (oder du hast sie gar nicht implementiert).

\edit: Zeig deinen Code! Von deiner unzureichenden Beschreibung kann man alles und nichts herauslesen.

Gruß
 
Hmmm...mit deiner ersten Codezeile kopierst du aber keine Daten!
Du bekommst über PtrSave nur Zugriff auf das alte Array.
Um zu kopieren, müsstest du auch PtrSave mit new einen Speicher zuweisen und jedes Element rüberkopieren.(die Schleife kann man sich mit der passenden Funktion sparen, aber nur vom Prinzip her)

Angenommen, du kopierst es: Wenn du mPtr->myArray jetzt nullst und dann deletest, prüft delete auf Null und löscht in Folge gar nichts. Wenn es ohne Überprüfen löschen wurde, hättest du einen schönen Absturz.
Aber du hast recht, du kannst noch über PtrSave zugreifen; wenn du mPtr wirklich löscht solltest du danach auch PtrSave nullen (nur damit es erkennbar ist, dass es weg ist)

Einen lokal def. und allok. Speicher solltest du vor dem Funktionsende selber freigeben.
 
So wie es aussieht hast du die Regel der großen 3 (rule of the big three) nicht beachtet.

erm... noch nie was von gehört...

D.h. du hast einen Fehler bei der Implementation des Destruktors und/oder Kopierkonstruktors und/oder Zuweisungsoperators gemacht (oder du hast sie gar nicht implementiert).

Konstruktor wie auch Destruktor der Klasse sind leer. Wenn ich da was reinschreibe mekkert der Compiler immer...

\edit: Zeig deinen Code! Von deiner unzureichenden Beschreibung kann man alles und nichts herauslesen.

Ich würde gerne, aber das ist ein bisschen vieeeeel... werde mal sehen ob ich das wichtige herauspicken kann.

Hier wäre mal der Header der Klasse mit der vor allem gearbeitet wird:

Code:
class ES_Cluster
{
public:
	ES_Cluster(void);
	~ES_Cluster(void);
	void ClearCluster(void);
	void ConvertHyg(void);
	void LoadCat(std::string CatName, ES_Stellar_Table *mTable);
	void InitCluster(void);
	void CreateCube(ES_Cluster &Cube, long X, long Y, long Z, float AvgColor);
	void AddStarsToScene(scene::ISceneManager *smgr, int CubeOffsetX, int CubeOffsetY, int CubeOffsetZ);
	void AddStalksToScene(scene::ISceneManager *smgr);
	void AddLabelsToScene(scene::ISceneManager *smgr, gui::IGUIEnvironment *mGUI);
	void RemoveStarsFromScene(void);
	void RemoveStalksFromScene(void);
	void RemoveLabelsFromScene(void);
	bool GetStalks(void);
	bool GetLabels(void);
	bool GetStars(void);
	bool GetSystems(void);
	void ToggleStars(scene::ISceneManager *smgr,  int CubeOffsetX, int CubeOffsetY, int CubeOffsetZ);
	void ToggleStalks(scene::ISceneManager *smgr);
	void ToggleLabels(scene::ISceneManager *smgr, gui::IGUIEnvironment *mGUI);
	void SwitchCubes(scene::ISceneManager *smgr, gui::IGUIEnvironment *mGUI, 
					 CubePtr &mCubes, core::vector3df &CurCube, int mDir, video::IImage* GalaxyImg);   
	ES_Star IdentifyNode(CubePtr mCubes, long NodeID);
	void ResetStars(int CubeOffsetX, int CubeOffsetY, int CubeOffsetZ);
	long nStars;
	ES_Star *Stars;
	void CompleteCube(ES_Cluster *Cube, long X, long Y, long Z, float AvgColor);	
	ES_Star GenerateStar(int mAge, double mSeed);
	void ToggleCubes(scene::ISceneManager *smgr, CubePtr mCube);
	void ToggleCntStalks(scene::ISceneManager *smgr, CubePtr mCube);
	void ToggleCntLabels(scene::ISceneManager *smgr, CubePtr mCube, gui::IGUIEnvironment *mGUI);
	void CreateSystems(long X, long Y, long Z);
	void SetBools();	//sets all the bools in a cluster to false. Used for initialisation
	void DeleteSystems();
	double MaxLum;

private:
	bool mLabels;
	bool mStalks;
	bool mStars;
	bool mCubeState;
	bool mSystems;
	void CreateSwitchCubes(CubePtr mCubes[], core::vector3df CurCube, int mDir, video::IImage* GalaxyImg);
	void CompleteStar(ES_Star &mStar);	//used for filling in missing atributes of catalogue stars
	float GetDensity(float AvgColor);
	double mSeed;
	ES_Stellar_Table *StelTable;
}

Das Array das am meisten verwendet wird ist Stars vom Typ ES_Star:

Code:
struct ES_Star
{
	double x;
	double y;
	double z;
	long CubeX;
	long CubeY;
	long CubeZ;
	bool LabelDir;
	core::vector3df Position;
	scene::ISceneNode *node;
	scene::ISceneNode *stalk;
	scene::ISceneNode *label;
	ES_System *MySystem;
	bool HasSystem;
	ES_Sun* Sun;
	double ColorIndex;
};

Der wiederum einen member vom Typ ES_Sun enthält:

Code:
class ES_Sun
{
public:
	ES_Sun(void);
	~ES_Sun(void);

	double	mass;
	long double life;
	long double age;
	long double r_ecosphere;

	std::string Name;
	float Magnitude;
	std::string Spectrum;
	int Fraction;
	std::string LumClass;
	std::string Sequence;
	double Luminosity;
	double Radius; //km
	double SurfTemp; //K
	bool LabelDir;
};

Die Funktion um die es nun geht sieht (im Moment) folgendermassen aus (scheint perfekt zu funktionieren im Gegensatz zu allem anderen was ich probiert habe, und hat auch nach ner halben Stunde dauerbetrieb bis jetzt noch keine Zicken gemacht):

Code:
void ES_Cluster::CompleteCube(ES_Cluster *Cube, long X, long Y, long Z, float AvgColor)
//this function fills missing stars in a cube based on the density received from the galaxy map. Should only be called on the MilkyCat.
{
	mSeed = (X + Y * 4165 + Z * (4165 * 4165)) * 1000;
	srand(mSeed);

	float DensPerc = GetDensity(AvgColor);
	DensPerc -= (Cube->nStars / 10);
	int CountTo = 1000;

	if (DensPerc < 10)
	{
		DensPerc = DensPerc * 10;
		CountTo = 100;
	}
	else if (DensPerc < 20)
	{	
		DensPerc = DensPerc * 5;
		CountTo = 200;
	}
	else if (DensPerc < 50)
	{	
		DensPerc = DensPerc * 2;
		CountTo = 500;
	}
	
	ES_Cluster TempCube;
	ES_Cluster TempCubeSave;
	TempCube.Stars = new ES_Star[CountTo];
	TempCubeSave = *Cube;
	int StarCount = 0;

	for (int i = 0; i < (CountTo); ++i)
	{
		int mCreate = (((double) rand() / (double) RAND_MAX) * 100);
		if (mCreate <= DensPerc)
		{
			double StarSeed = 	mSeed + TempCubeSave.nStars + i;
			TempCube.Stars[StarCount] = GenerateStar(0, StarSeed);
			if (TempCube.Stars[StarCount].Sun->Luminosity <= Cube->MaxLum) //no stars are generated with luminosity bigger than any known stars in this cube, since they would be visible if they existed
				++StarCount;
		}
	}

	if (TempCubeSave.nStars + StarCount > 1000)
		StarCount = 1000 - TempCubeSave.nStars;

	Cube->Stars = NULL;
	delete [] Cube->Stars;

	Cube->Stars = new ES_Star[StarCount + TempCubeSave.nStars];
	Cube->nStars = StarCount + TempCubeSave.nStars;

	for (int i = 0; i < TempCubeSave.nStars; ++i)
	{
			Cube->Stars[i] = TempCubeSave.Stars[i];
	}

	for (int i = TempCubeSave.nStars; i < TempCubeSave.nStars + StarCount; ++i)
	{
			Cube->Stars[i] = TempCube.Stars[i - TempCubeSave.nStars];
	}

	Cube->SetBools();
}
 
erm... noch nie was von gehört...
http://en.wikipedia.org/wiki/Rule_of_three_(C++_programming)
Konstruktor wie auch Destruktor der Klasse sind leer. Wenn ich da was reinschreibe mekkert der Compiler immer...
Und wie?
Ich würde gerne, aber das ist ein bisschen vieeeeel... werde mal sehen ob ich das wichtige herauspicken kann.
Es reichen ja die relevanten Stellen. Also erstmal die Deklaration der Klassen und die 3 genannten speziellen Methoden und die Fehlermeldung des Compilers.

Gruß
 
Hmmm... dank den Antworten hier und ein bisschen mehr lesen fange ich an das Problem zu erkennen. Ich habe im selben Element Pointer die nicht gelöscht werden dürfen, da sie auf einen Katalog zeigen der für die ganze Zeit verwendet wird. Wenn ich sie also lösche gehen mir die Katalogeinträge flöten, was das Programm sofort zum erliegen bringt. Zum anderen ergänze ich die Sterne die aus diesem Katalog ausgelesen werden mit zufallsgenerierten, die nach gebrauch wieder gelöscht werden müssen. Ich muss also die zufallsgenerierten Sterne markieren und sie nach gebrauch einzeln löschen, sodass ihr Speicher freigegeben wird, wärend der Katalog komplett bleibt. Werd das mal versuchen und sehen wie's läuft...
 
Das war eine sehr lehrreiche Lektion... Alles wird nun gelöscht und nichts stürzt mehr ab, und Kopierprozesse habe ich nun auch viel weniger. Nebenbei habe ich zum ersten mal richtig den Unterschied zwischen Heap und Stack memory begriffen... :)
 

Neue Beiträge

Zurück