インスタンスメンバと静的メンバ
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 27/104。
これまで、作成したすべてのメンバ変数は特定のオブジェクトに属していました。2つの Player オブジェクトを作成すると、それぞれが独自の name と health を持ちます。これらはインスタンスメンバと呼ばれ、クラスのインスタンスごとに個別に存在します。
しかし、すべてのオブジェクトで共有されるデータが必要な場合はどうすればよいでしょうか?例えば、合計で何人のプレイヤーが存在するかを追跡する場合などです。
ここで静的メンバが登場します。静的メンバは、特定のオブジェクトではなく、クラス自体に属します。
class Player {
std::string name; // インスタンスメンバ - 各プレイヤーが個別に保持します
int health; // インスタンスメンバ
static int playerCount; // 静적メンバ - すべてのプレイヤーで共有されます
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // プレイヤーが作成されるたびにインクリメントされます
}
~Player() {
playerCount--; // プレイヤーが破棄されるたびにデクリメントされます
}
};主な違いは、インスタンスメンバはオブジェクトを通じてアクセスされるのに対し、静的メンバはクラス名自体を通じてアクセスできる点です。インスタンスメンバを「オブジェクトごとに1つ」、静的メンバを「クラスごとに1つ」と考えてください。
| インスタンスメンバ | 静的メンバ |
|---|---|
| 各オブジェクトに固有 | すべてのオブジェクトで共有される |
| オブジェクトが作成されたときに作成される | オブジェクトが作成される前から存在する |
オブジェクト経由でアクセス: player.health | クラス経由でアクセス: Player::playerCount |
静的メンバは、カウンタや設定値、あるいは個々のオブジェクトではなく論理的にクラスに属するあらゆるデータに役立ちます。
チャレンジ
簡単インスタンスメンバと静的メンバを使用して、個々の製品の詳細と在庫全体の統計の両方を追跡する製品在庫システムを構築しましょう。
コードを整理するために2つのファイルを作成します:
Product.h: 個々の製品を管理しながら、在庫全体のデータも追跡するProductクラスを定義します。クラスには以下のものを含める必要があります:- インスタンスメンバ:
name(string) とprice(double) — 各製品に固有 - 静的メンバ
totalProducts: 現在存在する製品の総数をカウントします - 静的メンバ
totalValue: すべての製品の合計価格を追跡します - コンストラクタ: 名前と価格を受け取り、インスタンスメンバを初期化し、両方の静的カウンタを更新します(カウントをインクリメントし、合計価格に価格を加算します)
- デストラクタ: 製品が削除されたときに静的カウンタを更新します(カウントをデクリメントし、合計価格から価格を減算します)
getInfo()メソッド:"<name>: "<name>: $<price>"lt;price>"という形式の文字列を返します- 静的メソッド
getInventoryStats():"Products: <count>, Total Value: "Products: <count>, Total Value: $<value>"lt;value>"という文字列を返します
- インスタンスメンバ:
main.cpp: インスタンスメンバが固有である一方で、静的メンバがすべてのオブジェクト間でどのように共有されるかを実証します。入力から2つの製品名とその価格(name1, price1, name2, price2 — それぞれ別々の行)を読み取り、以下の処理を行います:- クラス名を使用して初期の在庫統計を出力します:
Product::getInventoryStats() - 波括弧を使用してスコープブロックを作成し、その中で最初の名前と価格を使用して
Productを作成します。その情報と現在の在庫統計を出力します。 - ブロックが終了した後(最初の製品が破棄された後)、
"After first scope:"に続いて在庫統計を出力します。 - 2番目の名前と価格を使用して別の
Productを作成します。その情報と在庫統計を出力します。 - 名前が
"Bonus"で価格が5.00の3つ目のProductを作成します。最終的な在庫統計を出力します。
- クラス名を使用して初期の在庫統計を出力します:
静的メンバは、ヘッダーファイル内のクラス定義の外(クラス定義の後)で定義することを忘れないでください。価格は <iomanip> の std::fixed と std::setprecision(2) を使用して、小数点以下2桁でフォーマットしてください。
main.cpp で #include "Product.h" を使用してヘッダーファイルをインクルードしてください。
チートシート
C++は、**インスタンスメンバ**と**静的メンバ**の2種類のクラスメンバをサポートしています。
**インスタンスメンバ**は各オブジェクトに固有であり、すべてのインスタンスが独自のコピーを保持します。
class Player {
std::string name; // 各プレイヤーは独自の名前を持ちます
int health; // 各プレイヤーは独自の体力を持ちます
};**静的メンバ**はクラスのすべてのオブジェクト間で共有されます。クラス自体に属するコピーは1つだけです。
class Player {
static int playerCount; // すべてのプレイヤーで共有されます
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // 共有カウンターをインクリメントします
}
~Player() {
playerCount--; // 破棄時にデクリメントします
}
};**主な違い:**
| インスタンスメンバ | 静的メンバ |
|---|---|
| 各オブジェクトに固有 | すべてのオブジェクト間で共有 |
| オブジェクトが作成されたときに作成される | オブジェクトが作成される前から存在する |
オブジェクト経由でアクセス: player.health | クラス経由でアクセス: Player::playerCount |
**静的メソッド**はクラス名を使用して呼び出すことができ、静的メンバにのみアクセスできます。
class Player {
static int playerCount;
public:
static int getPlayerCount() {
return playerCount; // 静的メンバにのみアクセス可能
}
};
// クラス名を使用して呼び出す
int count = Player::getPlayerCount();静的メンバはクラスの外で定義する必要があります(通常は.cppファイル内、またはヘッダー内のクラス定義の後)。
int Player::playerCount = 0;静的メンバは、カウンター、共有設定値、または個々のオブジェクトではなく論理的にクラスに属するデータに役立ちます。
自分で試してみよう
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// 入力を読み込む
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: Product::getInventoryStats() を使用して初期の在庫統計を表示する
// TODO: 波括弧を使用してスコープブロックを作成する
// ブロック内:
// - name1 と price1 を使用して Product を作成する
// - getInfo() を使用してその情報を表示する
// - 現在の在庫統計を表示する
// TODO: ブロック終了後、"After first scope:" と表示する
// その後、在庫統計を表示する
// TODO: name2 と price2 を使用して別の Product を作成する
// その情報と在庫統計を表示する
// TODO: 名前が "Bonus" で価格が 5.00 の3つ目の Product を作成する
// 最終的な在庫統計を表示する
return 0;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタ引数付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ詳解Rule of Three / Five / Zeroまとめ - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () 演算子のオーバーロード型変換演算子まとめ:Matrix クラス