Go のポインターを使用すると関数の引数を変更できることは知っていますが、参照 (適切な const または mutable 修飾子を使用) だけを採用すればもっと簡単だったのではないでしょうか。現在ではポインターがあり、マップやチャネルなどの一部の組み込み型では暗黙的に参照渡しが行われます。
何か見落としているのでしょうか、それとも Go のポインタは単に不必要な複雑さなのでしょうか?
ベストアンサー1
ポインタはいくつかの理由で便利です。ポインタを使用すると、メモリレイアウトを制御できます (CPU キャッシュの効率に影響します)。Go では、すべてのメンバーが連続したメモリにある構造を定義できます。
type Point struct {
x, y int
}
type LineSegment struct {
source, destination Point
}
この場合、Point
構造体は構造体内に埋め込まれますLineSegment
。ただし、常にデータを直接埋め込むことができるわけではありません。バイナリ ツリーやリンク リストなどの構造体をサポートする場合は、何らかのポインタをサポートする必要があります。
type TreeNode {
value int
left *TreeNode
right *TreeNode
}
Java、Python などでは複合型の埋め込みが許可されていないため、この問題は発生しません。そのため、埋め込みとポインティングを構文的に区別する必要はありません。
Swift/C# 構造体の問題は Go ポインタで解決されます
同じことを実現する別の方法としては、 C# や Swift のようにstruct
と を区別する方法class
があります。ただし、これには制限があります。通常、関数が構造体をinout
パラメーターとして受け取るように指定して構造体のコピーを回避することはできますが、構造体への参照 (ポインター) を保存することはできません。つまり、プール アロケーターを作成する場合など、構造体を参照型として扱うことが便利な場合は、決してそうすることができません (以下を参照)。
カスタムメモリアロケータ
ポインターを使用して、独自のプール アロケータを作成することもできます (これは、原理を示すために多くのチェックが削除され、非常に簡略化されています)。
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
}
2つの値を交換する
ポインタを使用すると、 を実装することもできますswap
。これは、2 つの変数の値を交換することです。
func swap(a *int, b *int) {
temp := *a
*a = *b
*b = temp
}
結論
Java は、Google などのシステム プログラミングにおいて C++ を完全に置き換えることはできませんでした。その理由の 1 つは、メモリのレイアウトと使用法を制御できないため、パフォーマンスを同じ程度に調整できないためです (キャッシュ ミスはパフォーマンスに大きく影響します)。Go は多くの領域で C++ を置き換えることを目指しているため、ポインターをサポートする必要があります。