Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Gelöschter Benutzer

unregistriert

1

24.10.2012, 22:42

[C++] Löschen von Heap-Objekten in einer Schleife

Guten Abend,
ich stehe vor folgendem Problem:

Vorweg einmal der Code:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for (map<string, list<string>>::iterator iter = pInfo->serverScripts.begin(); iter != pInfo->serverScripts.end(); ++iter)
{
    string strFilter = iter->first;
    TiXmlElement FilterElement ("Filter");
    FilterElement.SetAttribute("name", strFilter.c_str());
    ServerElement.LinkEndChild(&FilterElement);

    for (list<string>::iterator it = iter->second.begin(); it != iter->second.end(); ++it)
    {
        string strSource = *it;
        TiXmlElement ScriptElement ("script");
        ScriptElement.SetAttribute("src", strSource.c_str());
        FilterElement.LinkEndChild(&ScriptElement);
    }
}
doc.SaveFile(m_strPath.c_str());


Wie man sieht benutze ich TinyXML, um aus einem Mapping eine XML Datei zusammenzusetzen. (Der Code davor sollte irrelevant sein)
Die Methode "LinkEndChild" nimmt nur Zeiger entgegen. Da der Speicher, auf den die Zeiger zeigen, aber noch in der Methode "SaveFile" adressiert werden muss, müsste ich die Objekte auf dem Heap anlegen.
Da ich diesen Speicher aber auch wieder freigeben muss, stellt sich für mich die Frage, wie ich das am besten angehe. Ich könnte die Zeiger in einen Container packen und anschließend löschen, aber: Gibt es keine elegantere Lösung?
Mit Smart-Pointern habe ich mich bisher wenig beschäftigt und bin in diesem Zusammenhang noch nicht ganz durchgestiegen, wie genau sie funktionieren.

Viele Grüße

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

2

24.10.2012, 22:59

Solltest du eindeutig Smartpointer benutzen. Ansonsten könntest du es natürlich über so eine Schleife machen. Aber wie gesagt, Smartpointer sind das Stichwort.
Guck dich mal hier.
Oder hier.
Das hilft dir bestimmt schon mal weiter. Ansonsten kannst du ja konkrete Fragen stellen.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

3

24.10.2012, 23:40

TinyXML 1 hat hierfür InsertEndChild. Die Methode kopiert das übergebene Element intern und übernimmt die Speicherverwaltung für dich, deine temporären Objekte kannst du dann einfach verfallen lassen.

TinyXML 2 hat hierfür Fabrikmethoden in der Dokumentenklasse. Diese erlauben zum Beispiel mittels NewElement die direkte Erzeugung eines neuen Elements im Dokumentenspeicher, um den du dich dann ebenfalls nicht mehr kümmern musst. Dieser Ansatz hat offenkundig den Vorteil, dass weniger kopiert werden muss, weil du die Elemente direkt an der richtigen Stelle im Speicher zusammensetzen kannst.

Auch wenn es hier vielleicht nichts zur Sache tut, solltest du dich mit unique_ptr, shared_ptr und weak_ptr vertraut machen. Schorschs Links sind leider beide unbrauchbar veraltet, aber vielleicht kennt ja ein anderer Mitleser hier eine gute und aktuelle Einführung in moderne Ressourcenverwaltung.
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

4

24.10.2012, 23:41

Erklär mal genauer, wieso du die Objekte am Heap anlegen musst, das wird mir aus obiger Erklärung nicht ganz klar. Falls sich das echt nicht vermeiden lässt, solltest du Smartpointer benutzen, std::unique_ptr bietet sich vermutlich an...

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

5

25.10.2012, 00:17

Die sind wirklich unbrauchbar? Gut ich arbeite normal nicht mit C++ aber dass es da so viel neues gibt hätte ich nicht gedacht;) Dann sorry dafür.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Gelöschter Benutzer

unregistriert

6

26.10.2012, 13:25

Erstmal danke für die vielen Antworten.

Zitat von »CodingCat«

TinyXML 1 hat hierfür InsertEndChild. Die Methode kopiert das übergebene Element intern und übernimmt die Speicherverwaltung für dich, deine temporären Objekte kannst du dann einfach verfallen lassen.

Wenn ich InsertEndChild benutze, wird nur das erste Element in die XML-Datei geschrieben und alle anderen Unterelemente ignoriert. Laut Dokumentation fügt InsertEndChild auch ein Kindelement nach dem Element, welches übergiben wird, ein. An der Stelle habe ich mich vielleicht etwas unklar ausgedrückt.
Meine XML-Datei soll bspw. folgendermaßen aussehen (wobei gesagt sei, dass die Tags "Project", "Server" und "Client" vor der Schleife erstellt werden):

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Project name="SampleProject" description="This is a sample project" author="Me" version="1.0">
    <Server>
        <Filter name="Economy">
            <script src="server1.lua" />
            <script src="server2.lua" />
        </Filter>
    </Server>
    <Client>
        <Filter name="Economy">
            <script src="client1.lua" />
            <script src="client2.lua" />
        </Filter>
    </Client>
</Project>

Wenn ich LinkEndChild benutze, erhalte ich dieses Ergebnis auch.

Zitat von »dot«

Erklär mal genauer, wieso du die Objekte am Heap anlegen musst, das wird mir aus obiger Erklärung nicht ganz klar.

Wenn ich die Elemente in der Schleife auf dem Stack anlege, werden sie nach Durchlaufen des "Schleifendurchgangs" wieder gelöscht. Da ich LinkEndChild aber einen Zeiger übergeben muss, auf dessen Speicherbereich SaveFile später zugreift und der Zeiger dann verwaist ist, stürzt das Programm ab.

Zitat von »Schorsch«

Ansonsten kannst du ja konkrete Fragen stellen.

Das werde ich jetzt auch tun ;)
Undzwar habe ich den Smartpointer jetzt so definiert:

C-/C++-Quelltext

1
unique_ptr<TiXmlElement> pFilterElement(new TiXmlElement("Filter"));

Wie aber lege ich jetzt den "Besitzer" fest?

CodingCat

1x Contest-Sieger

Beiträge: 420

Beruf: Student (KIT)

  • Private Nachricht senden

7

26.10.2012, 13:42

Zitat

Zitat von »CodingCat«

TinyXML 1 hat hierfür InsertEndChild. Die Methode kopiert das übergebene Element intern und übernimmt die Speicherverwaltung für dich, deine temporären Objekte kannst du dann einfach verfallen lassen.

Wenn ich InsertEndChild benutze, wird nur das erste Element in die XML-Datei geschrieben und alle anderen Unterelemente ignoriert. Laut Dokumentation fügt InsertEndChild auch ein Kindelement nach dem Element, welches übergiben wird, ein. An der Stelle habe ich mich vielleicht etwas unklar ausgedrückt.
Meine XML-Datei soll bspw. folgendermaßen aussehen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Project name="SampleProject" description="This is a sample project" author="Me" version="1.0">
    <Server>
        <Filter name="Economy">
            <script src="server1.lua" />
            <script src="server2.lua" />
        </Filter>
    </Server>
    <Client>
        <Filter name="Economy">
            <script src="client1.lua" />
            <script src="client2.lua" />
        </Filter>
    </Client>
</Project>

Wenn ich LinkEndChild benutze, erhalte ich dieses Ergebnis auch.

Dieses Ergebnis erhältst du mit InsertEndChild genauso. Du darfst die Kinder nach dem Einfügen des Elternelements natürlich nicht einfach weiter dem temporären Element hinzufügen, wie du es im Moment tust. Denn das temporäre Elternelement wird am Ende einfach zerstört, wie du bereits in deinem Eingangspost richtig erkannt hast.

Korrekt wäre:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
for (map<string, list<string>>::iterator iter = pInfo->serverScripts.begin(); iter != pInfo->serverScripts.end(); ++iter)
{
    TiXmlNode *FilterNode;
    {
       TiXmlElement FilterElement ("Filter");
       FilterElement.SetAttribute("name", iter->first.c_str());
       // Zeiger auf das _eingefügte_ Element speichern und benutzen,
       // NICHT das temporäre Element mit Kindern befüllen
       FilterNode = ServerElement.InsertEndChild(FilterElement);
    }

    for (list<string>::iterator it = iter->second.begin(); it != iter->second.end(); ++it)
    {
        string strSource = *it;
        TiXmlElement ScriptElement ("script");
        ScriptElement.SetAttribute("src", strSource.c_str());
        FilterNode->InsertEndChild(ScriptElement);
    }
}
doc.SaveFile(m_strPath.c_str());


Oder alternativ:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (map<string, list<string>>::iterator iter = pInfo->serverScripts.begin(); iter != pInfo->serverScripts.end(); ++iter)
{
    TiXmlElement FilterElement ("Filter");
    FilterElement.SetAttribute("name", iter->first.c_str());

    for (list<string>::iterator it = iter->second.begin(); it != iter->second.end(); ++it)
    {
        string strSource = *it;
        TiXmlElement ScriptElement ("script");
        ScriptElement.SetAttribute("src", strSource.c_str());
        // Temporäres Element mit Kindern befüllen
        FilterElement.InsertEndChild(ScriptElement);
    }

    // Temporäres Element erst NACH dem Befüllen mit Kindern in das Dokument einfügen
    ServerElement.InsertEndChild(FilterElement);
}
doc.SaveFile(m_strPath.c_str());


Zitat

Zitat von »Schorsch«

Ansonsten kannst du ja konkrete Fragen stellen.

Das werde ich jetzt auch tun ;)
Undzwar habe ich den Smartpointer jetzt so definiert:

C-/C++-Quelltext

1
unique_ptr<TiXmlElement> pFilterElement(new TiXmlElement("Filter"));

Wie aber lege ich jetzt den "Besitzer" fest?

Der unique_ptr IST der Besitzer. Deshalb hilft er dir in diesem Fall auch nicht wirklich weiter. Prinzipiell funktionieren unique_ptr-Objekte so, dass sie mit ihrer eigenen Zerstörung auch automatisch das durch den Zeiger referenzierte TiXmlElement mit zerstören. Um die Lebenszeit von Objekten über einen Geltungsbereich hinaus zu verlängern, erlauben unique_ptr-Objekte das Verschieben der durch sie verwalteten Objekte:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
unique_ptr<TiXmlElement> longLiveThefilterElement;
{
   // Filter-Element wird bei Verlassen des Geltungsbereichs automatisch zerstört
   unique_ptr<TiXmlElement> filterElement(new TiXmlElement("Filter"));
   // Durch Verschieben in ein anderes unique_ptr-Objekt lässt sich die automatische Zerstörung
   // auf einen anderen Zeitpunkt verschieben, nämlich den des neuen unique_ptr-Objekts.
   longLiveThefilterElement = std::move(filterElement);
   // Achtung, der filterElement-Zeiger in diesem Geltungsbereich ist nach dem Verschieben ungültig

   // Jetzt lebt das Filter-Element so lange, bis longLiveThefilterElement zerstört wird,
   // insbesondere wird es nicht mehr bei Verlassen dieses Geltungsbereichs zerstört
}
// Filter-Element lebt in longLiveThefilterElement weiter ...


In diesem Beispiel wechselt das Filter-Element also durch das std::move den Besitzer. unique_ptrs sind enorm praktisch, weil sie durch den Besitzerwechsel auch das Verwalten vieler Objekte in Containern erlauben:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
vector<unique_ptr<TiXmlElement>> manyFilterElements;
{
   // Filter-Element konstruieren, initialisieren ...
   unique_ptr<TiXmlElement> filterElement(new TiXmlElement("Filter"));
   // Filter-Element in den Container verschieben (danach ist der vector der "Besitzer")
   manyFilterElements.push_back( std::move(filterElement) );
   // Achtung, der filterElement-Zeiger in diesem Geltungsbereich ist nach dem Verschieben ungültig
}
// Filter-Element lebt in manyFilterElements weiter ...
alphanew.net (last updated 2011-06-26) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »CodingCat« (26.10.2012, 13:51)


Gelöschter Benutzer

unregistriert

8

26.10.2012, 14:13

Okay, klappt alles, danke!

Werbeanzeige