Go 構造体の詳細

Linda Hamilton
Linda Hamiltonオリジナル
2025-01-04 05:02:40459ブラウズ

Go では、struct はデータの定義とカプセル化に使用される集約型です。異なるタイプのフィールドを組み合わせることができます。構造体は、他の言語のクラスと同様のカスタム データ型と見なされますが、継承はサポートされていません。メソッドは、特定の型 (多くの場合、構造体) に関連付けられた関数であり、その型のインスタンスを使用して呼び出すことができます。

構造体の定義と初期化

構造体の定義

構造体は、type キーワードと struct キーワードを使用して定義されます。単純な構造体定義の例を次に示します。

type User struct {
  Username    string
  Email       string
  SignInCount int
  IsActive    bool
}

構造体の初期化

構造体はさまざまな方法で初期化できます。

フィールド名による初期化

user1 := User{
  Username:    "alice",
  Email:       "alice@example.com",
  SignInCount: 1,
  IsActive:    true,
}

デフォルト値で初期化する

一部のフィールドが指定されていない場合、それらはそれぞれのタイプのゼロ値に初期化されます。

user2 := User{
  Username: "bob",
}

この例では、Email は空の文字列 ("")、SignInCount は 0、IsActive は false に初期化されます。

ポインタによる初期化

構造体はポインターを使用して初期化することもできます。

user3 := &User{
  Username: "charlie",
  Email:    "charlie@example.com",
}

構造体のメソッドと動作

Go では、構造体はデータを保存するためだけでなく、構造体に対してメソッドを定義することもできます。これにより、構造体はデータに関連する動作をカプセル化できるようになります。以下に、構造体のメソッドと動作について詳しく説明します。

構造体のメソッドの定義

メソッドはレシーバーを使用して定義されます。レシーバーはメソッドの最初のパラメーターであり、メソッドが属するタイプを指定します。レシーバーは、値レシーバーまたはポインタ レシーバーのいずれかです。

バリューレシーバー

値レシーバーは、メソッドが呼び出されたときに構造体のコピーを作成するため、フィールドを変更しても元の構造体には影響しません。

type User struct {
  Username string
  Email    string
}

func (u User) PrintInfo() {
  fmt.Printf("Username: %s, Email: %s\n", u.Username, u.Email)
}

ポインターレシーバー

ポインター レシーバーを使用すると、メソッドで元の構造体フィールドを直接変更できます。

func (u *User) UpdateEmail(newEmail string) {
  u.Email = newEmail
}

メソッドセット

Go では、構造体のすべてのメソッドがそのメソッド セットを形成します。値レシーバーに設定されたメソッドには、値レシーバーを持つすべてのメソッドが含まれますが、ポインター レシーバーに設定されたメソッドには、ポインターと値レシーバーの両方を持つすべてのメソッドが含まれます。

インターフェイスと構造体メソッド

構造体メソッドは、多態性を実現するためにインターフェイスとともによく使用されます。インターフェイスを定義するときは、構造体が実装する必要があるメソッドを指定します。

type UserInfo interface {
  PrintInfo()
}

// User implements the UserInfo interface
func (u User) PrintInfo() {
  fmt.Printf("Username: %s, Email: %s\n", u.Username, u.Email)
}

func ShowInfo(ui UserInfo) {
  ui.PrintInfo()
}

構造体のメモリアライメント

Go では、アクセス効率を向上させるために構造体のメモリ アラインメントが設計されています。さまざまなデータ型には特定のアライメント要件があり、コンパイラはこれらの要件を満たすために構造体フィールド間にパディング バイトを挿入する場合があります。

メモリアライメントとは何ですか?

メモリのアラインメントとは、メモリ内のデータが特定の値の倍数のアドレスに配置される必要があることを意味します。データ型のサイズによって、そのアライメント要件が決まります。たとえば、int32 では 4 バイトへのアライメントが必要で、int64 では 8 バイトへのアライメントが必要です。

なぜメモリアライメントが必要なのでしょうか?

CPU のパフォーマンスにとって、効率的なメモリ アクセスは重要です。変数が適切にアライメントされていない場合、CPU はデータの読み取りまたは書き込みに複数のメモリ アクセスを必要とし、パフォーマンスの低下につながる可能性があります。データを整列させることにより、コンパイラは効率的なメモリ アクセスを保証します。

構造体のメモリアラインメントのルール

  • フィールドのアライメント: 各フィールドのアドレスは、その型のアライメント要件を満たしている必要があります。コンパイラは、適切な位置合わせを確保するためにフィールド間にパディング バイトを挿入する場合があります。
  • 構造体のアライメント: 構造体のサイズは、フィールド間の最大のアライメント要件の倍数である必要があります。

例:

type User struct {
  Username    string
  Email       string
  SignInCount int
  IsActive    bool
}

出力: 12

分析:

  • a は int8 で、1 バイトを占有し、1 にアライメントされます。
  • b は int32 であり、4 バイトへのアライメントが必要です。コンパイラは、a と b の間に 3 つのパディング バイトを挿入して、b のアドレスを 4 に揃えます。
  • c は int8 で、1 バイトを必要としますが、構造体の合計サイズは 4 の倍数 (最大のアライメント要件) である必要があります。コンパイラは最後に 3 つのパディング バイトを追加します。

メモリアライメントの最適化

構造体フィールドを再配置してパディングを最小限に抑え、メモリ使用量を削減できます。

user1 := User{
  Username:    "alice",
  Email:       "alice@example.com",
  SignInCount: 1,
  IsActive:    true,
}

出力: 8

この最適化されたバージョンでは、b が最初に配置され、4 バイトに配置されます。 a と c が連続して配置されるため、合計サイズは 8 バイトとなり、最適化されていないバージョンよりもコンパクトになります。

まとめ

  • Go の構造体フィールドは、アライメント要件に基づいてメモリが割り当てられ、潜在的なパディングバイトも含まれます。
  • フィールドの順序を調整すると、パディングを最小限に抑え、メモリ使用量を最適化できます。
  • 構造体の実際のメモリ サイズを決定するには、unsafe.Sizeof を使用します。

入れ子構造と構成

Go では、ネストされた構造体と合成は、コードの再利用と複雑なデータの整理のための強力なツールです。ネストされた構造体を使用すると、構造体に別の構造体をフィールドとして含めることができるため、複雑なデータ モデルを作成できます。一方、コンポジションでは、他の構造体を含めることで新しい構造体が作成され、コードの再利用が容易になります。

入れ子になった構造体

ネストされた構造体を使用すると、1 つの構造体に別の構造体をフィールドとして含めることができます。これにより、データ構造がより柔軟で整理されたものになります。以下はネストされた構造体の例です:

type User struct {
  Username    string
  Email       string
  SignInCount int
  IsActive    bool
}

構造構成

合成により、複数の構造体を新しい構造体に結合できるため、コードの再利用が可能になります。合成では、構造体に他の複数の構造体をフィールドとして含めることができます。これは、より複雑なモデルを構築し、共通のフィールドやメソッドを共有するのに役立ちます。以下は構造体の構成例です:

user1 := User{
  Username:    "alice",
  Email:       "alice@example.com",
  SignInCount: 1,
  IsActive:    true,
}

ネストされた構造体と構成の違い

  • ネストされた構造体: 構造体を結合するために使用されます。ある構造体のフィールドの型は別の構造体です。このアプローチは、階層関係を持つデータ モデルを記述するためによく使用されます。
  • Composition: 構造体に他の複数の構造体のフィールドを含めることができます。このメソッドはコードの再利用を実現するために使用され、構造体がより複雑な動作と属性を持つことができるようになります。

まとめ

ネストされた構造体と合成は、複雑なデータ構造の整理と管理に役立つ Go の強力な機能です。データ モデルを設計するとき、ネストされた構造体と構成を適切に使用すると、コードがより明確になり、保守しやすくなります。

空の構造体

Go の空の構造体は、フィールドのない構造体です。

サイズとメモリアドレス

空の構造体はメモリのゼロバイトを占有します。ただし、そのメモリ アドレスは、状況によっては等しい場合もあれば、等しくない場合もあります。メモリエスケープが発生すると、アドレスは等しく、runtime.zerobase.
を指します。

user2 := User{
  Username: "bob",
}

出力から、変数 a、b、および zerobase は同じアドレスを共有し、すべてグローバル変数 runtime.zerobase (runtime/malloc.go) を指しています。

脱出シナリオについて:

  • 変数 c と d はヒープにエスケープされます。それらのアドレスは 0x590d00 であり、等しい (true) と比較されます。
  • 変数 e と f は異なるアドレス (0xc00008ef47) を持ち、比較が等しくありません (false)。

この動作は Go では意図的なものです。空の構造体変数がエスケープされない場合、それらのポインタは等しくありません。エスケープ後、ポインタは等しくなります。

空の構造体を埋め込む場合のスペース計算

空の構造体自体はスペースを占有しませんが、別の構造体に埋め込まれると、その位置に応じてスペースを消費する可能性があります。

  • それが構造体の唯一のフィールドである場合、構造体はスペースを占有しません。
  • それが 最初または中間フィールド である場合、スペースは占有されません。
  • それが最後のフィールドの場合、前のフィールドと同じスペースを占有します。
user3 := &User{
  Username: "charlie",
  Email:    "charlie@example.com",
}

空の構造体が配列またはスライスの要素である場合:

type User struct {
  Username string
  Email    string
}

func (u User) PrintInfo() {
  fmt.Printf("Username: %s, Email: %s\n", u.Username, u.Email)
}

アプリケーション

空の構造体のサイズがゼロのプロパティにより、追加のメモリ オーバーヘッドなしでさまざまな目的に使用できます。

キーなしの構造体の初期化を防止する

type User struct {
  Username    string
  Email       string
  SignInCount int
  IsActive    bool
}

セットデータ構造の実装

user1 := User{
  Username:    "alice",
  Email:       "alice@example.com",
  SignInCount: 1,
  IsActive:    true,
}

チャネルを介した信号伝送

場合によっては、チャネルを通じて送信されるデータの内容は無関係で、信号としてのみ機能することがあります。たとえば、空の構造体はセマフォ実装で使用できます:

user2 := User{
  Username: "bob",
}

私たちは Leapcell で、Go プロジェクトをクラウドにデプロイするための第一の選択肢です。

Deep Dive into Go Struct

Leapcell は、Web ホスティング、非同期タスク、Redis 用の次世代サーバーレス プラットフォームです:

  1. 多言語サポート
  • JavaScript、Python、Go、または Rust を使用して開発します。
  1. 無制限のプロジェクトを無料でデプロイ
  • 使用料金のみお支払いください。リクエストや料金はかかりません。
  1. 比類のないコスト効率
  • アイドル料金なしの従量課金制です。
  • 例: $25 は、平均応答時間 60 ミリ秒で 694 万のリクエストをサポートします。
  1. 効率化された開発者エクスペリエンス
  • 直感的な UI でセットアップが簡単です。
  • 完全に自動化された CI/CD パイプラインと GitOps の統合。
  • 実用的な洞察を得るリアルタイムのメトリクスとログ。
  1. 簡単な拡張性と高いパフォーマンス
  • 自動スケーリングにより、高い同時実行性を簡単に処理できます。
  • 運用上のオーバーヘッドはゼロです。構築だけに集中してください。

ドキュメントでさらに詳しく見てみましょう!

X でフォローしてください: @LeapcellHQ


ブログをお読みください

以上がGo 構造体の詳細の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。