クラスと構造体の選択は、プログラムの意味とパフォーマンスに直結します。値として扱うべきか、参照で共有すべきかを判断するための実務的な指針と具体例を示します。
November 15, 2025 (4mo ago) — last updated March 18, 2026 (26d ago)
クラスと構造体の使い分けガイド
値型と参照型の違い、メモリとパフォーマンスの影響、言語別の注意点と実践ルールを具体例で解説します。
← Back to blog
クラスと構造体の使い分けガイド
概要: 値型と参照型の違い、メモリとパフォーマンスの影響、実践的な設計ルールを実例とともに解説します。
はじめに
クラスと構造体を選ぶ判断は単なる構文の差ではなく、プログラムの意味と挙動に直接影響します。重要なのは、値として扱いたいか、参照で共有したいかです。本記事では、値セマンティクスと参照セマンティクスの違いをわかりやすく説明し、言語別の注意点、パフォーマンスに関する実務的な指針、そして設計上の簡潔なルールを提示します。
核心の区別:値セマンティクス vs 参照セマンティクス
言語のキーワードを取り払うと、問題は値型か参照型かの違いになります。構造体はコピーされる値、つまり誰かに渡すと相手は自分のコピーを操作します。これが値セマンティクスで、安全に独立した操作ができます。クラスは共有ドキュメントのように、参照を渡すと同じインスタンスを複数が操作します。これが参照セマンティクスで、同一性と共有状態が重要になります。
主な違い(概観)
| 特性 | 構造体(値型) | クラス(参照型) |
|---|---|---|
| データの扱い | 渡されるとコピーされる | 参照(ポインタ)が渡される |
| メモリ割り当て | インラインまたはスタック上 | ヒープ上に割り当てられる |
| ライフサイクル | 短命でローカル | 長寿命で共有される |
| 同一性 | データの等価性で判断 | オブジェクト固有の同一性がある |
| 継承 | 通常継承はない | 継承とポリモーフィズムをサポート |
| 主な用途 | 小さく自己完結した値 | 振る舞いを持つ複雑な実体 |
これらの違いは、レイテンシ、メモリ使用、正確性、並行性に実務的な影響を与えます。意図的に選べばコードは予測可能で保守しやすくなります。
メモリ割り当てがパフォーマンスに与える影響
スタックとヒープの違いが、しばしばパフォーマンス差の主要因になります。
スタック:高速で予測可能
スタックはLIFOの領域で、関数ローカルデータがプッシュ/ポップされます。スタック上の割り当ては非常に安価です。小さな構造体はスタックや配列の要素として扱われるため、割り当てと解放がほとんどコストを生みません。
ヒープ:柔軟だがコストがある
ヒープはオブジェクトを関数境界を越えて保持できますが、ヒープ割り当ては遅く、ガベージコレクション(GC)や手動解放のオーバーヘッドを伴います。参照型は追加の間接参照を導入します。頻繁なヒープ割り当てはGCのプレッシャーを高め、マネージドランタイムでは停止を引き起こす可能性があります1。
配列や連続メモリに格納された構造体はキャッシュ局所性が高く、特にデータ指向なループでは大きな性能改善をもたらすことがあります2。
C# の例
// Value Type - often lives inline
public struct PointStruct {
public int X;
public int Y;
}
// Reference Type - object lives on the heap
public class PointClass {
public int X;
public int Y;
}
public void ProcessPoints() {
PointStruct p1 = new PointStruct { X = 10, Y = 20 };
PointClass p2 = new PointClass { X = 10, Y = 20 };
}
密なループ内で小さなオブジェクトを何千回もヒープ割り当てすると、GCの活動が顕著に増えることがあります。負荷の高いコードでは割り当て戦略を見直し、プロファイル結果に基づいて最適化してください12。
言語ごとの扱いと実務的な違い
言語ごとに推奨されるプラクティスは異なります。以下は主要言語の要点です。
C++:struct と class の違いは主に慣習
C++では、struct と class のキーワードに機能的差はほとんどありません。デフォルトのアクセス修飾が異なるだけです。プレーンなデータ集約には struct、カプセル化や複雑な振る舞いには class を使うのが一般的です3。
C#:値型と参照型の明確な区別
C# では struct が値型、class が参照型です。構造体は小さく不変な値(座標や色)に向き、クラスは同一性や共有される可変状態を表現します。
Swift:値型を優先する設計
Swift は値型を推奨します。Apple のガイドラインとコミュニティでは、デフォルトで struct を使い、共有可変状態や Objective-C との相互作用が必要な場合に class を選択することが勧められています4。
Rust:所有権と借用による安全性
Rust は struct と所有権・借用モデルでメモリ安全性を保証します。振る舞いは impl で追加し、コンパイル時に所有権ルールを強制するため、多くの共有状態バグをランタイム前に防げます5。
struct Player {
username: String,
level: u32,
is_active: bool,
}
impl Player {
fn level_up(&mut self) {
self.level += 1;
}
}
構造体を選ぶべき典型例
型が小さく自己完結していて、同一性より値としての扱いが適切なら構造体が有利です。典型的な候補は次のとおりです:
- 幾何学的な点(Point2D)
- 色の値(RGB/RGBA)
- 小さな設定ペイロード
- 軽量な計算入力
利点はヒープ割り当ての削減、キャッシュ局所性の向上、マネージドランタイムでのGCプレッシャー低減です。特にデータ中心のループでは性能差が出やすい点に注意してください2。
クラスを選ぶべき典型例
オブジェクトが同一性を持ち、共有される可変状態や継承が必要ならクラスが適切です。典型的な候補は次のとおりです:
- ユーザープロファイルやドメインエンティティ
- データベースやネットワーク接続オブジェクト
- 状態を調整するサービスやマネージャー
クラスは継承とポリモーフィズムにより複雑な振る舞いを表現しやすく、オブジェクト指向パターンと相性が良いです。
意思決定チェックリスト
| 検討項目 | 構造体を使う(値) | クラスを使う(参照) |
|---|---|---|
| 同一性 | データ自体が同一性を示す | オブジェクトに固有の同一性がある |
| 可変性 | 不変、または局所的な変更のみ | 共有される可変状態がある |
| 振る舞い | 単純なロジック | 複雑な相互作用やライフサイクル |
| ライフサイクル | 短くローカル | 長くアプリ全体で共有 |
| 共有 | コピーして安全 | 参照で共有が必要 |
よくある質問(技術的)
構造体にメソッドは持てますか?
持てます。C#、Swift、Rust のような現代言語は構造体にメソッドやイニシャライザ、プロトコルやインターフェース適合を許可します。主な違いはコピーと渡され方です。
構造体は常に速いですか?
いいえ。小さな構造体はヒープ割り当てされたオブジェクトより優れることが多いですが、大きな構造体はコピーコストが高くなります。変更前に実際のワークロードでプロファイルしてください。
構造体は継承をサポートしますか?
通常はしません。多くの言語では構造体がインターフェースやプロトコルを実装でき、継承なしでも柔軟な設計が可能です。
実践的なQ&A(簡潔セクション)
Q1: パフォーマンスを理由に構造体にすれば常に速くなりますか?
A1: いいえ。小さく不変な値には構造体が有利ですが、大きな構造体はコピーコストで遅くなることがあります。必ずプロファイルしてください。
Q2: 並行処理で構造体はバグを減らしますか?
A2: はい。値セマンティクスは意図しない共有ミューテーションを防ぎ、並行性に関連するバグを減らすことが多いです。
Q3: いつクラスから構造体へリファクタすべきですか?
A3: 型が小さく不変で同一性が不要ならリファクタを検討してください。ヒープ割り当てを減らし、値セマンティクスを明確にすることでパフォーマンスと安全性が向上します。
At Clean Code Guy, we help teams refactor for scalability and maintainability. Learn more at https://cleancodeguy.com.
AIがコードを書きます。あなたがそれを長持ちさせます。
AI加速の時代において、クリーンコードは単なる良い実践ではありません—スケールするシステムと自らの重みで崩壊するコードベースの違いです。