Réponses:
Les choses que vous pouvez faire avec make
cela que vous ne pouvez pas faire autrement:
C'est un peu plus difficile à justifier new
. La principale chose que cela facilite est la création de pointeurs vers des types non composites. Les deux fonctions ci-dessous sont équivalentes. On est juste un peu plus concis:
func newInt1() *int { return new(int) }
func newInt2() *int {
var i int
return &i
}
m := map[string]int{}
lieu de m := make(map[string]int)
? pas besoin de préallouer la taille également.
Go propose plusieurs méthodes d'allocation de mémoire et d'initialisation de valeur:
&T{...}
, &someLocalVar
, new
,make
L'allocation peut également se produire lors de la création de littéraux composites.
new
peut être utilisé pour allouer des valeurs telles que des entiers, &int
est illégal:
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
La différence entre new
et make
peut être vue en regardant l'exemple suivant:
p := new(chan int) // p has type: *chan int
c := make(chan int) // c has type: chan int
Supposons que Go n'a pas new
et make
, mais il a la fonction intégrée NEW
. Ensuite, l'exemple de code ressemblerait à ceci:
p := NEW(*chan int) // * is mandatory
c := NEW(chan int)
Le *
serait obligatoire , donc:
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
Oui, la fusion new
et make
en une seule fonction intégrée est possible. Cependant, il est probable qu'une seule fonction intégrée entraînerait plus de confusion chez les nouveaux programmeurs Go que d'avoir deux fonctions intégrées.
Compte tenu de tous les points ci-dessus, il semble plus approprié new
et make
séparé.
int
est créé.
make(Point)
et make(int)
dans ces 2 dernières lignes?
make
La fonction alloue et initialise uniquement un objet de type tranche, carte ou chan. Comme new
, le premier argument est un type. Mais, cela peut aussi prendre un deuxième argument, la taille. Contrairement à new, le type de retour de make est le même que le type de son argument, pas un pointeur sur celui-ci. Et la valeur allouée est initialisée (non mise à zéro comme dans new). La raison en est que tranche, carte et chan sont des structures de données. Ils doivent être initialisés, sinon ils ne seront pas utilisables. C'est la raison pour laquelle new () et make () doivent être différents.
Les exemples suivants de Efficace Go le montrent très clairement:
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)
, il alloue simplement de la mémoire à [] int, mais ne s'initialise pas, il revient donc simplement nil
; pas le pointeur vers la mémoire car il est inutilisable. make([]int)
alloue et initialise pour qu'il soit utilisable, puis retourne son adresse.
new(T)
- Alloue de la mémoire et la met à zéro pour le type T .. ..
c'est-à-dire 0
pour int , ""
pour chaîne et nil
pour les types référencés ( tranche , carte , chan )
Notez que les types référencés ne sont que des pointeurs vers certaines structures de données sous-jacentes , qui ne seront pas créées par l' new(T)
exemple: en cas de tranche , le tableau sous-jacent ne sera pas créé, donc ne new([]int)
renvoie un pointeur vers rien
make(T)
- Alloue de la mémoire pour les types de données référencés ( tranche , carte , chan ), plus initialise leurs structures de données sous-jacentes
Exemple: en cas de tranche , le tableau sous-jacent sera créé avec la longueur et la capacité spécifiées
Gardez à l'esprit que, contrairement à C, un tableau est un type primitif dans Go!
Cela étant dit:
make(T)
se comporte comme une syntaxe littérale composite
new(T)
se comporte comme var
(lorsque la variable n'est pas initialisée)
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)
}
Exécutez le programme
-- 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
Pour en savoir plus:
https://golang.org/doc/effective_go.html#allocation_new
https://golang.org/doc/effective_go.html#allocation_make
Vous devez make()
créer des canaux et des cartes (et des tranches, mais ceux-ci peuvent également être créés à partir de tableaux). Il n'y a pas d'autre moyen de les créer, vous ne pouvez donc pas les supprimer make()
de votre lexique.
Quant à new()
, je ne connais aucune raison pour laquelle vous en avez besoin lorsque vous pouvez utiliser la syntaxe struct. Il a cependant une signification sémantique unique, qui est "créer et renvoyer une structure avec tous les champs initialisés à leur valeur zéro", ce qui peut être utile.
En dehors de tout ce qui est expliqué dans Effective Go , la principale différence entre new(T)
et &T{}
est que ce dernier effectue explicitement une allocation de tas. Cependant, il convient de noter que cela dépend de la mise en œuvre et peut donc être sujet à changement.
La comparaison make
avec new
n'a pas de sens car les deux remplissent des fonctions entièrement différentes. Mais cela est expliqué en détail dans l'article lié.
&T{}
effectue explicitement une allocation de tas est AFAIK qui n'est basée sur rien dans les spécifications. En fait, je crois que les analyses d'échappement conservent déjà ce * T sur la pile autant que possible de la même manière qu'avec new(T)
.
new (T): il renvoie un pointeur pour taper T une valeur de type * T, il alloue et met à zéro la mémoire. new (T) est équivalent à & T {} .
make (T): il retourne une valeur initialisée de type T , il alloue et initialise la mémoire. Son utilisé pour les tranches, la carte et les canaux.