Antworten:
Dinge, die Sie damit machen können make
, können Sie nicht anders machen:
Es ist etwas schwieriger zu rechtfertigen new
. Die Hauptsache, die es einfacher macht, ist das Erstellen von Zeigern auf nicht zusammengesetzte Typen. Die beiden folgenden Funktionen sind äquivalent. Man ist nur etwas prägnanter:
func newInt1() *int { return new(int) }
func newInt2() *int {
var i int
return &i
}
m := map[string]int{}
statt m := make(map[string]int)
? Es ist nicht erforderlich, die Größe vorab zuzuweisen.
Go bietet mehrere Möglichkeiten zur Speicherzuweisung und Wertinitialisierung:
&T{...}
, &someLocalVar
, new
,make
Die Zuordnung kann auch beim Erstellen von zusammengesetzten Literalen erfolgen.
new
kann verwendet werden, um Werte wie Ganzzahlen zuzuweisen, &int
ist illegal:
new(Point)
&Point{} // OK
&Point{2, 3} // Combines allocation and initialization
new(int)
&int // Illegal
// Works, but it is less convenient to write than new(int)
var i int
&i
Der Unterschied zwischen new
und make
lässt sich anhand des folgenden Beispiels erkennen:
p := new(chan int) // p has type: *chan int
c := make(chan int) // c has type: chan int
Angenommen, Go hat kein new
und make
, aber es hat die eingebaute Funktion NEW
. Dann würde der Beispielcode folgendermaßen aussehen:
p := NEW(*chan int) // * is mandatory
c := NEW(chan int)
Das *
wäre obligatorisch , also:
new(int) --> NEW(*int)
new(Point) --> NEW(*Point)
new(chan int) --> NEW(*chan int)
make([]int, 10) --> NEW([]int, 10)
new(Point) // Illegal
new(int) // Illegal
Ja, Zusammenführung new
und make
in eine einzige integrierte Funktion ist möglich. Es ist jedoch wahrscheinlich, dass eine einzelne integrierte Funktion bei neuen Go-Programmierern zu mehr Verwirrung führen würde als zwei integrierte Funktionen.
Alle oben genannten Punkte in Anbetracht, scheint es besser geeignet für new
und make
getrennt zu bleiben.
int
das erstellt wird.
make(Point)
und make(int)
in diesen letzten 2 Zeilen?
make
Die Funktion weist nur ein Objekt vom Typ Slice, Map oder Chan zu und initialisiert es. Wie new
das erste Argument ist ein Typ. Es kann aber auch ein zweites Argument sein, die Größe. Im Gegensatz zu new ist der Rückgabetyp von make derselbe wie der Typ seines Arguments und kein Zeiger darauf. Und der zugewiesene Wert wird initialisiert (nicht wie in neu auf Null gesetzt). Der Grund ist, dass Slice, Map und Chan Datenstrukturen sind. Sie müssen initialisiert werden, sonst können sie nicht verwendet werden. Dies ist der Grund, warum new () und make () unterschiedlich sein müssen.
Die folgenden Beispiele aus Effective Go machen dies sehr deutlich:
p *[]int = new([]int) // *p = nil, which makes p useless
v []int = make([]int, 100) // creates v structure that has pointer to an array, length field, and capacity field. So, v is immediately usable
new([]int)
weist es nur Speicher für [] int zu, initialisiert es jedoch nicht, sodass es einfach zurückkehrt nil
. nicht der Zeiger auf den Speicher, weil er unbrauchbar ist. make([]int)
ordnet und initialisiert, damit es verwendet werden kann, und gibt dann seine Adresse zurück.
new(T)
- Ordnet Speicher zu und setzt ihn auf den Wert Null für Typ T ..
..das gilt 0
für int , ""
für Zeichenfolge und nil
für referenzierte Typen ( Slice , Map , Chan ).
Beachten Sie, dass referenzierte Typen nur Zeiger auf einige zugrunde liegende Datenstrukturen sind , die nicht durch new(T)
Beispiel erstellt werden : Im Falle eines Slice wird das zugrunde liegende Array nicht erstellt, sodass new([]int)
ein Zeiger auf nichts zurückgegeben wird
make(T)
- Ordnet Speicher für referenzierte Datentypen ( Slice , Map , Chan ) zu und initialisiert die zugrunde liegenden Datenstrukturen
Beispiel: Im Falle eines Slice wird das zugrunde liegende Array mit der angegebenen Länge und Kapazität erstellt.
Beachten Sie, dass ein Array im Gegensatz zu C ein primitiver Typ in Go!
Davon abgesehen:
make(T)
verhält sich wie eine Composite-Literal-Syntax
new(T)
verhält sich wie var
(wenn die Variable nicht initialisiert ist)
func main() {
fmt.Println("-- MAKE --")
a := make([]int, 0)
aPtr := &a
fmt.Println("pointer == nil :", *aPtr == nil)
fmt.Printf("pointer value: %p\n\n", *aPtr)
fmt.Println("-- COMPOSITE LITERAL --")
b := []int{}
bPtr := &b
fmt.Println("pointer == nil :", *bPtr == nil)
fmt.Printf("pointer value: %p\n\n", *bPtr)
fmt.Println("-- NEW --")
cPtr := new([]int)
fmt.Println("pointer == nil :", *cPtr == nil)
fmt.Printf("pointer value: %p\n\n", *cPtr)
fmt.Println("-- VAR (not initialized) --")
var d []int
dPtr := &d
fmt.Println("pointer == nil :", *dPtr == nil)
fmt.Printf("pointer value: %p\n", *dPtr)
}
Führen Sie das Programm aus
-- MAKE --
pointer == nil : false
pointer value: 0x118eff0 # address to underlying array
-- COMPOSITE LITERAL --
pointer == nil : false
pointer value: 0x118eff0 # address to underlying array
-- NEW --
pointer == nil : true
pointer value: 0x0
-- VAR (not initialized) --
pointer == nil : true
pointer value: 0x0
Weiterführende Literatur:
https://golang.org/doc/effective_go.html#allocation_new
https://golang.org/doc/effective_go.html#allocation_make
Sie müssen make()
Kanäle und Karten (und Slices, aber diese können auch aus Arrays erstellt werden) erstellen. Es gibt keine alternative Möglichkeit, diese zu erstellen, sodass Sie sie nicht make()
aus Ihrem Lexikon entfernen können.
Was new()
, weiß ich nicht , aus welchem Grunde auch freihändig , warum Sie benötigen es , wenn Sie struct Syntax verwenden. Es hat jedoch eine eindeutige semantische Bedeutung: "Erstellen und Zurückgeben einer Struktur mit allen Feldern, die auf ihren Nullwert initialisiert wurden", was nützlich sein kann.
Abgesehen von allem, was in Effective Go erklärt wird , besteht der Hauptunterschied zwischen new(T)
und &T{}
darin, dass letzteres explizit eine Heap-Zuweisung durchführt. Es ist jedoch zu beachten, dass dies implementierungsabhängig ist und sich daher ändern kann.
Ein Vergleich make
mit new
macht wenig Sinn, da beide völlig unterschiedliche Funktionen ausführen. Dies wird jedoch im verlinkten Artikel ausführlich erläutert.
&T{}
explizit eine Heap-Zuweisung durchführt, ist AFAIK und basiert nicht auf irgendetwas in den Spezifikationen. Eigentlich glaube ich, dass die Escape-Analyse solche * T, wann immer möglich, genau so wie bei auf Stapel hält new(T)
.
new (T): Gibt einen Zeiger auf Typ T zurück, einen Wert vom Typ * T, weist den Speicher zu und setzt ihn auf Null. new (T) entspricht & T {} .
make (T): Gibt einen initialisierten Wert vom Typ T zurück . Es ordnet den Speicher zu und initialisiert ihn. Es wird für Slices, Karten und Kanäle verwendet.