Tiefe Kopie / flache Kopie ...
Ein Wort, das oft Verwirrung stiftet.
Hier möchte ich anhand des kleinsten Beispiels, in dem der Unterschied zwischen den einzelnen Kopiermethoden erkennbar ist, sehen, wie sich das Verhalten des Programms je nach Kopiermethode unterscheidet.
Es ist ein Haftungsausschluss. Wenn Sie nichts dagegen haben, fahren Sie mit dem Kapitel "Was machen Sie?" Weiter.
In diesem Artikel verwenden wir "Shared Passing", um auf die Methoden "Pass-by-Sharing", "Call-by-Sharing", "Shared Passing" und "Reference Passing by Value" zu verweisen.
Der Autor empfiehlt diesen Begriff nicht. Zum leichteren Vergleich mehrsprachiger Spezifikationen haben wir nur die in diesem Artikel verwendeten Namen angegeben.
In diesem Artikel werden die Begriffe "Referenzübergabe" und "Weitergabe über Freigabe" auch zur Beschreibung von "Methoden zur Variablenzuweisung" verwendet.
Normalerweise ist es ein Wort, das verwendet wird, wenn ein Argument an eine Funktion übergeben wird. Da jedoch dasselbe Konzept für die Zuweisung gilt, verwenden wir das Wort so, wie es ist.
(Dies ist meine persönliche Meinung, aber ich bin mir nicht sicher, ob es sinnvoll ist, das Wort "pass XX" nur zu verwenden, wenn Argumente an eine Funktion übergeben werden. Ich denke, es ist in Ordnung, es für die Zuweisung zu verwenden, also werde ich das in diesem Artikel tun. Ich werde.)
Wenn ich suche, erhalte ich viele Artikel mit der Aufschrift "Flache Kopie ist eine Methode, bei der nur eine Referenz oder ein Zeiger kopiert und keine neue Entität erstellt wird".
In diesem Artikel wird der Fall, in dem "eine neue Entität erstellt wird, sich jedoch irgendwo im Inhalt auf dieselbe Entität wie vor dem Kopieren bezieht", als "flache Kopie" bezeichnet.
Englische Version von Wikipedia und Offizielle Python-Dokumentation Aber das ist der Fall.
(Dies ist meine persönliche Meinung, aber zumindest dieser Fall "Erstellen einer neuen Entität, aber Verweisen auf die Kopierquelle" sollte als "flache Kopie" bezeichnet werden, sodass "flache Kopie neu ist". Ich denke, die Erklärung, dass "es keine Entität erstellt", ist falsch. Wenn "ein Objekt, das keine neue Entität erstellt **, auch als" flache Kopie "bezeichnet wird, gibt es keinen Widerspruch, aber ich denke, dass dies auch falsch ist. )
Ersetzen Sie eine "Kopie in gewissem Sinne" der Variablen "a" durch die Variable "b", spielen Sie mit dem "b" und überprüfen Sie dann den Inhalt des "a".
Zu dieser Zeit gab es nicht nur zwei Arten von Kopiermethoden
Vergleichen wir die vier.
Die Bedeutung jedes Begriffs wird zusammen erläutert, wenn der Code unten erläutert wird.
assignment.dart
void main() {
var a = [
["did deep copy"]
];
//Hier ist der Prozess des Ersetzens einer Kopie von a in gewissem Sinne für b
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [
["did pass-by-reference"]
];
print(a[0][0]);
}
Ich wollte nur den Verarbeitungsinhalt vorstellen, und ich wollte nicht abhängig von einer bestimmten Sprache darüber sprechen, also schrieb ich ihn in Dart, das anscheinend von wenigen Leuten verwendet wird. (Kürzlich erhöht ...?)
Der Verarbeitungsinhalt ist
a
eine Liste von Zeichenfolgen zu.b
durch eine andere Zeichenfolge.b
durch eine Liste verschiedener Zeichenfolgen.a
aus.Mal sehen, warum das einen Unterschied macht.
Wenn die Zuweisung "b = a" als Referenz übergeben wird, hat "b" eine "Referenz", die auf "a" zeigt, und jede nachfolgende Verarbeitung auf "b" wirkt sich auch auf "a" aus. Ich werde.
Es ist einfacher, die Bewegung zu verstehen, wenn Sie sich vorstellen, dass "a" den Alias "b" erhält.
In diesem Fall funktioniert der Code folgendermaßen.
assignment.dart
void main() {
var a = [
["did deep copy"]
];
//Übergeben Sie nun a unter Bezugnahme auf b
//Alle folgenden Verarbeitungen entsprechen denen für a
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [
["did pass-by-reference"]
];
print(a[0][0]); // did pass-by-reference
}
Letzte
b = [
["did pass-by-reference"]
];
Der Inhalt von "a" ist eine Liste von Listen mit dieser Zeichenfolge.
Daher wird die Ausgabe "Referenzübergabe" sein.
Diese Zeichenfolge bedeutet übrigens "als Referenz übergeben".
Genau genommen ist Shared Delivery nicht nur ein Begriff, der sich auf das Bestehen bezieht.
In einer gemeinsam genutzten Sprache ist der Inhalt einer Variablen nicht der Wert selbst, sondern ein Verweis auf diesen Wert. (Dies gilt für viele Sprachen wie Java, Python, JavaScript.)
Wenn dann "b = a" gesetzt ist, wird die in "a" gespeicherte "Referenz" kopiert und auch in "b" gespeichert.
Aus diesem Grund wird Shared Passing manchmal als "Passing by Reference" bezeichnet.
In diesem Fall verhält sich der obige Code wie folgt.
assignment.dart
void main() {
//a hat einen Verweis auf dieses Doppelarray
var a = [
["did deep copy"]
];
//Teilen Sie hier a bis b
//Dieser Prozess wirkt sich auf a aus, da a und b Verweise haben, die auf dieselbe Entität verweisen.
b[0][0] = "did shallow copy";
//Dieser Prozess betrifft auch a
b[0] = ["did pass-by-sharing"];
//Hier speichert b eine neue Referenz, die auf die neue Entität verweist, sodass dieser Prozess a nicht beeinflusst.
b = [
["did pass-by-reference"]
];
print(a[0][0]); // did pass-by-sharing
}
Letzte
b = [
["did pass-by-reference"]
];
Hat "b" "[[" hat Pass-by-Reference "]]" "zugewiesen". Dies ist eine Referenz, die auf eine andere Entität als zuvor verweist, sodass dieser Prozess "a" beeinflusst. Nein.
Daher wird die Ausgabe "Pass-by-Sharing" ausgeführt.
Diese Zeichenfolge bedeutet übrigens "geteilt und übergeben".
Wir sprechen weiterhin über Sprachen, in denen der Inhalt einer Variablen nicht der Wert selbst ist, sondern ein Verweis auf diesen Wert. (Java, Python, JavaScript usw.)
In Sprachen, in denen der Inhalt der Variablen der Wert selbst ist (z. B. C ++), gibt es keine flache Kopie. (Ich denke, es kann mit Zeigern reproduziert und als Referenz übergeben werden)
Eine flache Kopie macht, wie der Name schon sagt, eine Kopie.
Wenn der Inhalt "Referenz" ist, kopieren Sie ihn so wie er ist.
Mal sehen, was das im Code bedeutet.
assignment.dart
void main() {
//a hat einen Verweis auf dieses Doppelarray
var a = [
["did deep copy"]
];
//Erstellen Sie nun eine flache Kopie von a und weisen Sie sie b zu
//a und b haben Verweise, die auf verschiedene Entitäten verweisen.
//Beide Entitäten haben jedoch die gleiche "Referenz" im 0. Element, also
//Dieser Prozess betrifft a
b[0][0] = "did shallow copy";
//Das 0. Element von b wird als Referenz umgeschrieben, die auf etwas anderes verweist.
//Dieser Vorgang wirkt sich nicht auf a aus.
b[0] = ["did pass-by-sharing"];
//Hier speichert b eine neue Referenz, die auf die neue Entität verweist, sodass dieser Prozess auch a nicht beeinflusst.
b = [
["did pass-by-reference"]
];
print(a[0][0]); // did shallow copy
}
a
und seine flache Kopie sind tatsächlich unterschiedlich. Wenn Sie "id" in Python oder "Hashcode" in Dart ausgeben und überprüfen, können Sie sehen, dass "a" und "b" auf verschiedene Entitäten verweisen.
Es werden jedoch dieselben Inhalte kopiert.
In diesem Fall wurde eine "Referenz" kopiert, die auf dasselbe verweist.
Wenn Sie also den Inhalt des "Referenzziels des Inhalts" neu schreiben, sind sowohl "a" als auch "b" betroffen.
Es ist das.
b[0][0] = "did shallow copy";
Wenn Sie dagegen den "Inhalt von" b "selbst neu schreiben, hat dies keine Auswirkungen auf" a ".
das ist
b[0] = ["did pass-by-sharing"];
ist. Dieser Vorgang wirkt sich nicht auf "a" aus. Das Ergebnis ist eine "flache Kopie".
Wie eine flache Kopie macht eine tiefe Kopie eine Kopie, aber
Untersuchen Sie den referenzierten Wert im Inhalt und kopieren Sie ihn ebenfalls.
Wenn es sich auch um eine "Referenz" handelt, überprüfen Sie den Wert der Referenz und kopieren Sie sie ebenfalls.
Wiederholen Sie diesen Vorgang, bis Sie alle Referenzen kopiert haben.
a
und b
teilen nichts mehr.
Operationen, die an einem ausgeführt werden, wirken sich überhaupt nicht auf den anderen aus.
assignment.dart
void main() {
//a hat einen Verweis auf dieses Doppelarray
var a = [
["did deep copy"]
];
//Übergeben Sie nun eine tiefe Kopie von a an b. Danach wirkt sich die Operation zu b nicht auf a aus.
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [
["did pass-by-reference"]
];
print(a[0][0]); // did deep copy
}
Da "a" nichts geändert hat, wird der Anfangswert "did deep copy" ausgegeben.
Mal sehen, wie sie in einigen Sprachen tatsächlich zugeordnet sind! !! !!
JavaScript
JavaScript wird bei normaler Zuweisung freigegeben.
assignment.js
a = [["did deep copy"]];
b = a;
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [["did pass-by-reference"]];
console.log(a[0][0]); // did pass-by-sharing
Wenn Sie die Entität nicht freigeben möchten, wenn es sich um ein Array handelt, können Sie mit "Slice" eine Kopie erstellen und diese haben.
assignment.js
a = [["did deep copy"]];
b = a.slice(0, a.length);
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [["did pass-by-reference"]];
console.log(a[0][0]); // did shallow copy
Die Kopie ist in diesem Fall eine flache Kopie. Wenn Sie eine tiefe Kopie erstellen möchten, müssen Sie diese erstellen.
Auch wenn es sich um ein Objekt anstelle eines Arrays handelt, wird es freigegeben, wenn Sie es normal zuweisen. Wenn Sie es kopieren möchten, können Sie wie folgt vorgehen.
assignment.js
a = { x: { y: "did deep copy" } };
b = Object.assign({}, a); //Hier b=Wenn a festgelegt ist, wird es freigegeben
b.x.y = "did shallow copy";
b.x = { y: "did pass-by-sharing" };
b = { x: { y: "did pass-by-reference" } };
console.log(a.x.y); // did shallow copy
Python
Als nächstes kommt Python.
assignment.py
import copy
a = [['did deep copy']]
b = a
b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0]) # did pass-by-sharing
Selbst in Python wird es freigegeben, wenn Sie es normal zuweisen.
Python verfügt über ein Modul namens "copy", mit dem Sie explizit flache oder tiefe Kopien erstellen können.
Klicken Sie hier für die Dokumentation Kopieren --- flache und tiefe Kopiervorgänge
Sie können eine flache Kopie mit copy.copy
erstellen.
assignment.py
import copy
a = [['did deep copy']]
b = copy.copy(a)
b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0]) # did shallow copy
Sie können eine tiefe Kopie mit copy.deepcopy
erstellen.
assignment.py
import copy
a = [['did deep copy']]
b = copy.deepcopy(a)
b[0][0] = 'did shallow copy'
b[0] = ['did pass-by-sharing']
b = [['did pass-by-reference']]
print(a[0][0]) # did deep copy
Objekte können auch in diesem Modul kopiert werden. Es ist bequem.
Dart
assignment.dart
void main() {
var a = [
["did deep copy"]
];
var b = a;
b[0][0] = "did shallow copy";
b[0] = ["did pass-by-sharing"];
b = [
["did pass-by-reference"]
];
print(a[0][0]); // did pass-by-sharing
}
Wenn Dart auch normal zugewiesen wird, wird es geteilt.
C++
C ++ verhält sich ganz anders als die bisherigen Sprachen.
C ++ wird bei normaler Zuweisung nicht freigegeben.
In C ++ ist der Inhalt einer Variablen keine "Referenz", sondern ein "Wert selbst".
Da es so kopiert wird, wie es ist, besteht zu diesem Zeitpunkt keine Beziehung zur Kopierquelle.
Unabhängig davon, wie Sie es kopieren und erstellen, hat dies keine Auswirkungen auf die Kopierquelle.
Es verhält sich genau wie ** Deep Copy **.
assignment.cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<vector<string>> a{vector<string>{"did deep copy"}};
vector<vector<string>> b = a;
b[0][0] = "did shallow copy";
b[0] = vector<string>{"did pass-by-sharing"};
b = vector<vector<string>>{vector<string>{"did pass-by-reference"}};
cout << a[0][0] << endl; // did deep copy
}
Sie können auch als Referenz in C ++ übergeben.
Bei der Übergabe als Referenz wirken sich alle Vorgänge, die durch Kopieren und Erstellen ausgeführt werden, auf die Kopierquelle aus.
assignment.cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<vector<string>> a{vector<string>{"did deep copy"}};
vector<vector<string>> &b = a;
b[0][0] = "did shallow copy";
b[0] = vector<string>{"did pass-by-sharing"};
b = vector<vector<string>>{vector<string>{"did pass-by-reference"}};
cout << a[0][0] << endl; // did pass-by-reference
}
Wie wir bisher gesehen haben, können Sie beim Schreiben dieses Prozesses klarstellen, was zum Zeitpunkt der Zuweisung zugewiesen wird.
Wenn Sie das wissen, sollte es weniger wahrscheinlich sein, dass Sie von unerwartetem Verhalten gestört werden.
Obwohl es sich um einen ähnlichen Artikel handelt, gibt es einen Artikel, der das Verständnis fördert, indem er das Verhalten beim Übergeben eines Arguments an eine Funktion und das Verhalten beim Zuweisen vergleicht. Lesen Sie ihn daher bitte, wenn Sie möchten.
Wenn Sie in diesem Artikel einen Fehler machen, wäre ich Ihnen sehr dankbar, wenn Sie darauf hinweisen könnten! Vielen Dank.