2013-02-26 21 views
19

Ich lerne gerade mit Go Sprache programmieren. Ich habe einige Schwierigkeiten, Go-Zeiger zu verstehen (und mein C/C++ ist jetzt weit weg ...). In der Tour of Go # 52 (http://tour.golang.org/#52) zum Beispiel las ich:Golang Zeiger

type Vertex struct { 
    X, Y float64 
} 

func (v *Vertex) Abs() float64 { 
    return math.Sqrt(v.X*v.X + v.Y*v.Y) 
} 

func main() { 
    v := &Vertex{3, 4} 
    fmt.Println(v.Abs()) 
} 

Aber wenn statt

schrieb ich:

func (v Vertex) Abs() float64 { 
[...] 
v := Vertex{3, 4} 

Oder auch:

func (v Vertex) Abs() float64 { 
[...] 
v := &Vertex{3, 4} 

und umgekehrt:

func (v *Vertex) Abs() float64 { 
[...] 
v := Vertex{3, 4} 

Ich habe genau das gleiche Ergebnis. Gibt es einen Unterschied (speichertechnisch usw.)?

+2

Hallo und Willkommen bei den Programmierern. Direkte Implementierungsfragen wie diese sind hier nicht Thema, sondern zum Thema Stack Overflow. Ich werde eine Migration einleiten. Hab einen angenehmen Tag. –

+2

Versuchen Sie, das 'v' in allen Methoden zu ändern, und dann' fmt.Println() 'das Original nach dem Anruf, und Sie werden den Unterschied sehen. Mit den '(v Vertex)' - Versionen erhalten Sie eine Kopie des Originals. Wenn es auf einem Zeiger aufgerufen wurde, wird es automatisch für Sie dereferenziert. –

+1

Siehe auch: [Mehrdeutigkeiten von Methodenempfängern] (http://stackoverflow.com/questions/14926860/method-receivers-ambiguity) –

Antwort

31

Es gibt zwei verschiedene Regeln der Sprache geht durch Ihre Beispiele verwendet:

  1. Es ist möglich, ein Verfahren mit einem Zeiger Empfänger von einem Verfahren mit einem Wert Empfänger abzuleiten. So wird func (v Vertex) Abs() float64 automatisch eine zusätzliche Methode Implementierung erzeugen:

    func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) } 
    func (v *Vertex) Abs() float64 { return Vertex.Abs(*v) } // GENERATED METHOD 
    

    Der Compiler automatisch die generierte Methode finden:

    v := &Vertex{3, 4} 
    v.Abs() // calls the generated method 
    
  2. Go die Adresse einer Variablen automatisch erfolgen kann.In dem folgenden Beispiel:

    func (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X+v.Y*v.Y) } 
    func main() { 
        v := Vertex{3, 4} 
        v.Abs() 
    } 
    

    der Ausdruck v.Abs() entspricht dem folgenden Code:

    vp := &v 
    vp.Abs() 
    
+0

Erzeugt doing * v eine Kopie von v im Speicher (Vertex struct)? Wenn Nein dann die Änderung in v selbst während der Ausführung von func (v Vertex) Abs() nach außen reflektieren? – MaX

+2

Eine neue Kopie von struct Vertex wird erstellt, bevor 'func (v Vertex) Abs()' eingegeben wird. –

+1

@Atom Ist es nicht korrekter im ersten Fall zu schreiben, wenn der Struct Vertex vor dem Eingeben von 'func (v Vertex) Abs()' kopiert wird: 'func (v * Vertex) Abs() float64 {return (* v) .Abs()} // GENERIERTE METHODE? Ich habe Probleme, Vertex.Abs (* v) zu verstehen ... – eAbi

2

Der Unterschied ist Pass-by-Reference vs Pass-by-Value.

In func f(v Vertex) ist das Argument kopiert in Parameter v. In func f(v *Vertex) wird ein Zeiger auf eine existierende Vertex Instanz übergeben.

Bei der Verwendung von Methoden können einige der Dereferenzierung für Sie getan werden, so dass Sie eine Methode haben func (v *Vertex) f() und rufen Sie ohne einen Zeiger zuerst: v := Vertex{...}; v.f(). Dies ist nur ein Körnchen Syntax Zucker, AFAIK.

13

Es gibt Unterschiede. Zum Beispiel zwingt das Nicht-Zeiger-Empfängerformular die Methode, an einer Kopie zu arbeiten. Auf diese Weise kann die Methode die Instanz, für die sie aufgerufen wurde, nicht mutieren - sie kann nur auf die Kopie zugreifen. Was in Bezug auf z.B. Zeit/Speicherleistung/Verbrauch usw.

OTOH, Zeiger auf Instanzen und Methoden mit Zeigerempfängern ermöglichen eine einfache Instanzfreigabe (und Mutation), wo dies wünschenswert ist.

Weitere Details here.

0

Es gibt zwei Hauptunterschiede in diesen Beispielen:

func (v *Vertex) Abs().... 

Der Empfänger wird bestanden für v und du würdest diese Methode der Lage sein, auf Zeiger nur zu nennen:

v := Vertex{1,3} 
v.Abs() // This will result in compile time error 
&v.Abs() // But this will work 

Auf der anderen Seite

func (v Vertex) Abs() .... 

können Sie diese Methode aufrufen, sowohl auf Zeiger und Strukturen. Der Empfänger wird übergeben übergeben, auch wenn Sie diese Methode auf Zeiger aufrufen.

v := Vertex{1,3} 
v.Abs() // This will work, v will be copied. 
&v.Abs() // This will also work, v will also be copied. 

Sie können sowohl func (v *Vertex) und func (v Vertex) erklären.

0

Da die Spezifikation sagt

Ein Methodenaufruf x.m() gültig ist, wenn das Verfahren Satz (der Art von) x m Liste und das Argument enthält, kann in die Parameterliste von m zugeordnet werden. Wenn x adressierbar und & x Methode Set enthält m, xm() ist eine Abkürzung für (& x) .m():

In Ihrem Fall v.Abs() ist eine Abkürzung für & v.Abs() wenn Methode adressierbar ist.