January 29, 2026 (3mo ago)

クラス vs 構造体: 開発者のためのパフォーマンスガイド

クラスと構造体の核心的な違いを解き明かします。C#、Swift、C++などで高パフォーマンスかつクリーンなコードを書くために、どちらをいつ使うべきかを学びましょう。

← Back to blog
Cover Image for クラス vs 構造体: 開発者のためのパフォーマンスガイド

クラスと構造体の核心的な違いを解き明かします。C#、Swift、C++などで高パフォーマンスかつクリーンなコードを書くために、どちらをいつ使うべきかを学びましょう。

クラス vs 構造体: 開発者のためのパフォーマンスガイド

要約: クラスと構造体の核心的な違いを解き明かします。C#、Swift、C++などで高パフォーマンスかつクリーンなコードを書くために、どちらをいつ使うべきかを学びましょう。

はじめに

クラスと構造体の核心的な違いはシンプルだが決定的です:クラスは参照型であり、構造体は値型です。この区別がメモリ配置、コピーの振る舞い、実行時パフォーマンスを左右します(C#、C++、Swiftなどの言語で)。どちらを選ぶかを理解することは、予測可能なコードと高性能なシステムを作る上で不可欠です。

根本的な違いの理解

クラスをインスタンス化すると、保持する変数はヒープ上のオブジェクトを指す参照になります。その変数をコピーすれば参照がコピーされ、複数の参照が同じオブジェクトを指すことになり、ある参照を介して行った変更は他の参照からも見えます。この参照セマンティクスが、識別性を持つエンティティにクラスが使われる主要な理由です。1

構造体はデータそのものを表します。構造体を作成すると、値の具体的な束(多くの場合スタックや配列のインラインに格納される)が生成され、構造体をコピーすると独立した複製が得られます。複製を変更しても元には影響しないため、構造体は単純で不変な値に適しています。1

カプセル化とオブジェクト設計の詳細については、オブジェクト指向カプセル化に関するガイドを参照してください: https://cleancodeguy.com/blog/object-oriented-encapsulation.

Diagram illustrating Structs stored on the Stack and Classes referenced from the Stack to the Heap.

クイック比較: 参照型 vs 値型

特性クラス(参照型)構造体(値型)
メモリの位置ヒープ;ポインタで参照されるオブジェクト。スタックまたはインライン;変数自体がデータ。
代入オブジェクトではなく参照をコピーする。値全体をコピーする。
ライフタイムガベージコレクション(または一部言語では手動削除)で管理される。スコープを抜けるかインラインに格納されると破棄される。
識別性 vs 値識別性を持つ;複数の参照が同一インスタンスを指す可能性がある。値を表す;等価はしばしばデータに基づく。

共有される識別性が必要ならクラスを使いましょう。副作用なくコピー可能な単純で自己完結した値が必要なら構造体を使いましょう。

この基礎が、ヒープ対スタック割り当て、キャッシュ局所性、ガベージコレクション負荷といったより深いパフォーマントレードオフを導きます。以下で詳述します。

メモリ割り当てが速度を決める仕組み

メモリ配置はCPU効率、スループット、レイテンシに影響します。クラスへのアクセスは通常間接参照を伴います:スタック上のポインタがヒープ上のデータを参照します。その余分なルックアップはコストを増し、キャッシュ挙動を悪化させることがあります。構造体は変数が存在する場所に直接格納されるため、その間接参照を回避しやすく、より密なメモリ配置でキャッシュ局所性を向上させることがよくあります。1

Diagram comparing Struct/Stack and Class/Heap memory allocation, highlighting contiguous memory, garbage collection, and speed differences.

ガベージコレクションのコスト

ヒープ上のオブジェクトはガベージコレクションの対象になります。GCサイクルは実行を一時停止させ得るため、リアルタイムや高スループットのシステムではレイテンシが増すことがあります。短命なクラスオブジェクトを頻繁に割り当てるとGC負荷とCPUオーバーヘッドが増えます。多数の小さく短命なオブジェクトに対して値型を使うと、ヒープの churn とGC作業を削減できます。3

ヒープ割り当ては潜在的なGCオーバーヘッドを伴います。構造体はボックス化されず値ベースのままであればそのオーバーヘッドを回避できます。

これはスケールと応答性を考慮したシステムで特に重要です—割り当てを減らすことがGC活動の直接的な削減となり、実行時パフォーマンスの平滑化につながります。

キャッシュ局所性とスループット

現代のCPUはキャッシュに依存しています。構造体の配列のような連続したレイアウトはキャッシュヒットとスループットを改善します。各クラスインスタンスを別々にヒープ割り当てするとデータがメモリ中に散在し、キャッシュミスが増えて処理が遅くなります。タイトなループやデータ処理パイプラインでは、連続した値のレイアウトが大きな利点となります。5

ボクシングの罠

ボクシングは値型が参照型に変換されるときに発生します(例:オブジェクトを期待するコレクションに格納される場合)。ボクシングはヒープオブジェクトを割り当て値をそこにコピーするため、構造体のパフォーマンス上の利点を打ち消し、GC負荷を増やします。ボクシングを避けることは効率的な値型使用の核心原則です。4

言語ごとの差異:C#、C++、Swift

言語ごとに慣習や機能に違いがあります。言語固有のルールを知っておかないと、一つの言語でのルールを別の言語にそのまま適用してしまう危険があります。

Diagram comparing C#, C++, and Swift programming language characteristics, focusing on reference vs value types and object flexibility.

C#:明確な参照型と値型のモデル

C#では、class = 参照型、struct = 値型です。識別性を持つエンティティ(例:Customer や DatabaseConnection)にはクラスを使い、小さく不変な値(例:Point、Color)には構造体を使いましょう。構造体を小さく不変に保つことで、微妙なバグやコピーオーバーヘッドを避けられます。1

よくある誤りは、大きすぎるまたは可変な構造体を作ることです;どちらも驚くべきバグやパフォーマンス低下を招きます。C#で最適化する際は、不変で小さな構造体というガイドラインに従ってください。

C++:言語制約より慣習

C++では、structとclassの構文上の差はデフォルトのアクセシビリティだけです。どちらもスタックやヒープに割り当てられ、メソッドを持ち、継承をサポートします。慣習としては、単純なデータ集約にはstruct、カプセル化されたオブジェクトやRAII資源管理にはclassを使います。

この柔軟性は、C++開発者が値/参照の区別を言語に頼らず慣習と設計選択に依存する必要があることを意味します。ポリモーフィズムや継承パターンのガイダンスについては、C++設計ノートを参照してください: https://cleancodeguy.com/blog/polymorphism-vs-inheritance.

Swift:デフォルトで値志向

Swiftはほとんどのカスタム型に対して構造体を優先することを推奨します。Swiftの構造体はメソッド、拡張、プロトコル準拠をサポートしており、強力で安全なデフォルトになります。参照セマンティクスや識別性、Objective-Cとの相互運用が必要な場合にのみクラスを選択してください。2

この値優先の設計は、不変性とデータフローの理解を容易にし、特に並行コードで役立ちます。

最大効率のために構造体を選ぶべき場合

構造体は、その識別性が完全に値によって定義される小さく不変なデータの束に最適です。典型的な例:

  • 幾何データ:Point2D や RGBColor
  • 金融値:Money(金額+通貨)
  • 高スループットパイプラインで使われる小さなDTO

実用的なサイズガイドラインは「16–32バイト」ルールです:構造体のフィールドが概ねその範囲に収まるなら、コピーコストは控えめであり、ヒープ割り当てより安価なことが多いです。構造体がそれより大きくなるか可変にする必要がある場合は、クラスの方が適切な選択でしょう。5

Guide on choosing structs for small, value-type data like RGB, 2D Point, and Money, with a 16-byte guideline.

不変性とサイズルール

  • 不変な構造体を優先する:値は一度作成され、変更ではなく置き換えによって扱うべきです。
  • 構造体は小さく保つ:大きな構造体を頻繁にコピーすると、参照を渡すより高コストになることがあります。

これらのルールは(可変コピーによる)無音のバグや(過剰コピーやボックス化による)パフォーマンスの落とし穴を回避するのに役立ちます。

よくある落とし穴とリファクタリング

二つの一般的な問題は可変構造体と過度のボクシングです。

可変構造体は、変更がコピーにしか影響しないため驚くべき挙動を招きます。可変構造体は、新しいインスタンスを返す不変構造体へリファクタリングしましょう。

ボクシングは多くのAPIやコレクションで暗黙に発生します;ボクシングのホットスポットを特定して除去することで構造体のパフォーマンス利点を保ちます。

例:可変な Point を不変な構造体にリファクタリングする(C#)

// PITFALL: Mutable struct public struct MutablePoint { public int X { get; set; } public int Y { get; set; }

public void Move(int dx, int dy)
{
    X += dx;
    Y += dy;
}

}

// REFACTOR: Immutable struct public readonly struct ImmutablePoint { public int X { get; } public int Y { get; }

public ImmutablePoint(int x, int y)
{
    X = x;
    Y = y;
}

public ImmutablePoint MovedBy(int dx, int dy)
{
    return new ImmutablePoint(X + dx, Y + dy);
}

}

このリファクタリングは意図を明確にし、偶発的な状態破壊を排除します。クリーンコーディングの実践については、原則ガイドを参照してください: https://cleancodeguy.com/blog/clean-coding-principles.

よくある質問(簡潔なQ&A)

Q1: いつクラスより構造体を優先すべきですか?

A: 型が小さく不変で、識別性ではなく値を表す場合に構造体を優先してください。ポイント、色、または小さなDTOのような単純なデータに構造体は向いています。

Q2: どんなパフォーマンスの落とし穴に注意すべきですか?

A: 可変構造体、大きすぎる構造体(コピーコスト)、およびヒープオブジェクトへのボクシングを避けてください—これらは値型の利点を無効化し、パフォーマンスを損なう可能性があります。

Q3: 言語差は選択にどう影響しますか?

A: 言語の慣習に従ってください:C#は値型と参照型を明確に区別します;C++は慣習に依存します;Swiftはデフォルトで値型を好みます。プラットフォームのルールを学んでからクロスランゲージのパターンを適用しましょう。12


Clean Code Guy では、これらの原則を実際のコードベースに適用するお手伝いをしています。弊社のコードベースクリーンアップとAI対応リファクタはソフトウェアをより速く、安全に、保守しやすくします。詳細は https://cleancode.com をご覧ください。

1.
Microsoft Docs, “Choosing between classes and structs,” https://learn.microsoft.com/en-us/dotnet/standard/choosing-between-class-and-struct
2.
Apple Developer Documentation, “Structures and Classes,” https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
4.
5.
NDepend Blog, “Class vs Struct in C#: Making Informed Choices,” https://blog.ndepend.com/class-vs-struct-in-c-making-informed-choices/
← Back to blog
🙋🏻‍♂️

AIがコードを書きます。
あなたがそれを長持ちさせます。

AI加速の時代において、クリーンコードは単なる良い実践ではありません—スケールするシステムと自らの重みで崩壊するコードベースの違いです。