2010-05-02 14 views

Antwort

5

Wenn Sie eine Variable type bar deklarieren oder zuweisen, reservieren und initialisieren Sie für rd uint8 und foo foo_ den Speicher null. Es gibt immer eine Variable type foo_ eingebettet in eine Variable type bar.

var b bar // declare b 

Wenn Sie eine Variable von type barP deklarieren oder zuordnen, können Sie auf Null Speicher sowohl reservieren und initialisieren für rd uint8 und foo *foo_. Ein Nullwertzeiger ist ein nil Zeiger. Keine Variable type foo_ ist zugewiesen; Sie müssen das separat machen. Es gibt entweder Null (foo == nil) oder eine Variable von type foo_, auf die eine Variable von type barP zeigt. Eine Variable von type barP kann auf dieselbe Variable wie type foo_ als andere Variablen von type barP zeigen, die dieselbe Kopie der Variablen type foo_ teilen. Eine Änderung an einer gemeinsamen Kopie wird von allen Variablen erkannt, die auf sie zeigen.

var bp barP   // declare bp 
bp.foo = new(foo_) // allocate bp.foo 

Welches, hängt im Vergleich zu type barP auf die Eigenschaften von type bar zu verwenden. Welcher Typ spiegelt das Problem, das Sie lösen möchten, näher wider?

Betrachten Sie beispielsweise dieses Rechnungsproblem. Wir haben immer eine Rechnungsadresse; Wir werden immer nach unserem Geld fragen. Wir liefern jedoch oft an die Rechnungsadresse, aber nicht immer. Wenn die Lieferadresse nil lautet, verwenden Sie die Rechnungsadresse. Andernfalls verwenden Sie eine separate Lieferadresse. Wir haben zwei Lagerhäuser, und wir versenden immer von dem einen oder dem anderen. Wir können die zwei Lagerstandorte teilen. Da wir keine Rechnung senden, bis die Bestellung vom Lager versandt wird, wird der Lagerort niemals sein.

type address struct { 
    street string 
    city string 
} 

type warehouse struct { 
    address string 
} 

type invoice struct { 
    name  string 
    billing address 
    shipping *address 
    warehouse *warehouse 
} 
2

Die Antwort ist weitgehend unabhängig von der Sprache - das Äquivalent in C hat die gleichen Probleme.

Wenn Sie einen eingebetteten Wert haben (wie in bar), dann ist Ihre Struktur groß genug, um die komplette Unterstruktur und den anderen Teil zu halten.

Wenn Sie einen Zeiger auf einen Wert haben (wie in barP), dann kann eine Anzahl von Strukturen des Typs barP die gleichen foo teilen. Wenn einer der barP einen Teil der foo ändert, auf die er zeigt, wirkt sich dies auf alle anderen barP Strukturen aus, die auf dieselbe Stelle zeigen. Wie der Kommentar bereits andeutet, müssen Sie zwei separate Objekte verwalten - das barP und das foo gegenüber einem mit dem einfachen bar Typ.

In einigen Sprachen müssten Sie sich Gedanken über hängende Zeiger und nicht initialisierte Werte usw. machen; Go ist Müll gesammelt und generell typsicherer als andere Sprachen.

Verwenden Sie also einen Zeiger, wenn mehrere Objekte barP dasselbe Objekt foo teilen sollen; Verwenden Sie andernfalls ein explizites Mitgliedsobjekt und keinen Zeiger auf ein Objekt.

+1

Wenn eine Variable vom Typ "barP" deklariert oder zugewiesen wird, ist der Anfangswert von foo ein "nil" -Zeiger. – peterSO

2

Die Golang FAQ fasst nun die Differenz zwischen:

func (s *MyStruct) pointerMethod() { } // method on pointer 
func (s MyStruct) valueMethod() { } // method on value 

Erstens und am wichtigsten, funktioniert das Verfahren benötigt den Empfänger zu ändern?
Wenn dies der Fall ist, muss der Empfänger ein Zeiger sein. (Slices und Maps sind Referenztypen, also ist ihre Geschichte etwas subtiler, aber um beispielsweise die Länge eines Slices in einer Methode zu ändern, muss der Empfänger immer noch ein Zeiger sein.)
In den obigen Beispielen ändert pointerMethod die Felder von s, der Anrufer wird diese Änderungen sehen, aber valueMethod wird mit einer Kopie des Arguments des Aufrufers aufgerufen (das ist die Definition der Übergabe eines Wertes), so Änderungen, die es macht, wird für den Aufrufer unsichtbar sein.
Zeiger Empfänger sind übrigens identisch mit der Situation in Java, obwohl in Java die Zeiger unter den Deckeln versteckt sind; es ist Gos Wertempfängern, die ungewöhnlich sind.

Zweitens ist die Überlegung von Effizienz. Wenn der Empfänger groß ist, beispielsweise eine große Struktur, wird es viel billiger sein, einen Zeigerempfänger zu verwenden.

Weiter ist Konsistenz (Dieser Wirkungsgrad Punkt in "Memory, variables in memory, and pointers" auch dargestellt ist). Wenn einige der Methoden des Typs Zeigerempfänger haben müssen, sollte der Rest auch, also die Methodenmenge konsistent sein, unabhängig davon, wie der Typ verwendet wird. Einzelheiten finden Sie im Abschnitt method sets.

Verwandte Themen