Programm läuft einfach nicht weiter?



  • 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