はじめに
C++のクラスは、オブジェクトの「設計図」です。この設計図は、メンバと呼ばれる様々な要素から構成されます。メンバを大きく分類すると、データを保持するための「メンバ変数」、操作を定義する「メンバ関数」、そしてクラス固有の型を定義する「メンバ型」の3つに分けられます。
これらのメンバを適切に設計し、後述するアクセス制御を組み合わせることで、安全で再利用性の高い、カプセル化されたクラスを構築できます。この記事では、これらのクラスメンバの全貌を網羅的に解説します。
1. メンバ変数 (Member Variables)
オブジェクトの状態(データ)を保持します。
非静的メンバ変数
最も一般的なメンバ変数で、オブジェクトごとに個別の値を持ちます。
class Player {
public:
// C++11以降、非静的メンバ変数は宣言時に直接初期化できる
std::string name = "名無し";
int level = 1;
};
静的メンバ変数 (static
)
static
で宣言された変数は、特定のオブジェクトに属さず、そのクラス全体で共有されます。
class GameSession {
public:
static int active_players; // 宣言
GameSession() { active_players++; }
~GameSession() { active_players--; }
};
// クラスの外で定義と初期化が必要
int GameSession::active_players = 0;
mutable
指定子
const
メンバ関数(オブジェクトを変更しないと約束した関数)内で、例外的に変更を許可したいメンバ変数に mutable
を付けます。
class DataCache {
private:
mutable int access_count = 0;
public:
std::string getData() const {
access_count++; // const関数内でもmutableなメンバは変更可能
return "some_data";
}
};
2. メンバ関数 (Member Functions)
オブジェクトの振る舞い(操作)を定義します。
コンストラクタ
オブジェクトが生成される際に自動で呼ばれる初期化用の関数。
class Widget {
private:
int id_;
std::string name_;
public:
// デフォルトコンストラクタ
Widget() : id_(-1), name_("default") {}
// 引数付きコンストラクタ
explicit Widget(int id) : id_(id), name_("custom") {}
// 委譲コンストラクタ
Widget(int id, std::string name) : id_(id), name_(name) {}
Widget(std::string name) : Widget(0, name) {} // 他のコンストラクタに処理を委譲
};
explicit
:Widget w = 1;
のような暗黙の型変換を禁止します。
デストラクタ
オブジェクトが破棄される際に自動で呼ばれる後片付け用の関数。~クラス名()
という名前です。
コピー/ムーブ操作
- コピーコンストラクタ:
Widget w2 = w1;
- コピー代入演算子:
w2 = w1;
- ムーブコンストラクタ:
Widget w3 = std::move(w1);
- ムーブ代入演算子:
w3 = std::move(w2);
= default
/ = delete
コンパイラが自動生成できる特殊なメンバ関数について、
= default
: コンパイラに自動生成を明示的に依頼する。= delete
: そのメンバ関数の生成を禁止する(コピー禁止など)。
struct NonCopyable {
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
静的メンバ関数 (static
)
static
で宣言されたメンバ関数は、クラス全体に関連付けられ、クラス名::関数名()
のようにオブジェクトなしで呼び出せます。
型変換演算子
operator 型名()
という形式で定義し、自作クラスを他の型に変換できるようにします。
struct Switch {
bool is_on = false;
// Switchをboolに変換できるようにする
explicit operator bool() const { return is_on; }
};
Switch s;
if (s) { /* ... */ } // explicitなので if(s) のような文脈でのみ有効
3. メンバ型 (Member Types)
クラスのスコープ内で、新しい型を定義したり、既存の型に別名を付けたりします。
クラス内クラス(ネストしたクラス)
class Outer {
public:
struct Inner { int value; };
private:
Inner inner_data;
};
// Outer::Inner inner_obj; のように外部からもアクセス可能
型の別名宣言 (using
)
template<typename T>
class MyContainer {
public:
// T型に value_type という別名を付ける (typedef int value_type; とほぼ同じ)
using value_type = T;
};
4. アクセス制御
メンバへのアクセス権限を制御します。
指定子 | 説明 |
public | 公開: どこからでもアクセス可能。クラスのインターフェース。 |
protected | 限定公開: そのクラスと、その子クラスの内部からのみアクセス可能。 |
private | 非公開: そのクラスの内部からのみアクセス可能。実装の詳細を隠蔽する。 |
friend | 友達: friend 宣言された外部の関数やクラスに、private メンバへのアクセスを特別に許可する。 |
まとめ
クラスは、これらの「メンバ変数」「メンバ関数」「メンバ型」という部品と、「アクセス制御」の仕組みを組み合わせることで、一つの完成した「モノ」として設計されます。それぞれのメンバの役割を理解し、適切に組み合わせることが、オブジェクト指向プログラミングの鍵となります。