Menu
Coddy logo textTech

構造体へのポインタ

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 20/107。

構造体(struct)を扱う際、大きなデータ構造のコピーを避け、元の構造体を変更するために、ポインタを頻繁に使用します。構造体へのポインタは、その構造体インスタンスのメモリ番地を保持します。

既存の構造体に対して & 演算子を使用するか、構造体リテラルで直接アドレス演算子を使用することで、構造体ポインタを作成できます。

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
ptr := &p  // 既存の構造体へのポインタ

// または直接作成する
ptr2 := &Person{Name: "Bob", Age: 25}

Goは、ポインタを介して構造体のフィールドにアクセスする際に便利なショートカットを提供します。(*ptr).Nameと書く代わりに、単にptr.Nameと書くことができます。Goは自動的にポインタをデリファレンスします。

ptr := &Person{Name: "Alice", Age: 30}

// どちらも同じように動作します
fmt.Println((*ptr).Name)  // 明示的なデリファレンス
fmt.Println(ptr.Name)     // 自動的なデリファレンス

この自動的なデリファレンスは、フィールドを変更する際にも適用されます。ポインタを介した変更は、元の構造体に影響を与えます。

p := Person{Name: "Alice", Age: 30}
ptr := &p

ptr.Age = 31           // 元の値を変更します
fmt.Println(p.Age)     // 31

構造体ポインタは、GoのOOPパターンにおいて不可欠です。これらにより、メソッドがレシーバの状態を変更できるようになり、大きな構造体を関数に効率的に渡すことが可能になります。また、ツリーやリストのような連結データ構造を構築するための基礎となります。

challenge icon

チャレンジ

簡単

構造体ポインタを使用して元のデータを効率的に変更する方法を示す銀行口座システムを構築しましょう。ポインタ操作を通じて更新可能な口座を作成します。

コードを2つのファイルに分けて構成します:

  • account.go: Owner (string)、Balance (float64)、および AccountNumber (string) フィールドを持つ BankAccount 構造体を定義します。所有者名と口座番号をパラメータとして受け取り、開始残高が 0 の新しい BankAccount へのポインタを返す NewAccount という関数を作成します。また、*BankAccount ポインタと金額を受け取り、ポインタを介してその金額を口座の残高に加算する Deposit 関数を作成します。
  • main.go: 入力から口座情報を読み取り、コンストラクタ関数(ポインタを返すもの)を使用して口座を作成し、ポインタを使用して入金を行い、残高が変更されたことを確認するために口座の詳細を表示します。

以下の入力が提供されます:

  • 1行目: 所有者名
  • 2行目: 口座番号
  • 3行目: 入金額 (float)

出力形式:

Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]

例えば、John SmithACC-12345500.50 が与えられた場合、出力は以下のようになります:

Account: ACC-12345
Owner: John Smith
Balance: 500.50

NewAccount 関数は & 演算子を使用して *BankAccount を返す必要があること、また Deposit 関数は Go の自動デリファレンス(例:(*account).Balance の代わりに account.Balance)を使用して、ポインタを介して直接残高を変更できることを覚えておいてください。

チートシート

構造体へのポインタは、構造体インスタンスのメモリ番地を保持します。& 演算子を使用して構造体ポインタを作成します:

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}
ptr := &p  // 既存の構造体へのポインタ

// または直接作成する
ptr2 := &Person{Name: "Bob", Age: 25}

Goは、ポインタを介して構造体のフィールドにアクセスする際に自動的なデリファレンスを提供します。(*ptr).Name の代わりに、ptr.Name と書くことができます:

ptr := &Person{Name: "Alice", Age: 30}

// どちらも同じように動作します
fmt.Println((*ptr).Name)  // 明示的なデリファレンス
fmt.Println(ptr.Name)     // 自動的なデリファレンス

ポインタを介した変更は、元の構造体に影響を与えます:

p := Person{Name: "Alice", Age: 30}
ptr := &p

ptr.Age = 31           // 元の値を変更する
fmt.Println(p.Age)     // 31

構造体ポインタにより、メソッドがレシーバの状態を変更できるようになり、大きな構造体を関数に効率的に渡すことが可能になります。また、連結データ構造の基礎となります。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 所有者の名前を読み込む
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)
	
	// 口座番号を読み込む
	accountNumber, _ := reader.ReadString('\n')
	accountNumber = strings.TrimSpace(accountNumber)
	
	// 入金額を読み込む
	amountStr, _ := reader.ReadString('\n')
	amountStr = strings.TrimSpace(amountStr)
	amount, _ := strconv.ParseFloat(amountStr, 64)
	
	// TODO: NewAccount関数を使用して新しい口座を作成する(ポインタを返す)
	
	// TODO: 口座ポインタを使用してDeposit関数で入金を行う
	
	// TODO: 以下の形式で口座の詳細を表示する:
	// Account: [AccountNumber]
	// Owner: [Owner]
	// Balance: [Balance]
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン