Array.Copy
-
Hi, ich experimentiere gerade ein wenig mit C++ und habe mir folgende Copy-Funktion geschrieben:
void Array::Copy(const void* sourceArray, void* destinationArray, int length) { char* dest = static_cast<char*>(destinationArray); const char* src = static_cast<const char*>(sourceArray); while (length--) { *dest++ = *src++; } }Nun verwende ich diese in meiner List-Klasse folgendermaßen:
template <class T> List<T>::List(const List& list) : capacity(list.capacity), items(new T[list.capacity]), size(list.size) { Array::Copy(list.items, items, capacity * (sizeof(items) / sizeof(items[0]))); }Ist der Aufruf von Array::Copy so nun richtig? Bei der sizeof Geschichte bin ich mir unsicher.
Und wäre ein Aufruf von
Array::Copy(list.items, items, size * (sizeof(items) / sizeof(items[0])));auch legitim? Die reservierten Stellen mitzukopieren ist ja nicht gerade sinnvoll.
-
Was soll das? Wieso experimentierst du nicht gleich mit echtem C++ und verwendest die bereits vorhandenen Mittel? Was du machst, ist C#/Java in C++, das will niemand.
-
experimentator schrieb:
Was soll das? Wieso experimentierst du nicht gleich mit echtem C++ und verwendest die bereits vorhandenen Mittel?
Aus Lernzwecken.
Die reservierten Stellen mitzukopieren ist ja nicht gerade sinnvoll.
Nein, absolut nicht.
Array::Copy(list.items, items, size * (sizeof(items) / sizeof(items[0])));Ich nehme mal an,
itemsist ein Zeiger.
Dann einfachArray::Copy(list.items, items, size * sizeof(*items));Das Äquivalent zu deiner Copy-Funktion wäre übrigens
std::memcpy.
-
Die Notation:
sizeof(items) / sizeof(items[0])Wird nur für echte Arrays verwendet.
Der Trick ist einfach:When applied to an array, the result is the total number of bytes in the array.
This implies that the size of an array of n elements is n times the size of an element.Dividiert man also das Ergebnis mit der Größe eines Array-Elements, hat man die Länge.
Das Funktioniert aber nur, falls, in diesem Beispielitems, auch wirklich als ein Array deklariert wurde.
-
experimentator schrieb:
Was soll das? Wieso experimentierst du nicht gleich mit echtem C++ und verwendest die bereits vorhandenen Mittel? Was du machst, ist C#/Java in C++, das will niemand.
Was erinnert dich hier bitte an C#/Java?
-
Ethon schrieb:
Was erinnert dich hier bitte an C#/Java?
Die
Array-Klasse, um array-basierte Funktionen zu gruppieren.
Der NameList, um dynamische Arrays kennzuzeichnen.
-
Sone schrieb:
Ich nehme mal an,
itemsist ein Zeiger.
Dann einfachArray::Copy(list.items, items, size * sizeof(*items));Jupp, ist ein Zeiger. Das hatte ich ursprünglich so verwendet aber sporadisch Programmabstürze mit dieser Funktion. Nehme ich stattdessen
for (int i = 0; i < size; ++i) { items[i] = list.items[i]; }im Copy-Constructor funktioniert alles prima. Sehr merkwürdig.
-
Der äußere Schein trügt
Mich erinnert das ganze viel eher an schlechtes C.
-
Nexus schrieb:
Ethon schrieb:
Was erinnert dich hier bitte an C#/Java?
Die
Array-Klasse, um array-basierte Funktionen zu gruppieren.
Der NameList, um dynamische Arrays kennzuzeichnen.Und deswegen ist es kein C++ oder was ;). Sorry aber der Styling-Code der Standard-Library ist ein absolutes Grauen, weshalb ich mich gerne an C# halte.
-
Nicht alle Typen sind einfach mit memcpy (oder Äquivalenten) kopierbar. Wenn jetzt jemand Liststd::string benutzt, fliegt dir das im hohen Bogen auseinander. Die Zuweisungsschleife benutzt ggf. den Zuweisungsoperator, der das richtige machen sollte. (Wenn er es nicht tut, ist das ein Fehler im Werttyp, nicht in der Liste)
So oder so solltest du dir aber mal std::vector anschauen. Den scheinst du da gerade nachbauen zu versuchen.
-
Mechanics schrieb:
Der äußere Schein trügt
Mich erinnert das ganze viel eher an schlechtes C.Ach bitte dann schau dir mal die Implementiering von std::vector an. Das funktioniert unter der Haupe genau so. Weis gar nicht was ihr Standard-Library Fanatiker euch immer einbildet. Die Standard-Library kann - jetzt bitte nicht traurig sein - leider auch nicht zaubern.
-
Enumerator schrieb:
Sorry aber der Styling-Code der Standard-Library ist ein absolutes Grauen, weshalb ich mich gerne an C# halte.
Wenn du versuchst, C#s Standardbibliothek in C++ nachzubauen, wirst du dich auf große Schmerzen einstellen müssen. Eine ganze Reihe Dinge funktionieren in C++ ganz anders -- wenn du von einer gemanagten Sprache kommst, wird dir insbesondere die Umstellung auf Wertesemantik Probleme bereiten, was sich schon an der Zeile
items(new T[list.capacity]),in der ctor-init-list deines Kopierkonstruktors deutlich zeigt.
-
Enumerator schrieb:
Ach bitte dann schau dir mal die Implementiering von std::vector an. Das funktioniert unter der Haupe genau so.
Nope, hast du dir sie einmal angesehen? Bei denen ist copy ein Template und nicht einfach ein memcopy.
-
seldon schrieb:
Nicht alle Typen sind einfach mit memcpy (oder Äquivalenten) kopierbar. Wenn jetzt jemand Liststd::string benutzt, fliegt dir das im hohen Bogen auseinander. Die Zuweisungsschleife benutzt ggf. den Zuweisungsoperator, der das richtige machen sollte. (Wenn er es nicht tut, ist das ein Fehler im Werttyp, nicht in der Liste)
So oder so solltest du dir aber mal std::vector anschauen. Den scheinst du da gerade nachbauen zu versuchen.
Warum fliegt das auseinander? Kannst du das ein wenig genauer erklären?
-
Überlege mal, was passiert, wenn du folgende Klasse Byte für Byte kopierst:
template <typename T> class autp_ptr { T *p; public: auto_ptr(T *p) : p(p) {} auto_ptr(auto_ptr& o) : p(o.p) { o.p = 0; } ~auto_ptr() { delete p; } };
-
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.
-
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.