Wskaźniki są przydatne z kilku powodów. Wskaźniki umożliwiają kontrolę nad układem pamięci (wpływa na wydajność pamięci podręcznej procesora). W Go możemy zdefiniować strukturę, w której wszyscy członkowie są w ciągłej pamięci:
type Point struct {
x, y int
}
type LineSegment struct {
source, destination Point
}
W tym przypadku Point
struktury są osadzone w LineSegment
strukturze. Ale nie zawsze możesz bezpośrednio osadzać dane. Jeśli chcesz obsługiwać struktury, takie jak drzewa binarne lub lista połączona, musisz obsługiwać jakiś rodzaj wskaźnika.
type TreeNode {
value int
left *TreeNode
right *TreeNode
}
Java, Python itp. Nie ma tego problemu, ponieważ nie pozwala na osadzanie typów złożonych, więc nie ma potrzeby składniowego rozróżniania między osadzaniem a wskazywaniem.
Problemy ze strukturami języka Swift / C # rozwiązane za pomocą wskaźników Go
Możliwą alternatywą osiągnięcia tego samego jest rozróżnienie między struct
i, class
jak C # i Swift. Ale to ma ograniczenia. Chociaż zwykle można określić, że funkcja przyjmuje strukturę jakoinout
parametr, aby uniknąć kopiowania struktury, nie pozwala to na przechowywanie odniesień (wskaźników) do struktur. Oznacza to, że nigdy nie możesz traktować struktury jako typu referencyjnego, jeśli uznasz to za przydatne, np. Do utworzenia alokatora puli (patrz poniżej).
Niestandardowy alokator pamięci
Korzystając ze wskaźników, możesz również utworzyć własny alokator puli (jest to bardzo uproszczone, ponieważ usunięto wiele sprawdzeń, aby pokazać tylko zasadę):
type TreeNode {
value int
left *TreeNode
right *TreeNode
nextFreeNode *TreeNode; // For memory allocation
}
var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0]
func poolAlloc() *TreeNode {
node := firstFreeNode
firstFreeNode = firstFreeNode.nextFreeNode
return node
}
func freeNode(node *TreeNode) {
node.nextFreeNode = firstFreeNode
firstFreeNode = node
}
Zamień dwie wartości
Wskaźniki pozwalają również na implementację swap
. To jest zamiana wartości dwóch zmiennych:
func swap(a *int, b *int) {
temp := *a
*a = *b
*b = temp
}
Wniosek
Java nigdy nie była w stanie w pełni zastąpić C ++ w programowaniu systemów w miejscach takich jak Google, po części dlatego, że wydajności nie można dostroić w takim samym stopniu z powodu braku możliwości kontrolowania układu i wykorzystania pamięci (błędy w pamięci podręcznej znacząco wpływają na wydajność). Go miał na celu zastąpienie C ++ w wielu obszarach i dlatego musi obsługiwać wskaźniki.