new() 関数
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 22/107。
Goは、型のメモリを割り当て、そのポインタを返す new() という組み込み関数を提供しています。割り当てられたメモリは、その型のゼロ値で初期化されます。
p := new(int) // int型のメモリを割り当てます
fmt.Println(*p) // 0 (ゼロ値)
fmt.Println(p) // 0xc000018030 (メモリアドレス)new()関数は、型を引数として受け取り、その型の新しく割り当てられたゼロ値へのポインタを返します。これは、ポインタが必要だが参照する既存の変数がない場合に特に便利です。
構造体では、new() はすべてのフィールドがゼロ値に設定された構造体へのポインタを作成します。
type Person struct {
Name string
Age int
}
p := new(Person)
fmt.Println(p.Name) // "" (空文字列)
fmt.Println(p.Age) // 0new() とアドレス演算子 & のどちらを使うべきか迷うかもしれません。以下のどちらのアプローチも同等です:
// new() を使用
p1 := new(Person)
// リテラルとアドレス演算子を使用
p2 := &Person{}&Person{} 構文は、フィールドを同時に初期化できるため、実際にはより一般的です。ゼロ値のインスタンスが特に必要な場合や、明示的なメモリ割り当てのセマンティクスを好む場合は、new() を使用してください。
チャレンジ
簡単new() がどのようにメモリを割り当て、構造体をゼロ値で初期化するかを実演する設定マネージャーを構築しましょう。このアプローチが、構造体リテラルでアドレス演算子を使用する場合とどのように異なるかを確認します。
コードは2つのファイルに分けて構成します:
config.go:Host(string)、Port(int)、MaxConnections(int)、DebugMode(bool) フィールドを持つServerConfig構造体を定義します。以下の2つの関数を作成してください:NewServerConfig:new()を使用してServerConfigを割り当て、そのポインタを直接返します(すべてのフィールドはゼロ値になります)。ConfigureServer:*ServerConfigポインタ、ホスト文字列、ポート整数を受け取り、ポインタを介して設定のそれらのフィールドをセットします。
Displayメソッドを*ServerConfigに追加してください。main.go: 入力からホストとポートを読み取ります。まず、NewServerConfig()を使用して設定を作成し、それを表示してゼロ値であることを示します。次に、ConfigureServerを使用してホストとポートを設定し、再度設定を表示して更新された値を示します。
以下の入力が提供されます:
- 1行目: ホスト名 (string)
- 2行目: ポート番号 (integer)
Display メソッドは、以下の形式で文字列を返す必要があります:
Host: [Host]
Port: [Port]
MaxConnections: [MaxConnections]
DebugMode: [DebugMode]表示出力を2回プリントしてください。1回目は設定前(ゼロ値を表示)、2回目は設定後(更新されたホストとポートを表示)です。
例えば、localhost と 8080 が与えられた場合、出力は以下のようになります:
Host:
Port: 0
MaxConnections: 0
DebugMode: false
Host: localhost
Port: 8080
MaxConnections: 0
DebugMode: falsenew() はすべてをゼロ値で初期化するため、最初の表示ではすべてのゼロ値(空文字列、0、0、false)が表示されることに注目してください。ConfigureServer を呼び出した後は、ホストとポートのみが変更され、他のフィールドはゼロ値のままとなります。
チートシート
new() 関数は、型のメモリを割り当て、その型をゼロ値で初期化した後、そのポインタを返します。
p := new(int) // int型のメモリを割り当てます
fmt.Println(*p) // 0 (ゼロ値)
fmt.Println(p) // 0xc000018030 (メモリ番地)構造体の場合、new() はすべてのフィールドがゼロ値に設定された構造体へのポインタを作成します。
type Person struct {
Name string
Age int
}
p := new(Person)
fmt.Println(p.Name) // "" (空文字列)
fmt.Println(p.Age) // 0new() を使う場合と、アドレス演算子 & を使う場合を比較すると、どちらも同等です。
// new() を使用する場合
p1 := new(Person)
// リテラルに対してアドレス演算子を使用する場合
p2 := &Person{}&Person{} という構文の方が一般的です。なぜなら、作成時にフィールドの初期化ができるからです。明示的なメモリ割り当てのセマンティクスを持ち、ゼロ値で初期化されたインスタンスが必要な場合には new() を使用してください。
自分で試してみよう
package main
import (
"fmt"
)
func main() {
// 入力を読み込む
var host string
var port int
fmt.Scanln(&host)
fmt.Scanln(&port)
// TODO: NewServerConfig() を使用して設定を作成し、それを表示する
// これによりゼロ値が表示される
// TODO: ConfigureServer を使用してホストとポートを設定する
// TODO: 設定を再度表示して、更新された値を確認する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー