Programm läuft einfach nicht weiter?



  • Hm, also muss ich new nicht verwenden, wenn ich Speicherplatz für einen neuen Objektinstanzzeiger möchte?

    Ich meine, dass das hier geht, ist mir klar:

    Area a = Area(5);
    

    Aber ich dachte, wenn ich einen Zeiger will, brauche ich new, à la:

    Area* a = new Area(5);
    

    , da ich mir ja Speicherplatz für diesen Zeiger holen muss. Oder soll ich es aufwändiger machen und so umschreiben:

    Area a = Area(5);
    Area* aPtr = &a;
    

    Ich geb mir ja echt schon Mühe, ordentlich umzudenken. Aber natürlich vergleicht man im Kopf immer alles mit Java und wendet bekannte Muster an 🙂

    Dein Stichwort hab ich grad gegoogelt und ziehe mir den englischen Wikipedia-Artikel rein.



  • Für so große Datenmengen könnte man auch den Container std::deque verwenden, je nachdem welche Operationen du auf dem Container wie oft nutzt. Manuelle Speicherverwaltung solltest du wirklich meiden (siehe SeppJ).

    Und welchen Compiler nutzt du? Beim neuen Visual Studio 2010 ist in den Komponenten der Standardbibliothek afaik schon moving implementiert, da geht das Umkopieren um Welten schneller. In einer list<Area> muss er z.B. bei jedem push_back das komplette Objekt umkopieren, inklusive der zwei darin enthaltenen Listen. Das R-Value- und std::move-Konzept erspart das Kopieren praktisch, kommt aber erst mit dem C++0x-Standard, der aber in den neueren Compilern schon teilweise implementiert ist.


  • Mod

    Du verstehst nicht: Die Frage die du dir stellen solltest ist, ob du überhaupt einen Zeiger braucht.



  • SeppJ schrieb:

    Du verstehst nicht: Die Frage die du dir stellen solltest ist, ob du überhaupt einen Zeiger braucht.

    Konzeptionell nicht, aber wenn es einen wichtigen Geschwindigkeitsschub bringt, überwiegt das natürlich.



  • fdfdg schrieb:

    Für so große Datenmengen könnte man auch den Container std::deque verwenden, je nachdem welche Operationen du auf dem Container wie oft nutzt. Manuelle Speicherverwaltung solltest du wirklich meiden (siehe SeppJ).

    Und welchen Compiler nutzt du? Beim neuen Visual Studio 2010 ist in den Komponenten der Standardbibliothek afaik schon moving implementiert, da geht das Umkopieren um Welten schneller. In einer list<Area> muss er z.B. bei jedem push_back das komplette Objekt umkopieren, inklusive der zwei darin enthaltenen Listen. Das R-Value- und std::move-Konzept erspart das Kopieren praktisch, kommt aber erst mit dem C++0x-Standard, der aber in den neueren Compilern schon teilweise implementiert ist.

    Ich benutze derzeit VisualStudio 2005 - ich habe auch 2010 hier, muss aber projektgebunden 2005 verwenden.

    Ich hab mird die anderen Typen mal angesehen. Ich denke, ich bin mit einer Liste gut dabei, da ich ja nie irgendwo innen zugreifen muss. Allerdings weiß ich nicht, wie viel schneller eine deque ist - meinst du, das bringt was? Dann würde ich das mal ausprobieren.

    SeppJ schrieb:

    Du verstehst nicht: Die Frage die du dir stellen solltest ist, ob du überhaupt einen Zeiger braucht.

    Es kann gut sein, dass ich es nicht verstehe. Ich versuche mal zu erklären, wie ich das sehe, dann kommen wir vielleicht auf einen Nenner: Ich habe eine Area mit ~14Mio Elementen. Und eben diese Area an das Ende einer list anzuhängen dauert ewig (er erstellt doch ne Kopie und schmeißt die rauf, oder?). Klar ist es in meinen Augen performanter, mir einfach nur den Pointer zu holen und den raufzuschmeißen (der ist ja auch viel kleiner..).

    Ich habe mir bereits das Referenzwerk von Stroustrup bestellt, das morgen hoffentlich in meinem Briefkasten liegt. Nichtsdestotrotz bin ich euch für eure Erklärungen sehr dankbar.



  • Okay, habe mich über RAII belesen - erscheint mir auch sinnvoll. In Zukunft sollte ich also das Speicherbereitstellen über new unterlassen.

    Wie kann ich dennoch das Kopieren großer Objekte vermeiden? Soll ich immer erst das Objekt erstellen und mir dann mit & den Pointer holen? Oder ließe sich (besonders in diesem Szenrario) vollständig auf Pointer verzichten, ohne allzu krasse Geschwindigkeitseinbußen zu verzeichnen?

    Und verstehe ich jetzt richtig: Wenn ich new nicht verwende, wird der Destruktor auch automatisch aufgerufen, wenn der Scope der Variable verlassen wird. Ich muss also nur dafür sorgen, dass der Destruktor ordentlich ist (Dateien schließen, usw)?



  • Habt ihr die Fragen überlesen? 🙂

    Wäre nett, wenn ihr die Fragen aus den voran gegangenen Posts noch beantworten könnt (leider auch, weil mein Stroustrup immer noch nicht da ist und ich gern Köpfe mit Nägeln machen möchte..).



  • freddixx schrieb:

    Okay, habe mich über RAII belesen - erscheint mir auch sinnvoll. In Zukunft sollte ich also das Speicherbereitstellen über new unterlassen.

    Wie kann ich dennoch das Kopieren großer Objekte vermeiden? Soll ich immer erst das Objekt erstellen und mir dann mit & den Pointer holen? Oder ließe sich (besonders in diesem Szenrario) vollständig auf Pointer verzichten, ohne allzu krasse Geschwindigkeitseinbußen zu verzeichnen?

    Du könntest das Objekt auch direkt in deinem Container anlegen und dann mit Werten füllen.

    Und verstehe ich jetzt richtig: Wenn ich new nicht verwende, wird der Destruktor auch automatisch aufgerufen, wenn der Scope der Variable verlassen wird. Ich muss also nur dafür sorgen, dass der Destruktor ordentlich ist (Dateien schließen, usw)?

    Ja, der Destruktor wird aufgerufen, wenn das Objekt aus dem Scope fällt. Und der ruft auch automatisch die Destruktoren der enthaltenen Elemente auf - so daß du bei vernünftigen Klassen (Stichwort: RAII) auch die Aufräumarbeiten gratis bekommst.
    Wenn deine Klasse Speicher per new angefordert hat oder mit extern verwalteten Handles (z.B. FILE* aus der C <stdio.h>) arbeitet, mußt du die aber freigeben.



  • Und alle Klassen der STD enthalten ordentliche Destruktoren. Da brauchst du dir keine Sorgen zu machen.



  • SeppJ schrieb:

    new und delete brauchst du in C++ so gut wie nie.

    Wird dann nicht alles auf dem Stack angelegt wenn man so konsequent auf new verzichtet? Ich bin schon einige Jahre aus C++ raus und blicke nicht so ganz wie man heutzutage umfangreiche Datenmengen auf den Heap bekommt.



  • Die Alternative zu new ist es ja nicht, alles auf den Stack zu schieben, sondern Container der Standardbibliothek und eigene RAII-Klassen zu nutzen, die die nötigen Freigaben selbständig im Destruktor vornehmen.
    Die fehleranfällige manuelle Speicherverwaltung entfällt somit komplett.



  • Ok damit verlagere ich die new/delete Aufrufe in Konstruktor/Destruktor. Das ist ja überaus sinnvoll.
    Aber damit kann man doch nicht sagen, dass new/delete nicht mehr notwendig sind.

    Oder verwendet man eine Art Factory, in der new/delete nochmals weggekapselt sind? Mit Templates sollte sowas ja möglich sein.

    Gehen wir mal weg von Filehandles und dergleichen.
    Wie würde man, z.B. für ein Spiel, eine große Anzahl an Triangles und Vertices mit Hilfe von RAII anlegen und verwalten? Wo findet letztendlich die Speicherallokation statt?



  • Ein stl-Container kann nicht nur eingebaute Typen verwalten. Man könnte sich bspw. eine Klasse Triangle schreiben:

    class Triangle
    {
      float pos[3];
    public:
      Triangle(float x=0, float y=0, float z=0);
      ...
    };
    

    Die Verwendung könnte so aussehen:

    // 1
    Triangle* pTriangles;
    
    // 2
    std::vector<Triangle> triangles;
    
    ..
    // 1
    pTriangles = new Triangle[300];
    Triangle def;
    for(size_t i=0;i<300;++i)
      pTriangles[i] = def;
    // 2
    triangles.resize(300);
    

    Ein ganz entscheidender Vorteil bietet sich, wenn man die Anzahl der Dreiecke erhöhen möchte

    // 1, C++ bietet kein realloc
    Triangle* pCpy = new Triangle[301];
    memcpy(pCpy, pTriangle, 300*sizeof(Triangle);
    delete [] pTriangle;
    pTriangle = pCpy;
    pTriangle[300] = Triangle(30,30,30);
    
    // 2
    triangles.push_back(Triangle(30,30,30));
    

    Wichtig ist vor allem, dass bei der Verwendung eines vectors keine Geschwindigkeitsnachteile entstehen. Auch bisherige Schnittstellen kann man problemlos bedienen

    void f(float* data, size_t size);
    
    // 1
    f(pTriangles, 300);
    // 2
    f(&triangles[0], triangles.size());
    

    Wenn stattdessen Zeiger verwendet werden sollen (bspw. um Polymorphie zu betreiben), kann auch ein Pointercontainer verwendet werden, der nicht nur den eigenen Speicherbereich freigibt, sondern darüberhinaus jedes einzelne Element zerstört.



  • Darauf wollte ich nicht hinaus.

    Du hantierst wieder mit new und delete.
    Meine frage war, wie man sich dessen entledigt.



  • Ich habe doch immer die Möglichkeit mit einem nackten Zeiger und darunter jeweils die Alternative mit einem vector gezeigt. Ich dachte, dies wäre ersichtlich.



  • yahendrik schrieb:

    Ich dachte, dies wäre ersichtlich.

    Ist es auch.

    Nutze Container und RAII erledigt den Rest.



  • Ooookay, dann bearbeite ich mal meinen Quelltext. Ich werde iterativ vorgehen und schauen, wann es wieviel langsamer wird. Im Moment habe ich:

    Voxel* v = new Voxel(d1,d2,d3, greyValue);
    vgraph[d1][d2][d3] = v;
    

    Daraus mache ich:

    voxelGraph.vgraph[d1][d2][d3] = &Voxel(d1,d2,d3, greyValue);
    

    Dann wäre erstmal das new weg. Das ist grad nur kosmetisch, weil ich sonst zuviel ändern müsste. Im nächsten Schritt würde ich dann den Typedef ändern:

    typedef vector<Voxel*> voxVec1D;
    typedef vector<voxVec1D> voxVec2D;
    typedef vector<voxVec2D> voxVec3D;
    

    zu:

    typedef vector<Voxel> voxVec1D;
    typedef vector<voxVec1D> voxVec2D;
    typedef vector<voxVec2D> voxVec3D;
    

    Aber dann müsste ich richtig im Code wursteln 🙂 Aber bevor ich das jetzt mache: Im Moment hätte ich ja noch den 3D-Vector mit Adressen - da geht eine "Kopie" auf einen anderen Vector recht schnell, bzw, sich einen Zeiger auf ein Objekt dieses Vectors zu holen.

    Aber wenn ich auf dem Vector nur noch die ganzen Objekte habe, dann würde ja jede Kopie einen Haufen Rechenzeit in Anspruch nehmen.

    Ich glaube, ich muss mir auch Gedanken machen, WO ich im Speicher die Daten zu liegen haben möchte. Im Moment liegen sie ja irgendwo und der Vector hat nur die Referenzen. Aber wahrscheinlich ist es besser, wenn sie ausschließlich im Vector liegen und ich mir dann dort ne Referenz hole.

    *grübel*. C++ ist schon nicht so trivial. Aber ich will's ja richtig machen. Zum Glück hat der Bote vorhin meinen Stroustrup gebracht 😃



  • yahendrik schrieb:

    Ich habe doch immer die Möglichkeit mit einem nackten Zeiger und darunter jeweils die Alternative mit einem vector gezeigt. Ich dachte, dies wäre ersichtlich.

    Hmmm, bei deinem Beispiel mit den Containern liegen die Nutzdaten aber alle auf dem Stack. Oder übersehe ich etwas?



  • yahendrik schrieb:

    Wenn stattdessen Zeiger verwendet werden sollen (bspw. um Polymorphie zu betreiben), kann auch ein Pointercontainer verwendet werden, der nicht nur den eigenen Speicherbereich freigibt, sondern darüberhinaus jedes einzelne Element zerstört.

    Das habe ich übersehen und darauf wollte ich hinaus. Dann hat man die Daten auf dem Heap mit Scope-abhängiger Lebenszeit.

    Bei CodeProject habe ich gerade noch einen interessanten Artikel gefunden:
    http://www.codeproject.com/KB/cpp/RAIIFactory.aspx
    Da wird eine Factory vorgestellt und das dürfte so eine Art Pointercontainer wie Du ihn ansprichst sein.

    Alles andere ist ja nichts neues. Also Container etc...



  • freddixx schrieb:

    Ooookay, dann bearbeite ich mal meinen Quelltext. Ich werde iterativ vorgehen und schauen, wann es wieviel langsamer wird. Im Moment habe ich:

    Voxel* v = new Voxel(d1,d2,d3, greyValue);
    vgraph[d1][d2][d3] = v;
    

    Daraus mache ich:

    voxelGraph.vgraph[d1][d2][d3] = &Voxel(d1,d2,d3, greyValue);
    

    Das ist ein Schritt in die falsche Richtung, wenn es sich überhaupt übersetzen lässt. Voxel(...) erzeugt eine temporäre Variable, die am Ende der Anweisung wieder gelöscht wird - damit speicherst du einen Zeiger auf Datenmüll im Graph.

    typedef vector<Voxel*> voxVec1D;
    typedef vector<voxVec1D> voxVec2D;
    typedef vector<voxVec2D> voxVec3D;
    

    zu:

    typedef vector<Voxel> voxVec1D;
    typedef vector<voxVec1D> voxVec2D;
    typedef vector<voxVec2D> voxVec3D;
    

    Aber dann müsste ich richtig im Code wursteln 🙂 Aber bevor ich das jetzt mache: Im Moment hätte ich ja noch den 3D-Vector mit Adressen - da geht eine "Kopie" auf einen anderen Vector recht schnell, bzw, sich einen Zeiger auf ein Objekt dieses Vectors zu holen.

    Eine Lösung wäre es, dir einen Container zu bauen, in dem alle deine Voxel untergebracht sind. Im weiteren Verlauf des Programms kannst du dann Zeiger in diesen Container verwenden.

    @Minority Report: Die Containerklassen arbeiten intern auch wieder mit new/delete (oder einer alternativen Methode zur Head-Verwaltung, die von std::allocator<> vorgegeben wird), nur siehst du als Anwender davon nichts mehr.


Anmelden zum Antworten