閉じる

C++のstructとclassの違いを解説!使い分けの基準と唯一の相違点

C++において、データをまとめるための「構造体」と「クラス」は非常によく似た存在です。

C言語の経験がある方や、他のオブジェクト指向言語からC++に移行してきた方にとって、この2つの使い分けは最初に直面する疑問の一つかもしれません。

実はC++におけるstructとclassの機能的な違いは、デフォルトのアクセス権限という一点のみに集約されます。

しかし、その背景にある設計思想や現代的な使い分けの基準を理解することは、読みやすく安全なコードを書く上で極めて重要です。

本記事では、この唯一の違いから具体的なコード例、そしてプロフェッショナルが現場で実践する使い分けのガイドラインまでを詳しく解説します。

structとclassの根本的な違い

C++におけるstruct(構造体)とclass(クラス)の機能的な相違点は、驚くほどシンプルです。

それは「デフォルトのアクセス修飾子」が何かという点に尽きます。

メンバ変数とメンバ関数へのアクセス権

構造体内で定義された変数や関数は、特に指定がない限りすべて公開(public)として扱われます。

これに対して、クラス内で定義されたものはすべて非公開(private)として扱われます。

この仕様は、C言語との互換性を保つために構造体がオープンな設計を引き継いでいる一方で、クラスはカプセル化というオブジェクト指向の原則を重視しているために生じたものです。

以下のコードで、このアクセス権の違いを確認してみましょう。

C++
#include <iostream>
#include <string>

// 構造体の定義
struct PersonStruct {
    std::string name; // デフォルトでpublic
    int age;          // デフォルトでpublic
};

// クラスの定義
class PersonClass {
    std::string name; // デフォルトでprivate
    int age;          // デフォルトでprivate

public:
    // privateなメンバにアクセスするためのセッター
    void setData(std::string n, int a) {
        name = n;
        age = a;
    }

    void display() {
        std::cout << "Name: " << name << ", Age: " << age << std::endl;
    }
};

int main() {
    // 構造体のインスタンス化
    PersonStruct s;
    s.name = "Alice"; // 直接アクセス可能
    s.age = 25;       // 直接アクセス可能
    std::cout << "[Struct] " << s.name << " (" << s.age << ")" << std::endl;

    // クラスのインスタンス化
    PersonClass c;
    // c.name = "Bob"; // コンパイルエラー!privateメンバには直接アクセス不可
    c.setData("Bob", 30); // 公開メソッド経由で設定
    c.display();

    return 0;
}
実行結果
[Struct] Alice (25)
Name: Bob, Age: 30

このコードからわかるように、structは外部から自由に中身を操作できるのに対し、classは明示的にpublic:と記述しない限り、外部からのアクセスを拒絶します。

この「隠蔽性の初期値」の違いこそが、両者を分かつ最大のポイントです。

継承における挙動の違い

もう一つの細かな違いは、継承時のデフォルトアクセスレベルにあります。

C++ではクラスや構造体を継承して新しい型を作ることができますが、ここでもデフォルトの挙動が異なります。

構造体の継承(public継承)

構造体をベースにして別の型を定義する場合、継承のアクセス修飾子を省略すると自動的にpublic継承となります。

これは「親の公開メンバは子の公開メンバとして引き継ぐ」という、最も一般的な継承の形態です。

クラスの継承(private継承)

一方で、クラスをベースにして定義を行う際、アクセス修飾子を省略すると自動的にprivate継承となります。

private継承では、親クラスの公開メンバであっても、子クラスの外部からはアクセスできなくなります。

これは「実装の詳細を隠しつつ再利用する」という特殊な用途で使われますが、一般的な「is-a関係」の継承を意図する場合は、明示的にpublicキーワードを書く必要があります。

C++
#include <iostream>

class Base {
public:
    int value = 100;
};

// structはデフォルトでpublic継承
struct DerivedStruct : Base {
    // 省略しても public Base と同じ
};

// classはデフォルトでprivate継承
class DerivedClass : Base {
    // 省略すると private Base と同じ
};

int main() {
    DerivedStruct ds;
    std::cout << ds.value << std::endl; // アクセス可能

    DerivedClass dc;
    // std::cout << dc.value << std::endl; // コンパイルエラー!
    return 0;
}

このように、継承においてもstructは「開放的」、classは「閉鎖的」という一貫した設計思想が見て取れます。

どちらを使うべきか?使い分けの基準

技術的には、structの中にprivate:と書けばクラスと全く同じ振る舞いをさせることができますし、逆にclassの中にpublic:と書けば構造体のように振る舞わせることもできます。

しかし、エンジニア間の暗黙の了解として、「意図」に基づいた使い分けが存在します。

structを使うべき場面:データの集まり(POD)

structは、主に「単純なデータの保持」を目的とする場合に使用します。

これらは「Plain Old Data (POD)」とも呼ばれ、外部から値を直接読み書きしても問題ないような、軽量なデータ構造に適しています。

  • 座標情報(x, y, z)
  • 設定値のパラメータセット
  • 通信パケットのフォーマット定義
  • 関数から複数の戻り値を返したい場合の一時的な型

classを使うべき場面:振る舞いを持つオブジェクト

classは、「状態(データ)と振る舞い(関数)をカプセル化する」必要がある場合に使用します。

内部状態を勝手に書き換えられると不都合が生じる場合や、複雑なロジックを隠蔽したい場合に適しています。

  • データベース接続管理(接続状態を管理し、Open/Closeメソッドを持つ)
  • ゲームのキャラクター(HPや攻撃力などのステータスを持ち、計算処理を内部で行う)
  • GUIのウィジェット(描画ロジックやイベントハンドリングを持つ)
特徴structclass
デフォルトアクセスpublicprivate
デフォルト継承public継承private継承
主な用途データの保持(POD)オブジェクト指向(カプセル化)
設計思想開放的・シンプル隠蔽的・堅牢

現代的なC++での使い分けの考え方

現代のC++(C++11以降)では、より型安全で効率的なコードを書くために、structclassのどちらを使っても、コンストラクタやデストラクタ、演算子オーバーロードなどの機能をフルに活用できます。

不変条件(Invariant)の有無

使い分けに迷った際、最も有力な判断基準となるのが「不変条件(インバリアント)」の有無です。

不変条件とは、「そのデータ構造が常に満たしていなければならないルール」のことです。

例えば、日付を表す構造を考えたとき、「月は1から12の間でなければならない」というルールがあります。

もしこれをstructにしてメンバ変数を公開してしまうと、外部から「13月」という不正な値を代入されるリスクが生じます。

このような場合はclassを使い、セッター関数の中で値の妥当性をチェック(バリデーション)すべきです。

逆に、単なる2次元の座標点のように「xもyもどんな数値をとっても構わない」という場合は、structで簡潔に定義するのがスマートです。

C++
// どんな値でも許容されるため struct が適している
struct Point {
    double x;
    double y;
};

// 内部状態に厳格なルールがあるため class が適している
class Time {
private:
    int hour;
    int minute;

public:
    void setTime(int h, int m) {
        // 0-23, 0-59 の範囲内かチェック
        if (h >= 0 && h < 24 && m >= 0 && m < 60) {
            hour = h;
            minute = m;
        } else {
            // エラー処理
        }
    }
};

テンプレートメタプログラミングでの利用

C++のテンプレートプログラミングの文脈では、structが多用されます。

これは、メタ関数の結果を保持するvaluetypeといったメンバを、public:と書かずに簡潔に公開できるためです。

標準ライブラリのstd::is_integralなどの型特性(Type Traits)も、その多くがstructで実装されています。

まとめ

C++におけるstructclassは、言語仕様上の違いこそ「デフォルトのアクセス権限」のみですが、その背後には重要な設計意図の違いが含まれています。

  1. structは「データの集まり」を表現し、すべてのメンバがデフォルトで公開される。
  2. classは「自律的なオブジェクト」を表現し、カプセル化のためにデフォルトで非公開になる。
  3. 継承においても、structは公開継承、classは非公開継承がデフォルトとなる。
  4. 使い分けの鍵は不変条件の保持とバリデーションの必要性にある。

プログラムの動作自体はどちらを使っても同じにできますが、コードを読む他のエンジニア(あるいは未来の自分)に「この型はどのように扱われるべきか」というメッセージを伝えるために、適切な方を選択することが大切です。

単純なデータの入れ物ならstruct、複雑なロジックや状態管理を伴うならclassという原則を心に留めておきましょう。

クラスの定義と基本

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!