2012-06-21 25 views
19

que tienen la funciónpunteros golang sobre punteros como función de parámetros

func addCatsToMap(m map[string][]CatHouse, meowId int, treats Set, dog *Dog) { 

//if (complicated thing) add Cat to m 

} 

¿Es cierto que m, golosinas, y el perro se pasan por referencia, y meowId tiene su valor copiado.

Como m es el mapa, es una referencia de paso por referencia.

Perro es una estructura. Entonces, debería pasar el puntero para evitar copiar los datos.

Set es una interfaz, como se define aquí:

type Set interface { 
    Add(value string) 
    Contains(value string) (bool) 
    Length() (int) 
    RemoveDuplicates() 
} 

está ajustado pase por valor?

Respuesta

37

Un tipo de interfaz es simplemente un conjunto de métodos. Observe que los miembros de una definición de interfaz no especifican si el tipo de receptor es un puntero o no. Esto es porque el conjunto de métodos de un tipo de valor es un subconjunto del conjunto de métodos de su tipo de puntero asociado. Eso es un bocado.Lo que quiero decir es, si tiene lo siguiente:

type Whatever struct { 
    Name string 
} 

y definir los dos métodos siguientes:

func (w *Whatever) Foo() { 
    ... 
} 

func (w Whatever) Bar() { 
    ... 
} 

Entonces el tipo Whatever tiene sólo el método Bar(), mientras que el tipo *Whatever tiene los métodos Foo() y Bar(). Eso significa que si tiene la siguiente interfaz:

type Grits interface { 
    Foo() 
    Bar() 
} 

Entonces *Whatever implementa Grits pero Whatever no, porque le falta el método WhateverFoo(). Cuando define la entrada a una función como un tipo de interfaz, no tiene idea si es un puntero o un tipo de valor.

El siguiente ejemplo ilustra una función que toma un tipo de interfaz en ambos sentidos:

package main 

import "fmt" 

type Fruit struct { 
    Name string 
} 

func (f Fruit) Rename(name string) { 
    f.Name = name 
} 

type Candy struct { 
    Name string 
} 

func (c *Candy) Rename(name string) { 
    c.Name = name 
} 

type Renamable interface { 
    Rename(string) 
} 

func Rename(v Renamable, name string) { 
    v.Rename(name) 
    // at this point, we don't know if v is a pointer type or not. 
} 

func main() { 
    c := Candy{Name: "Snickers"} 
    f := Fruit{Name: "Apple"} 
    fmt.Println(f) 
    fmt.Println(c) 
    Rename(f, "Zemo Fruit") 
    Rename(&c, "Zemo Bar") 
    fmt.Println(f) 
    fmt.Println(c) 
} 

que se podría llamar Raname(&f, "Jorelli Fruit") pero no Rename(c, "Jorelli Bar"), porque ambos Fruit y *Fruit implemento Renamable, mientras *Candy implementa Renable y Candy no lo hace .

http://play.golang.org/p/Fb-L8Bvuwj

6

Pasar por referencia es algo del lenguaje, nada en Go es "pasar por referencia". Pasar por referencia significa que el operador de asignación puede cambiar el valor original cuando se usa solo. Sin embargo, hay tipos de referencia como mapas y punteros que apuntan a alguna parte. El uso del operador de asignación en ellos no modificará el original a menos que use otros operadores, como el índice del mapa y el operador *.

Tiene la certeza de que su mapa m es un tipo de referencia y, por lo tanto, como un puntero. Cualquier cambio en el mapa, excepto la sustitución del mapa, modificará el original.

m["whatever"] = 2   // Modifies the original map 
m = anothermap    // Does not modify the original map 

Si había verdadero "paso por referencia", el segundo ejemplo modificaría el mapa original.

Al pasar un puntero, como lo hace con dog le permite modificar el original. Si llama a cualquier método de puntero o utiliza el operador *, el original cambiará. En su ejemplo, un puntero puede no haber sido necesario. Si Dog es pequeño, puede ser más fácil simplemente pasar una copia. Depende del programador determinar cuándo es un buen momento para usar un puntero.

Set no se ha pasado por referencia. Las interfaces no son referencias. Si bien es cierto que internamente en el compilador 6g una interfaz utiliza punteros, la interfaz en sí misma no actúa como tal. Pasar una interfaz, sin importar el tamaño del objeto que contiene, es tan barato como pasar un puntero usando el compilador 6g. Sin embargo, no hay forma de modificar el valor original de una interfaz como lo hace con punteros y mapas.

Aunque no puede modificar la interfaz original aprobada, la interfaz puede contener un tipo de puntero. En ese caso, actuaría igual que el puntero del perro, donde la invocación de ciertos métodos puede modificar el original. Para su interfaz particular Set, supongo que contiene un tipo de puntero basado en los nombres del método. Por lo tanto, cuando llame al set.Add(whatever), cambiará los datos internos del original.

3

Calls, The Go Programming Language Specification

En una llamada de función, el valor de la función y los argumentos se evalúan en el orden habitual. Después de que se evalúan, los parámetros de la llamada se pasan por valor a la función y la función llamada comienza la ejecución . Los parámetros de retorno de la función pasan por el valor de regreso a la función de llamada cuando la función retorna.

When are function parameters passed by value? FAQ - The Go Programming Language.

Como en todos los idiomas en la familia C, todo en Go se pasa por valor. Es decir, una función siempre obtiene una copia de la cosa que se pasa, como si hubiera una instrucción de asignación asignando el valor al parámetro. Por ejemplo, al pasar un valor int a una función realiza una copia del int, y al pasar un valor de puntero se realiza una copia del puntero, pero no los datos a los que apunta . (Consulte la siguiente sección para una discusión de cómo esto afecta método receptores.)

Mapa y rebanada valores se comportan como punteros: son descriptores que contienen punteros a los mapas o segmento de datos subyacentes. Al copiar un mapa o , el valor de corte no copia los datos a los que apunta. Al copiar una interfaz , el valor realiza una copia de la cosa almacenada en el valor de la interfaz. Si el valor de la interfaz contiene una estructura, al copiar el valor de la interfaz se crea una copia de la estructura. Si el valor de interfaz contiene un puntero, copiando el valor de la interfaz hace una copia del puntero, pero de nuevo no los datos a los que apunta.