Importer:Error:WasEmpty
-
Cpp_Anfaeger schrieb:
Danke für den Hinweis, vector u. unique_ptr als dynamisches Array kannte ich noch nicht. Ich werde mich die Tage genauer damit beschäftigen. Gehören die Klassen zum C++ Standard, oder ist nur das std::array standard? Oder ist das einfach eine Klasse ähnlich wie in VC++ CStringArray?
Das sind allesamt Klassen aus der C++-Standardbibliothek. Gerade vector ist wohl einer der am häufigsten benutzten Teile der Standardbibliothek und sollte daher unbedingt bekannt sein. unique_ptr + new ist hingegen eher exotisch und wurde wohl nur erwähnt wegen ein paar winzigkleinen Unterschieden zwischen vector und der Kombo unique_ptr + new, über die sich sonst irgendein Klugscheißer beschwert hätte. Ansonsten machen beide praktisch das gleiche, aber vector ist viel komfortabler.
krümelkacker schrieb:
Woher weiß der Compiler in Zeile 20 (wenn ich delete[] p_merker verwenden würde), wie viel Byte Speicher das Array hat u. dementsprechend wie viel Speicher er löschen muss?
Der Compiler weiß davon gar nichts, der ist nur dafür zuständig, dein Programm zu übersetzen. Die Funktionen new und delete sind es hingegen, die das wissen (oder genauer gesagt, die Betriebssystemfunktionen, die hinter new und delete stecken und von diesen benutzt werden). Das machen sie ganz einfach, indem sie sich diese Daten irgendwo merken. Gängige Methoden sind, dass sie eine (globale) Tabelle mit Verwaltungsdaten führen oder aber, dass sie vor den reservierten Bereich ein paar Daten über die Größe dieses Bereichs schreiben.
-
Cpp_Anfaeger schrieb:
Gehören die Klassen zum C++ Standard, oder ist nur das std::array standard? Oder ist das einfach eine Klasse ähnlich wie in VC++ CStringArray?
Du kannst dir merken, das in der Regel alle Klassen im Namensraum std auch Teil der Standardbibliothek von C++ sind (Es gibt Ausnahmen, bei denen dann doch spezifische Implementierungen enthalten sind, das sollte sich aber in Grenzen halten).
-
SeppJ schrieb:
Das sind allesamt Klassen aus der C++-Standardbibliothek. Gerade vector ist wohl einer der am häufigsten benutzten Teile der Standardbibliothek und sollte daher unbedingt bekannt sein. unique_ptr + new ist hingegen eher exotisch und wurde wohl nur erwähnt wegen ein paar winzigkleinen Unterschieden zwischen vector und der Kombo unique_ptr + new, über die sich sonst irgendein Klugscheißer beschwert hätte. Ansonsten machen beide praktisch das gleiche, aber vector ist viel komfortabler.
danke, ja ich meinte den Befehl, den der Compiler umsetzt. Alles klar, grob genommen ist alles std standard.
Was ich fragen wollte im 2. Teil, ist aber eigentlich:
Löscht der Befehl delete[] p_merker in meinem Code wirklich das komplette Array, weil ich als Übergabeparameter in der Funktion showArray nur einen *Zeiger int p_array habe:
#include <iostream> using namespace std; int* inigetArray(){ int *p_array = new int[10]; int *p_merker = p_array; // Anfangsadresse merken for(int i=0; i<10; i++){ *p_array = i; p_array++; } return p_merker; } void showArray(int *p_array){ int *p_merker = p_array; // Anfangsadresse merken for( int i=0; i<10; i++){ cout << i << ".: " << *p_array << endl; p_array++; } delete[] p_merker; // immer noch Speicherleck? } int main(int argc, char** argv){ showArray(inigetArray()); return 0; }
-
Cpp_Anfaeger schrieb:
Löscht der Befehl delete[] p_merker in meinem Code wirklich das komplette Array, weil ich als Übergabeparameter in der Funktion showArray nur einen *Zeiger int p_array habe:
Ja, was solltest du denn sonst haben, anstatt eines Zeigers?
Aber nochmal: Auch wenn dieses Programm jetzt korrekt abläuft, so ist diese Methode sehr, sehr fehleranfällig. Wenn du Ressourcen (z.B. Speicher) verwaltest, dann benutze immer spezialisierte Halterklassen, die mit RAII programmiert wurden (und zwar nur eine Ressource pro Instanz!). Das ist nicht nur einfacher in der Benutzung, sondern auch äußerst robust gegen Fehler. Die genannten std::vector (für dynamische Felder) und std::unique_ptr (für alle möglichen Arten von dynamischem Speicher) sind jeweils solche Klassen. Schon fertig in der Standardbibliothek verfügbar, weil man dies (zumindest den vector, den unique_ptr eher seltener) so oft benötigt.
-
SeppJ schrieb:
Cpp_Anfaeger schrieb:
Löscht der Befehl delete[] p_merker in meinem Code wirklich das komplette Array, weil ich als Übergabeparameter in der Funktion showArray nur einen *Zeiger int p_array habe:
Ja, was solltest du denn sonst haben, anstatt eines Zeigers?
Aber nochmal: Auch wenn dieses Programm jetzt korrekt abläuft, so ist diese Methode sehr, sehr fehleranfällig. Wenn du Ressourcen (z.B. Speicher) verwaltest, dann benutze immer spezialisierte Halterklassen, die mit RAII programmiert wurden (und zwar nur eine Ressource pro Instanz!). Das ist nicht nur einfacher in der Benutzung, sondern auch äußerst robust gegen Fehler. Die genannten std::vector (für dynamische Felder) und std::unique_ptr (für alle möglichen Arten von dynamischem Speicher) sind jeweils solche Klassen. Schon fertig in der Standardbibliothek verfügbar, weil man dies (zumindest den vector, den unique_ptr eher seltener) so oft benötigt.
Nee nee klar, ich weiß, dass es viel sauberer ist, a la vector & co zu codieren. Ich wollte hier nur mit Zeigern rumprobieren, um den Effekt besser zu verstehen.
OK danke, ich dachte nur, dass ich in der Funktion inigetArray() insgesamt 40 Byte reserviert habe, und in der Funktion showArray aber nur 4 Byte gelöscht habe, weil der Zeiger von dieser zweiten Funktion ja nur noch 4 Byte groß ist. So blieben nach meiner Überlegung 36 Byte als Leck unreichbar.
-
Ich bin mir nicht sicher aber ich glaube ein Pointer ist immer gleich gross, es geht nur darum eine Speicheradresse zu speichern.
Das Objekt befindet sich dann an der Speicheradresse auf die der Pointer zeigt.
Deswegen kannst du auch Pointer ziemlich billig per return uebergeben oder e.g. ein vector<class *> vec; machen um bei einer (Vergroesserung) des Vectors nur die Pointer und nicht die ganzen Objekte zu kopieren auf die die Pointer zeigen.
Was Speicherlecks angeht gibt es glaube ich 2 grundsaetzliche Probleme.
1. Du hast den Pointer die ganze Zeit noch in der Hand und vergisst einfach delete auzurufen.
2. Du verlierst den letzten Pointer der noch auf das Objekt zeigt und bist nicht mehr in der Lage delete aufzurufen.
Bsp.: vector<class *> vec;
Wenn der Vector out of scope geht oder mit vec.clear(); geleert wird hast du ein Speicherleck.
Du muesstest bei der Konstellation erst manuell mit einer Schleife durch alle Pointer gehen und einzeln delete aufrufen.Bsp.: vector<unique_ptr <class> > vec;
Hier hast du den std unique pointer der das Objekt automatisch wegraeumt sobald es out of scope geht.
Hier ist aber z.b. auch wichtig zu wissen, dass der Vector, wenn er als Member in einer Klasse deklariert wurde, erst out of scope geht, sobald das Objekt zerstoert wurde. <-- Das ist mir mal passiert.
-
Ruvi schrieb:
Was Speicherlecks angeht gibt es glaube ich 2 grundsaetzliche Probleme.
1. Du hast den Pointer die ganze Zeit noch in der Hand und vergisst einfach delete auzurufen.
2. Du verlierst den letzten Pointer der noch auf das Objekt zeigt und bist nicht mehr in der Lage delete aufzurufen.
Bsp.: vector<class *> vec;
Wenn der Vector out of scope geht oder mit vec.clear(); geleert wird hast du ein Speicherleck.
Du muesstest bei der Konstellation erst manuell mit einer Schleife durch alle Pointer gehen und einzeln delete aufrufen.Bsp.: vector<unique_ptr <class> > vec;
Hier hast du den std unique pointer der das Objekt automatisch wegraeumt sobald es out of scope geht.
Hier ist aber z.b. auch wichtig zu wissen, dass der vector wenn er als member in einer Klasse deklariert wurde erst out of scope geht sobald, dass Objekt zerstoert wurde. <-- Das ist mir mal passiert.Ja, das genau darum gehts mir. Wenn ich mich in demselben Scope befinde:
*int p_array = new int[10];
und delete [] p_array - wie von krümelkacker vorgeschlagen - aufrufe, wird nach meinem Verständis das komplette Array gelöscht, da die gesamte Arraygröße im Scope bekannt ist.
Sobald ich aber die Funktion verlasse und nur noch eine Adresse zurückgebe (Zeiger), dann weiß die nächste Funktion doch nicht, wie groß das Array ist, und sollte demnach nicht das gesamt Array freigeben können.
Man müsste dann wie von dir vorgeschlagen mittels einer Schleife die einzelnen Zeigerelemente löschen, habe ich es richtig verstanden?
-
Nein, ein delete[] reicht. Du hast immer nur einen Pointer, auch wenn du das im gleichen Scope machen würdest.
-
cooky451 schrieb:
Nein, ein delete[] reicht. Du hast immer nur einen Pointer, auch wenn du das im gleichen Scope machen würdest.
Ich bin aber schon in der zweiten Funktion - also nicht mehr im selben Scope.
*Edit: Achso, es reicht demnach nur die Anfangsadresse aufzulösen und das OS gibt alles Folgende frei? Woher weiß das OS, wie viel Bytes nachfolgend freigegeben werden müssen, wenn ich außerhalb des Scope bin? Oder ist das irgendwie bei der Übergabe (return) irgendwo hinterlassen?
-
Ein delete[] reicht aus.
Mir ist nicht ganz klar woher delete[] weiss wieviele Elemente im Array sind, aber ich wuerde vermuten diese Information befindet sich entweder im Pointer selbst oder an der Speicheradresse auf die der Pointer zeigt, also im Array selbst.
http://www.cplusplus.com/reference/new/operator delete[]/
"operator delete[] can be called explicitly as a regular function, but in C++, delete[] is an operator with a very specific behavior: An expression with the delete[] operator, first calls the appropriate destructors for each element in the array (if these are of a class type), and then calls function operator delete[] (i.e., this function) to release the storage."Im Grunde gilt, alles was mit "new" geholt wird, muss auch wieder mit delete oder delete[] geloescht werden, ansonsten hat man ein Speicherleck.
Bei meinem ersten Beispiel oben mit dem Vector of Pointers habe ich also einen Vector voller Pointer die auf Objekte zeigen die mit "new" geholt wurden.
Wenn der Vector jetzt out of scope geht oder ich vec.clear(); benutze werden alle Objekte die auf dem Stack liegen (die Pointer) ordnungsgemaess geloescht.
Allerdings liegen die Objekte die mit "new" geholt wurden auf dem Heap und dort wird es erst mit dem Befehl "delete" weggeraeumt.
Das bedeutet, dass diese Objekte erst manuell geloescht werden muessen bevor ich den letzten Pointer verliere der auf diese jeweiligen Objekte zeigt.
-
Ruvi schrieb:
Ein delete[] reicht aus.
Mir ist nicht ganz klar woher delete[] weiss wieviele Elemente im Array sind, aber ich wuerde vermuten diese Information befindet sich entweder im Pointer selbst oder an der Speicheradresse auf die der Pointer zeigt, also im Array selbst.
http://www.cplusplus.com/reference/new/operator delete[]/
"operator delete[] can be called explicitly as a regular function, but in C++, delete[] is an operator with a very specific behavior: An expression with the delete[] operator, first calls the appropriate destructors for each element in the array (if these are of a class type), and then calls function operator delete[] (i.e., this function) to release the storage."Danke, es klingt logisch, eventuell ist es im Array selbst hinterlassen.
-
Ruvi schrieb:
Mir ist nicht ganz klar woher delete[] weiss wieviele Elemente im Array sind, aber ich wuerde vermuten diese Information befindet sich entweder im Pointer selbst oder an der Speicheradresse auf die der Pointer zeigt, also im Array selbst.
Der Compiler (g++) reserviert +4 bytes (sizeof(size_t)) vor dem array und speichert dort die Anzahl der Elemente.
#include <iostream> #include <new> class test { int i; // 4 bytes public: test() {std::cout << "ctor" << std::endl;} ~test() {std::cout << "dtor" << std::endl;} void* operator new[](size_t n) { void* result; result = ::operator new[](n); std::cout << "operator new[] size:" << n << " at:" << result << std::endl; return result; } }; int main(int, char**) { test* p = new test[2]; std::cout << "new test[2] at:" << p << std::endl; delete[] p; return 0; }operator new[] size:12 at:0x7e1050 ctor ctor new test[2] at:0x7e1054 dtor dtor- osdt
-
Danke, gut zu wissen.
-
osdt schrieb:
Der Compiler (g++) reserviert +4 bytes (sizeof(size_t)) vor dem array und speichert dort die Anzahl der Elemente.
#include <iostream> #include <new> class test { int i; // 4 bytes public: test() {std::cout << "ctor" << std::endl;} ~test() {std::cout << "dtor" << std::endl;} void* operator new[](size_t n) { void* result; result = ::operator new[](n); std::cout << "operator new[] size:" << n << " at:" << result << std::endl; return result; } }; int main(int, char**) { test* p = new test[2]; std::cout << "new test[2] at:" << p << std::endl; delete[] p; return 0; }Danke für den Hinweis,
ich wollte gerade den Code testen. Mein VC++6 sagt allerdings, dass in Zeile 15 der Befehl new[] kein Element von 'global namespace' ist. Man müsste den Operator nicht aus std entnehmen, sondern global lassen?
-
Dann ist was falsch, denn der Code läuft: http://ideone.com/kCe65G
-
Danke, der Code läuft unter Netbeans einwandfrei ... schient irgendetwas mit meiner heißgeliebten VC6 IDE zu sein http://lug-ottobrunn.de/wiki/Operator_New_in_CPP
-
VC6 ??? Der zugehörige Compiler ist noch nicht mal C++98 konform.
Nimm doch einfach das aktuelle VS Express.
-
Th69 schrieb:
VC6 ??? Der zugehörige Compiler ist noch nicht mal C++98 konform.
Nimm doch einfach das aktuelle VS Express.Ja, werde dann nur noch mit VS2010 Express u. Netbeans arbeiten

Nichtsdestotrotz denke ich, dass der Code, den osdt gepostet hat, keinen wirklichen Beweis liefert, da das Ganze in einer Klasse eingebetet ist und die Klasse kraft ihres Amtes die Auflösung der Zeigerelemente einleitet.
Gibt es eventuell eine andere Möglichkeit hinter die Kulissen zu schauen, vielleicht mit einem Debugger oder so, wo man die Speicherzellen direkt sieht und zum Testen ansprechen könnte? Also, ohne Hilfe von Klassen u. Strukturen.
-
Cpp_Anfaeger schrieb:
Nichtsdestotrotz denke ich, dass der Code, den osdt gepostet hat, keinen wirklichen Beweis liefert, da das Ganze in einer Klasse eingebetet ist ...
Damit hast du Recht. Der Code beweist nur, dass die Anzahl Elemente vom Compiler gespeichert wird (und werden muss) wenn es sich um Datentypen (Klassen) mit destructor handelt. Vor dem Aufruf des eigentlichen operator delete[] muss der Compiler den destructor (~test()) für jedes einzelne Element aufrufen (siehe Ausgabe 2x 'dtor').
Meine Aussage ...Der Compiler (g++) reserviert +4 bytes (sizeof(size_t)) vor dem array und speichert dort die Anzahl der Elemente.
gilt also nur in diesem Fall.
Für native (POD) Datentypen wie char[] oder int[] gilt dieses jedoch nicht.
char* p = new char[10];wird also nur 10 Bytes reservieren.
- osdt
-
Dies ist unabhängig vom Datentyp - denn irgendwie muß die Freigabefunktion (delete[] bzw. free() bzw. betriebssystemabhängige Funktion) ja wissen, wieviel Speicher sie wieder freigeben muß (eine andere Alternative wäre, daß intern eine Mappingtabelle <Speicheradresse, Groesse> benutzt würde).