Speicher freigeben: Aufrufer oder Klasse?
-
Im allgemeinen sollte wohl derjenige, der den Speicher beschafft, ihn auch wieder freigeben. Ausnahmen sind natürlich Klassen, die sich explizit um dieses Speichermanagement kümmern, indem sie einen Zeiger kapseln (wie z.B. std::auto_ptr).
-
Konrad Rudolph schrieb:
Im allgemeinen sollte wohl derjenige, der den Speicher beschafft, ihn auch wieder freigeben. Ausnahmen sind natürlich Klassen, die sich explizit um dieses Speichermanagement kümmern, indem sie einen Zeiger kapseln (wie z.B. std::auto_ptr).
Genau das ist (fast immer) so! Ich habe bemerkt, dass die "Übergabe von alleinigen Besitzerrechten" (std::auto_ptr) eigentlich nur selten gebraucht wird. Ist auf jeden bei uns so, kommt sicher auch auf den Programmierstil an.
Aber ich glaube nicht, dass in diesem Beispiel der Speicher nur noch A gehören soll, sonst könnte A das Objekt ja selber instanzieren.
-
Soweit einleuchtend.
Stellt euch nun ein vector mit Objeketen der obigen Klasse A (zur Erinnerung: enthält Zeiger auf
vor:class X { private: std::vector<A> viele_A; public: void add_A(const A& a) { viele_A.push_back(a); } }int main() { B* b = new B(17); A a(b); X x; x.push_back(a); x.push_back(a); x.push_back(a); //... delete b; //oder lieber die Klasse A das erledigen lassen?? }
-
wie soll a das objekt b löschen wenn mehrere As sich ein B teilen?
-
lolz schrieb:
wie soll a das objekt b löschen wenn mehrere As sich ein B teilen?
Das ginge schon, z.B. mit einem Referentzzähler in A. Aber ich sähe hier den Sinn nicht.
-
So wie der Code jetzt ist ist er auf jeden Fall böse. Woher sollen die As wissen, dass ihr B* nicht mehr gültig ist? Du müsstest hier auf jeden Fall irgendwas mit Referenzzählung machen, am Besten automatisiert wie in Boost.SmartPointer.
-
Konrad Rudolph schrieb:
lolz schrieb:
wie soll a das objekt b löschen wenn mehrere As sich ein B teilen?
Das ginge schon, z.B. mit einem Referentzzähler in A. Aber ich sähe hier den Sinn nicht.
dann gibt a aber eine resource wieder frei und löscht sie nicht (ok, wenn die klasse nicht wohlgeformt ist, dann geht da sschon)
-
.filmor schrieb:
Du müsstest hier auf jeden Fall irgendwas mit Referenzzählung machen, am Besten automatisiert wie in
Boost.SmartPointer.Ich bin mal grob drübergeflogen.
Bisher kannte ich nur auto_ptr.
Wäre shared_ptr hier das richtige für die B-Objekte?
-
Ja. Zumindest mit den Informationen, die du gegeben hast.
-
.filmor schrieb:
Zumindest mit den Informationen, die du gegeben hast.
In meinem "richtigen" Programm gibt es "nur" den Unterschied, dass die Objekte der Klasse A selbst als Zeiger im Vector gespeichert werden (da es recht große Objekte sind):
class X { private: std::vector<A*> viele_APointer;}
Diese Zeiger werden im Destruktor der Klasse X gelöscht:
X::~X() { for (int i = 0; i < viele_APointer.size(); ++i) delete viele_APointer[i]; }Ändert das was an der Situation?
-
Hmm, ich blick da im Moment nicht so richtig durch (kann auch an der Frühe liegen ;)).
So wie es jetzt aussieht ist doch die Zuständigkeit klar geregelt, die liegt nämlich bei dem X-Objekt?!
-
.filmor schrieb:
Hmm, ich blick da im Moment nicht so richtig durch (kann auch an der Frühe liegen ;)).
So wie es jetzt aussieht ist doch die Zuständigkeit klar geregelt, die liegt nämlich bei dem X-Objekt?!Das ist noch nicht zu sehen. Die Frage ist: Wer macht das "new" ?
Der Aufrufer ?void X::add(A* a) { viele_A.push_back(a); } int main(int argc, char* argv[]) { A* a = new A(); X x; x.add(a); delete a; // BÖÖÖÖÖSE };... oder das X
void X::add(A& a) { A* myA = new A(a); // kostet eine CopyKonstruktion viele_A.push_back(myA); } int main(int argc, char* argv[]) { A* pa = new A(); A a; X x; x.add(*pa); delete pa; // macht nix x.add(a); };Alternativ kann man natürlich die Zuständigkeit komplett aus X herausverlegen:
class X { public: X::~X() {} A* getA(int i) { return viele_APointer.at(i); } int getAcount() { return viele_APointer.size(); } }; // Benutzer muss eine "delete-Schleife" implementierenAber das sieht nach einem selbstgestrickten "Container-Wrapper" aus, bei dem ich sehr vorsichtig wäre; da steckt der Teufel oft im Detail.
Gruß,
Simon2.
-
Weil das stückchenweise Code posten für etwas Verwirrung gesorgt hat, hier noch mal alles auf einen Blick, wie der aktuelle Stand der Dinge ist:

class B { //unwichtig (enthält keine Zeiger) } class A { private: B* _b; int _y; public: A(B* b, int y) :_b(b), :_y(y) {} } class X { private: std::vector<A*> viele_A; public: void add_A(A* a) { viele_A.push_back(a); } ~X() { for (int i = 0; i < viele_A.size(); ++i) { delete[i]; } } };int main() { B* b = new B; X x; x.add_A(new A(b, 0); x.add_A(new A(b, 1); x.add_A(new A(b, 2); delete b; }Also für mich stellt sich das wie folgt dar:
- Es wird _einmal_ ein neues B auf dem Heap erzeugt.
- In den Vector viele_A werden Speicheradressen auf Objekte der Klasse A auf dem Heap gespeichert.
-> diese Objekte werden auch schön wieder im Destruktor der Klasse X freigegeben. - Jedem Objekt der Klasse A wird als Parameter neben dem int-Wert noch die Adresse auf das Objekt der Klasse B auf dem Heap mitgegeben.
-> um die Freigabe dieses Objekts der Klasse B kümmert sich der Aufrufer mit delete b;
So, und jetzt seid ihr dran mir zu erzählen, warum dieser Code böse ist / wie man es anders machen sollte / ob smart pointer wie der shared_ptr nötigt sind.

-
Du musst dir nur über die Zuständigkeiten Gedanken machen:
A -> wird von X verwaltet. (Falls A polymorph ist solltest du dir Boost.PointerContainer anschauen)
X -> liegt auf dem Stack
B -> nicht ganz eindeutig. Falls du SharedPointer verwendest, musst du den echten Zeiger direkt nach dem Einfügen unschädlich machen:int main() { X x; { B* b = new B; x.add_A(new A(b, 0); x.add_A(new A(b, 1); x.add_A(new A(b, 2); } }Das Beispiel ist aber meiner Meinung nach zu abstrakt, um eine Entscheidung für oder gegen SharedPointer zu fällen.
-
Hi sly,
ich würde
- "die As" von X erzeugen lassen und
- "den As" nur ein B& übergeben.
Dann ist die Zuständigkeit klar.
Wenn Dein B tatsächlich ein "singleton" ist, könntest Du es in ein solches Pattern einsetzen .... wenn das aber aufwendiger als notwendig ist, kann man eine Referenz in den X-Ctor aufnehmen....
...und ganz sauber wäre es dann, wenn Du noch auf const-correctness achtest.
(Ich tue jetzt mal so, als ob A und X keine non-const-Methoden von B aufrufen)class B { //unwichtig (enthält keine Zeiger) } class A { private: const B& _b; int _y; public: A(const B& b, int y) :_b(b), :_y(y) {} } class X { private: const B& _b; std::vector<A*> viele_A; // Kopieren unterbinden X(const X&); const X& operator=(const X&); public: X(const B& b) : _b(b) {} void add_A(int i) { viele_A.push_back(new A(b, i)); } ~X() { for (int i = 0; i < viele_A.size(); ++i) { delete[i]; } } };int main() { B* b = new B; X x(*b); x.add_A(0); x.add_A(1); x.add_A(2); delete b; }Gruß,
Simon2.