Array.Copy



  • Kurze, korrekte, aber wenig hilfreiche Antwort: Das Kopieren von Nicht-POD-Typen erzeugt undefiniertes Verhalten. Der Compiler darf dann Code erzeugen, der deinen Computer explodieren lässt.

    Längere Antwort: Wenn Objekte deiner Klasse Verweise enthalten (Zeiger oder Referenzen), dann wird bei einer flachen Kopie mit memcpy der Verweis einfach mitkopiert, nicht aber das, worauf er verweist. Dadurch geht das, worauf das überschriebene Objekt verweist, verloren, und zwei Objekte verweisen nachher auf das selbe. Das hat dann (üblicherweise; garantiert ist so was dann nicht mehr) zur Folge, dass (jetzt im Fall zweier std::strings) Änderungen am Inhalt des einen Strings auch den anderen betreffen (können), dass Änderungen am einen String, die diesen dazu veranlassen, seinen Inhalt im Speicher zu verlegen, den anderen ins Nirvana zeigen lassen, und dass bei der Zerstörung mindestens eines der beiden Objekte der Heap zerschossen wird.



  • Hui, ich hab' auf weiteres nicht geachtet! Wusst' nicht, dass er ein Template hat, hab ich glatt übersehen. ⚠ Natürlich ist das Doofsinn, was da passiert.
    ➡

    std::copy( list.items, list.items + size, items );
    


  • seldon schrieb:

    Kurze, korrekte, aber wenig hilfreiche Antwort: Das Kopieren von Nicht-POD-Typen erzeugt undefiniertes Verhalten.

    TriviallyCopyable ist das Wort, das du suchst. Man darf auch manche Nicht-PODs mit memcpy kopieren.

    Sone schrieb:

    Hui, ich hab' auf weiteres nicht geachtet! Wusst' nicht, dass er ein Template hat, hab ich glatt übersehen. ⚠ Natürlich ist das Doofsinn, was da passiert.
    ➡

    std::copy( list.items, list.items + size, items );
    

    Inwiefern ist das weniger Doofsinn? Damit hast du knallhart ein Memory-Leak in die Liste von Enumerator eingebaut.
    Werkzeug der Wahl wäre Speicher allozieren ohne initialisieren und dann mit uninitialized_copy drüberrauschen.
    Die Standardbibliothek kann zwar nicht zaubern, aber ein korrektes uninitialized_copy nicht TriviallyImplementable.


  • Mod

    seldon schrieb:

    Kurze, korrekte, aber wenig hilfreiche Antwort: Das Kopieren von Nicht-POD-Typen erzeugt undefiniertes Verhalten. Der Compiler darf dann Code erzeugen, der deinen Computer explodieren lässt.

    Etwas korrekter: das Kopieren (per memcpy & co.) an sich ist unproblematisch, erzeugt aber keine Objekte des Ursprungstyps. Ein Zugriff auf diese Kopien mit dem Ursprungstyp wird also für gewöhnlich die Aliasingregeln (3.10/10) oder die Regeln für den Umgang mit nicht initialisiertem Speicher (3.8) verletzen.



  • experimentator schrieb:

    seldon schrieb:

    Kurze, korrekte, aber wenig hilfreiche Antwort: Das Kopieren von Nicht-POD-Typen erzeugt undefiniertes Verhalten.

    TriviallyCopyable ist das Wort, das du suchst. Man darf auch manche Nicht-PODs mit memcpy kopieren.

    Sone schrieb:

    Hui, ich hab' auf weiteres nicht geachtet! Wusst' nicht, dass er ein Template hat, hab ich glatt übersehen. ⚠ Natürlich ist das Doofsinn, was da passiert.
    ➡

    std::copy( list.items, list.items + size, items );
    

    Inwiefern ist das weniger Doofsinn? Damit hast du knallhart ein Memory-Leak in die Liste von Enumerator eingebaut.

    Ähm, nein? Wie zum Teufel kommst du darauf?

    template <class T> 
    List<T>::List(const List& list) : 
        capacity(list.capacity), 
        items(new T[list.capacity]), 
        size(list.size) 
    {
         std::copy( list.items, list.items + size, items ); 
    }
    


  • Achso, darauf hätte ich auch selber kommen können. Danke euch für die Antworten.



  • Sone schrieb:

    experimentator schrieb:

    seldon schrieb:

    Kurze, korrekte, aber wenig hilfreiche Antwort: Das Kopieren von Nicht-POD-Typen erzeugt undefiniertes Verhalten.

    TriviallyCopyable ist das Wort, das du suchst. Man darf auch manche Nicht-PODs mit memcpy kopieren.

    Sone schrieb:

    Hui, ich hab' auf weiteres nicht geachtet! Wusst' nicht, dass er ein Template hat, hab ich glatt übersehen. ⚠ Natürlich ist das Doofsinn, was da passiert.
    ➡

    std::copy( list.items, list.items + size, items );
    

    Inwiefern ist das weniger Doofsinn? Damit hast du knallhart ein Memory-Leak in die Liste von Enumerator eingebaut.

    Ähm, nein? Wie zum Teufel kommst du darauf?

    template <class T> 
    List<T>::List(const List& list) : 
        capacity(list.capacity), 
        items(new T[list.capacity]), 
        size(list.size) 
    {
         std::copy( list.items, list.items + size, items ); 
    }
    

    Dann erkläre mir doch bitte was mit

    int instances;
    struct doofsinn {
      doofsinn() { ++instances; }
     ~doofsinn() { --instances; }
      doofsinn& operator=(doofsinn const&) {
        static int doofsinn_counter = 0;
        if (++doofsinn_counter % 5 == 0)
          throw "Sone";
      }
    };
    

    passiert (http://ideone.com/Y5C1KA).



  • Ich wollte doch nicht gleich zu exception-safety übergehen. Aber damit du zufrieden bist:
    http://ideone.com/dWDpzF

    template <class T> 
    List<T>::List(const List& list) : 
        capacity(list.capacity), 
        items(new T[list.capacity]), 
        size(list.size)
    { 
        try
        {
            std::copy( list.items, list.items + size, items ); 
        }
        catch(...)
        {
            delete [] items;
            throw;
        }
    }
    


  • Enumerator schrieb:

    Sorry aber der Styling-Code der Standard-Library ist ein absolutes Grauen, weshalb ich mich gerne an C# halte.

    So eine Aussage kommt meist von Leuten, die sich noch nicht genauer damit befasst haben 😉

    Schau dir die STL mal im Detail an. Durch die Trennung von Containern, Algorithmen und Iteratoren ist enorme Flexibilität möglich. Zum Beispiel basiert alles auf Templates und hat dadurch nicht die Nachteile tiefer Vererbungshierarchien. Wenn dich das interessiert, könntest du auch mal einen Blick auf "Notes on Programming" von Alexander Stepanov werfen -- das ist die Person, welche die STL entworfen hat -- wohl bis heute einer der bedeutendsten Beiträge zu C++.

    Davon abgesehen ist es unsinnig, für produktive Programmierung das Rad neu zu erfinden. Du wirst niemals an die Qualität heutiger STL-Implementierung herankommen. Das betrifft Kriterien wie Performance, Fehlerfreiheit, Debug-Unterstützung, API-Design, Integration in den Rest der Standardbibliothek. Als Übung kann man sich sowas antun, aber sobald du dies später wirklich brauchst, benutze bitte existierende Mittel. Und löse dich von C#, C++ ist eine komplett andere Sprache mit anderen Paradigmen, in der Probleme anders angegangen werden.

    Es gibt nichts Schlimmeres als wenn Leute dauernd Standardmechanismen selbst schreiben, "weil ihre Implementierung ja so viel schneller und besser ist". Diese Unsitte sieht man in vielen in die Jahre gekommenen C++-Biblitoheken. Die Konsequenz ist, dass man bei deren Benutzung ständig Inkompatibilitäten hat und gezwungen ist, selbst ihre Datenstrukturen zu verwenden, die meist schlechter entwickelt sind als Standardmechanismen.



  • Nexus schrieb:

    Diese Unsitte sieht man in vielen in die Jahre gekommenen C++-Biblitoheken.

    Gehört Qt dazu? Ich meine, ok, die haben schon das ein oder andere angepasst, aber QList, QString, QMap, QSet...



  • Nö. Qt ist eine in die Jahre gekommene Bibliothek einer C++-ähnlichen Sprache. :p


Anmelden zum Antworten