Segement Fault
-
Du kannst auch vielleicht deinen Debugger mal ganz lieb fragen ...
-
Beim Durchgehen meines Codes sind einige Fragen aufgekommen:
-
spielt die Reihenfolge der delete's eine Rolle? (also ich meine wenn ich am Ende der main() jeden mit new angeforderten Speicher freigebe)
-
Es existiere folgender Code:
//A.h #include "B.h" class A { public: A() {} void doSomething(B* b) { //alle auskommentierten Zeilen für Fall 4 (siehe unten) //b = new B; b->whatEver(); //delete b; } };-> was ist besser/wo liegt der Unterschied zwischen folgenden Aufrufen:
#include "A.h" #include "B.h" int main() { A a; //Fall 1: a.doSomething(new B); //Fall 2: B b2; a.doSomething(&b2); //Fall 3: B* b3 = new B; a.doSomething(b3) delete b3; //Fall 4: B* b4; a.doSomething(b4); }Was passiert in Fall 1? Eigentlich wird ja dort auch mit new neuer Speicher angefordert, aber eben über ein temporäres Objekt? Aber wer gibt den Speicher wieder frei??
Viele Fragen, ich hoffe auf viele Antworten.

-
-
sly schrieb:
- spielt die Reihenfolge der delete's eine Rolle? (also ich meine wenn ich am Ende der main() jeden mit new angeforderten Speicher freigebe)
Nur bei voneinander abhängigen Objekten (und das sollte man dann anders lösen ;))
Was passiert in Fall 1? Eigentlich wird ja dort auch mit new neuer Speicher angefordert, aber eben über ein temporäres Objekt? Aber wer gibt den Speicher wieder frei??
Niemand, das nennt man dann Speicherleck

In Fall 2 ist das Objekt ein gültiges Objekt auf dem Stack von main, in Fall 3 ein gültiges Objekt auf dem Heap.
Fall 4 ist undefiniert, da b4 nicht auf ein gültiges Objekt zeigt.
-
zu Frage 1) Es ist egal, in welcher Reihenfolge du den speicher freigibst, musst dich nich an die reihenfolge halten, wie du ihn mit new angefordert hast.
zu 2)
Fall 1 ist schlecht, da du, wie bereits scholn gesagt hast, niemand den speicher freigibt. Das müsste dann in der funktion doSomething gemacht werden, was aber echt schlehct wäre, da danach das objekt nicht mehr nutzbar ist, falls man es anderweitig übergtibt. Außerdem gäbe es eine zugriffsverletzung, wenn du versuchst, ein objekt das auf dem stack liegt zu löschen.Fall 2: Ist in diesem kontext wohl die beste Lösung, solagne B nicht allzu viel speicher auf dem stack anlegt. du übergibst Dosomething die Adresse, und da wird dann wa mit gemacht. So is das shcon ganz gut. Du könntest aber auch eine Referenz übergeben, manche machen das lieber so, is meiner meinung nach auch besserer Stil alsdie Adressübergabe.
Fall 3: Macht man so, wenn B ein sehr großes Objekt ist, was nicht auf den Stack passt. Oder man macht es, wenn man b noch außerhalb des scopes benötigt indem es erstellt wurde. Sieht mir nich so aus, also in dem fall auch nich anzuraten.
Fall 4 ist totaler Müll
Da du einbfach einen Zeiger übergibts der irgendwohin zeigt und nicht auf ein fertiges B, sieht das im code schonmal echt scheiße aus
Wieterhin wird das Objekt dann in DoSomething erstellt, danmit wird was gemacht und dann wird es wieder gelöscht. Wozu übergibts du dann überhaupt einen Zeiger als parameter? Den brauchst du gar nich, DoSomething kann den Zeiger auch lokal speichern.Also, Fall 2 und Fall 3 is OK, der rest nich so. fall 1 möglicherweise noch, wenn du Objekte hast, die sich im Konstruktor bei einer Managerklasse registrieren und dann von dieser gelöscht werden.
-
Nur bei voneinander abhängigen Objekten (und das sollte man dann anders lösen )
Wie?
Also angenommen es gibt zwei Klassen A und B, A besitze als Membervariable einen Zeiger auf ein Objekt der Klasse B.
Objekte der Klassen A und B werden jeweils in main() mit new angelegt und wieder mit delete freigegeben. Ist das schlecht?
Fall 1 ist schlecht, da du, wie bereits scholn gesagt hast, niemand den speicher freigibt. Das müsste dann in der funktion doSomething gemacht werden
WIE müsste ich das machen? Wie kann ich auf das Objekt überhaupt zugreifen? (es wurde ja kein Bezeichner eingeführt..)
Du könntest aber auch eine Referenz übergeben, manche machen das lieber so, is meiner meinung nach auch besserer Stil alsdie Adressübergabe.
So?
void doSomething(B& b) { b.whatEver(); }#include "A.h" #include "B.h" int main() { A a; B b; a.doSomething(b); }Fall 4 ist totaler Müll
Das war deutlich. Scheiße sieht es wohl wirklich im Code aus und unsinnig ist es wahrscheinlich auch.
Kannst mir aber nochmal genauer erklären, warum es speichertechnisch nicht korrekt ist?
Ok, ich übergebe einen undefinierten Zeiger, aber in der Methode wird ja dann mit new ein gültiges Objekt erzeugt und brav wieder freigegeben.
Ist sowas nicht erlaubt? Einen Zeiger übergeben, der erstmal nirgendwo hinzeigt und erst in der Methode dann auf ein Objekt gerichtet wird?
-
sly schrieb:
Wie?
Also angenommen es gibt zwei Klassen A und B, A besitze als Membervariable einen Zeiger auf ein Objekt der Klasse B.Objekte der Klassen A und B werden jeweils in main() mit new angelegt und wieder mit delete freigegeben. Ist das schlecht?
Grundsätzlich nicht. Aber angenommen im Destruktor von A wird b->free_resources() aufgerufen, wäre es schlecht wenn Du b vor a löscht. Denn dann wäre A von B abhängig. Und damit sowas nicht passiert, sollte man es anders lösen

WIE müsste ich das machen? Wie kann ich auf das Objekt überhaupt zugreifen? (es wurde ja kein Bezeichner eingeführt..)
Du hast in doSomething doch den Parameter, wo das Objekt übergeben wird!? DAS müsstest Du löschen - oder es anders machen

Das war deutlich. Scheiße sieht es wohl wirklich im Code aus und unsinnig ist es wahrscheinlich auch.
Kannst mir aber nochmal genauer erklären, warum es speichertechnisch nicht korrekt ist?
Es ist speichertechnisch nicht korrekt weil es keinen Speicher gibt. Es gibt nur einen Zeiger, und der zeigt auf nichts. Wenn Du "nichts"->irgendwas() aufrufst ist das schlicht und ergreifend nicht definiert.
Ok, ich übergebe einen undefinierten Zeiger, aber in der Methode wird ja dann mit new ein gültiges Objekt erzeugt und brav wieder freigegeben.
Das hast Du doch auskommentiert?!?
-
Das hast Du doch auskommentiert?!?
Dann hat du es falsch verstanden. Es sollte heißen, dass der auskommentierte Code für Fall4 (und nur für 4) als _nicht_ auskommentiert gelten soll.

War wohl etwas missverständlich.
-
sly schrieb:
Fall 4 ist totaler Müll
Das war deutlich. Scheiße sieht es wohl wirklich im Code aus und unsinnig ist es wahrscheinlich auch.
Kannst mir aber nochmal genauer erklären, warum es speichertechnisch nicht korrekt ist?
Naja, speichertechnisch ist es schon korrekt. Zumindest wird nichts Verbotenes gemacht. Das Problem hierbei ist, es ergibt keinen Sinn. Warum übergibst du einen Zeiger, mit dem überhaupt nichts gemacht wird? Der Zeiger selbst ist in der Funktion ja nicht verfügbar, da lediglich eine Kopie seines Wertes (Adresse) übergeben wird. Und diese ist undefiniert, also kann man damit nichts anfangen. Wenn du nichts übergeben würdest, hätte das den gleichen Effekt.
void doSomething() { B* b = new B; b->whatEver(); delete b; } int main() { a.doSomething(); }
-
Ok ich seh's ja ein. Sollte nur ein Beispiel sein, was syntaktisch/speichertechnisch erlaubt ist.
Aber zu meinem ursprünglichen Problem: ich glaube/hoffe, ich konnte die Problemursache eingrenzen. Es liegt wohl irgendwie an einer Kopie eines Objekts, das seinerseits einen vector mit einem Klassentyp enthält.
Ein kleines Beispiel:
#include "B.h" class A { private: vector<B> myVector; public: A() {} void add_B(const B& b) { myVector.push_back(b) } const vector<B>& getVector() const { return myVector; } };class B { private: int m_x; public: B(int x): m_x(x) {} int getX() const { return m_x; } };int main() { A a1; B b; a1.add_B(b); A a2(a1); //COPY-Konstruktor!? cout << a2.getVector().getX(); //Hier kracht's }Und zwar bekomme ich bei diesem kleinen Programm den gleichen "Segementation Fault" -Fehler.
Muss ich in A einen Copy-Konstuktor für die Kopie des Vectors implementieren? Bisher dachte ich eigentlich, das würde die STL übernehmen.
P.S.: gutes Forum

-
Nein, du musst in diesem Fall keinen copy ctor implementieren. Der Compiler legt ohne eigenen copy ctor selbst einen an, in dem alle Member über ihren jeweiligen copy ctor initialisiert werden. Und std::vector hat selbstverständlich einen solchen ctor, damit alles korrekt durchgeführt werden kann.
Dein Problem ist, das mit
a2.getVector()lediglich die std::vector Instanz selbst zurückgegeben wird. Und ein std::vector kennt nunmal keine Funktion getX. Du musst schon auf ein Element des Containers zugreifen, welches vom Typ B ist, damit du dann die Funktion getX aufrufen kannst.