Speicherzugriffsfehler bei vector<>

Schnelle objektorientierte, kompilierende Programmiersprache.
Diddi
Beiträge: 7
Registriert: Di Mär 18, 2014 10:53 am

Speicherzugriffsfehler bei vector<>

Beitrag von Diddi » Di Jul 01, 2014 4:05 pm

Liebe C++-Kollegen!

Zur Auswertung von Messdaten schreibe ich ein Programm, das diese zunächst einliest. Jeder eingelesene Datensatz wird in einem Objekt des Typs Dataset gespeichert. Die Datasets werden von der Hauptanwendungsklasse Cpw in einem vector <Dataset> abgelegt.
Es kompiliert nun ohne Fehler, aber zur Laufzeit gibt es Speicherzugriffsfehler, wenn ich entweder mindestens drei Datensätze lade oder wenn ich das Programm beende und mindestens zwei Datensätze geladen sind.

Ich hänge mal die Dateien cpw.cpp, cpw.h und main.cpp an.
In der Funktion parse_line() erkennt man, dass ich ein Dataset dynamisch anlege und mit setVector.push_back(*dataset) dem Vektor hinzufüge. Wenn ich in die Konsole "exit" tippe, wird außerdem setVector.clear() gemacht. Ist das nicht in Ordnung so?

Viele Grüße
Diddi
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.

Benutzeravatar
oenone
Beiträge: 223
Registriert: Do Sep 01, 2011 2:42 pm
Wohnort: Bremen
Kontaktdaten:

Re: Speicherzugriffsfehler bei vector<>

Beitrag von oenone » Di Jul 01, 2014 7:42 pm

was sagt denn der Debugger, an welcher Stelle der Fehler pasiert?

Lirrec
Beiträge: 15
Registriert: Mo Feb 20, 2012 11:55 am

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Lirrec » Di Jul 01, 2014 8:19 pm

Diddi hat geschrieben: In der Funktion parse_line() erkennt man, dass ich ein Dataset dynamisch anlege und mit setVector.push_back(*dataset) dem Vektor hinzufüge. Wenn ich in die Konsole "exit" tippe, wird außerdem setVector.clear() gemacht. Ist das nicht in Ordnung so?
In C++ bist du selbst dafür verantwortlich, Objekte die du dynamisch anlegst auch wieder freizugeben.
Das machst du hier zwar im Fehlerfall, aber nicht nachdem du das Dataset in den Vektor geschoben hast.
std::vector nimmt dir das nicht ab, bei push_back z.B. ist explizit angegeben, dass das Objekt beim Einfügen kopiert wird. Also solltest du das ursprüngliche Dataset nach dem Kopieren in den Vector auch wieder freigeben.
Das ist auf jeden Fall ein Memory Leak, muss aber nicht unbedingt für die Abstürze verantwortlich sein.

Mit einem Debugger mal nachzusehen, wo das Problem tatsächlich liegt wäre hier auf jeden Fall hilfreich.
Interessant wäre auch zu wissen, wie deine Klasse Dataset aussieht. wenn sie Pointer als Member hat musst du beim Kopieren aufpassen, dass du tatsächlich die Daten auf die der Zeiger zeigt kopierst, nicht nur den Zeiger.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Xin » Mi Jul 02, 2014 8:57 am

Diddi hat geschrieben:Zur Auswertung von Messdaten schreibe ich ein Programm, das diese zunächst einliest. Jeder eingelesene Datensatz wird in einem Objekt des Typs Dataset gespeichert. Die Datasets werden von der Hauptanwendungsklasse Cpw in einem vector <Dataset> abgelegt.
Es kompiliert nun ohne Fehler, aber zur Laufzeit gibt es Speicherzugriffsfehler, wenn ich entweder mindestens drei Datensätze lade oder wenn ich das Programm beende und mindestens zwei Datensätze geladen sind.
Da das Projekt nicht vollständig ist, kann man im Prinzip nix nachvollziehen.

Das erste, was mir auffiel - was falsch, aber nicht der Fehler ist - ist folgendes:

Code: Alles auswählen

		Dataset *dataset = new Dataset();
		if (dataset == NULL)
Das funktioniert nicht. Im Fehlerfall würde eine Exception (std::bad_alloc) fliegen und das Programm killen. Die Überprüfung kannst Du Dir in der gegenwärtigen Form also auch komplett schenken.
Du musst new(std::no_throw) Dataset() aufrufen, wenn Du gegen NULL (in C++ besser nullptr) prüfen möchtest oder die Exception fangen:

Code: Alles auswählen

		try
		{  
			Dataset *dataset = new Dataset();
		}
		catch( std::badalloc & badalloc )
		{
			cout << "Konnte keinen Speicher reservieren\n";
			return 1;
		}
Aber das tut hier erstmal nichts zur Sache.
Diddi hat geschrieben:Ich hänge mal die Dateien cpw.cpp, cpw.h und main.cpp an.
In der Funktion parse_line() erkennt man, dass ich ein Dataset dynamisch anlege und mit setVector.push_back(*dataset) dem Vektor hinzufüge. Wenn ich in die Konsole "exit" tippe, wird außerdem setVector.clear() gemacht. Ist das nicht in Ordnung so?
Vector kopiert Dein Datenset. Das Bedeutet, dass das Datenset, dass Du mit new angelegt hast, weiterhin existiert und nie freigegeben wird.

Ich vermute den Fehler eher beim Destruktor der Klasse DataSet. Aber wissen tue ich's auch nicht.
Was sagt denn der Debugger, wo es knallt?

Noch ein Detail:

Code: Alles auswählen

		setVector.push_back(*dataset);
		activeSet = setVector.size() - 1;
Dreh die Zeilen um und Du sparst Dir die Subtraktion. Ist Kleinkram, kann man sich aber angewöhnen. Wenn die entsprechende Passage dann 10millionenmal ausgeführt wird, merkt man solche Kleinigkeiten.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Diddi
Beiträge: 7
Registriert: Di Mär 18, 2014 10:53 am

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Diddi » Mi Jul 02, 2014 9:38 am

Der Debugger sagte, dass es an setVector.push_back(*dataset) scheiterte.
Ist aber nicht mehr wichtig, denn ich habe dank eurer Beiträge jetzt verstanden, dass pusch_back() das ganze Objekt kopiert. Da das natürlich nicht im Sinne des Erfinders ist, habe ich es wieder so hingedreht, wie ich es ursprünglich machen wollte, nämlich dass der setVector nur Zeiger aufnimmt:

Code: Alles auswählen

vector<Dataset *> setVector;
...
Dataset *dataset = new Dataset();
int loadsuccess = dataset->load(word[1]);
setVector.push_back(dataset);
Damit habe ich nun keine Schwierigkeiten mehr. Ich vermute, dass ich einen Kopierkonstruktor oder solches Gedöns hätte definieren müssen. Ich meine, nun einen angemessenen Destruktor von Cpw zu haben:

Code: Alles auswählen

Cpw::~Cpw()
{
	for (unsigned int i = 0; i < setVector.size(); i++)
		delete setVector[i];
}
In der Klasse Dataset gehe ich entsprechend vor, nur dass ich dort eine Liste habe.

Xin, vielen Dank für deine Kommentare zur Exception und zur Subtraktion

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Xin » Mi Jul 02, 2014 10:03 am

Diddi hat geschrieben:Damit habe ich nun keine Schwierigkeiten mehr. Ich vermute, dass ich einen Kopierkonstruktor oder solches Gedöns hätte definieren müssen.
Das kann nicht schaden. Wenn Du ihn nämlich nicht definierst, baut Dir C++ einen. Der kopiert dann zum Beispiel einen Zeiger, dabei wird aber nicht garantiert, dass der Zeiger auf etwas sinnvolles zeigt - zum Beispiel wenn das ursprüngliche Objekt zerstört wird und der Destruktor auch den Speicher freigibt, auf den der Zeiger zeigt.
Wird nun die Kopie freigegeben, zeigt der Zeiger auf bereits freigegebenen Speicher, der dann nochmals freigegeben wird. Und das knallt dann.
Diddi hat geschrieben: Ich meine, nun einen angemessenen Destruktor von Cpw zu haben:

Code: Alles auswählen

Cpw::~Cpw()
{
	for (unsigned int i = 0; i < setVector.size(); i++)
		delete setVector[i];
}
In der Klasse Dataset gehe ich entsprechend vor, nur dass ich dort eine Liste habe.
Der Destruktor ist absolut in Ordnung. Drei Tipps:
1. Du rufst laufend size() ab. Warum?

Code: Alles auswählen

Cpw::~Cpw()
{
	for (unsigned int i = 0, n = setVector.size(); i < n; i++)
		delete setVector[i];
}
Sehr gut finde ich übrigens, dass Du 'unsigned int' statt 'int' nutzt.

2. In C++ bedient man sich gerne Iteratoren, was mit 'auto' seit C++11 auch angenehmer ist:

Code: Alles auswählen

Cpw::~Cpw()
{
	for (auto it=setVector.begin(), eit=setVector.end(); it!=eit; it++)
		delete *it;
}
Der Zugriff per operator [] ist beim Vektor sehr billig. Es könnte aber auch ein Container sein, der mit Iteratoren schneller wäre.

3. Wenn Du nicht gerade auf alten Compilern arbeitest, kannst Du ein Range-Loop einsetzen:

Code: Alles auswählen

Cpw::~Cpw()
{
	for (Dataset * value : setVector)
		delete value;
}
Diddi hat geschrieben:Xin, vielen Dank für deine Kommentare zur Exception und zur Subtraktion
Wir sind doch hier, um Know-How auszutauschen. :-)
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Architekt » Mi Jul 02, 2014 10:58 am

Ich empfehle hierbei ganz stark smart pointer wie std::unique_ptr. ;) Die nehmen dir diese lästige manuelle Freigabe ab.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Xin » Mi Jul 02, 2014 11:07 am

Architekt hat geschrieben:Ich empfehle hierbei ganz stark smart pointer wie std::unique_ptr. ;) Die nehmen dir diese lästige manuelle Freigabe ab.
Womit ich ihm eigentlich auch nur recht geben kann und mich gerade frage, wieso mir das eben nicht eingefallen ist. :-D
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Diddi
Beiträge: 7
Registriert: Di Mär 18, 2014 10:53 am

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Diddi » Mi Jul 02, 2014 6:25 pm

Architekt hat geschrieben:Ich empfehle hierbei ganz stark smart pointer wie std::unique_ptr. ;) Die nehmen dir diese lästige manuelle Freigabe ab.
Mag ja sein, dass sie bequemer sind. Ich habe es nun mal in meinem C++-Buch gelernt, nicht benötigte Objekte wieder freizugeben und finde es gar nicht so schlimm, zumal ich gerne die Zügel über die Objektverwaltung selber behalte. ;) Außerdem scheint std::unique_ptr C++11-Standard zu sein und da mein Betriebssystem von 2011 ist, glaube ich kaum, dass mir solche zur Verfügung stehen.

Wie gesagt, nun geht es wieder sehr gut und nachdem ich mich mit einem Funktionszeiger herumgeschlagen habe, den ich für das Sortieren einer Liste benötige (war am Ende eigentlich recht leicht), scheinen mir die größten Stolpersteine überwunden zu sein.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Speicherzugriffsfehler bei vector<>

Beitrag von Architekt » Mi Jul 02, 2014 7:24 pm

Die C++ Version hängt mit deinem Compiler zusammen und nicht mit deinem Betriebssystem...
Du kannst dir jederzeit eine neue Compiler Version herunterladen, für die Gnu Compiler Collection z.B. hier: https://gcc.gnu.org/

Antworten