第26回 this と静的メンバ
メンバ関数の中で「自分自身のオブジェクト」を指すthis ポインタと、オブジェクトごとでなくクラスに 1 つだけ存在する static メンバ。前者は「誰が呼ばれたか」、後者は「クラス全体の状態」。両方を可視化で理解します。
このページで押さえること
✅ 最低限ここだけ覚える
this は「今呼ばれたメンバ関数のオブジェクトへのポインタ」
- メンバ関数内で
x_ と書くと this->x_ と同じ
static メンバは全オブジェクトで共有
- インスタンス数カウントなど、クラス全体の情報に使う
⭐ 余裕があれば読む
return *this; でメソッドチェーン
- static メンバ変数の定義は .cpp 側に必要(C++17 の inline 変数で省略可)
- 静的メンバ関数には
this がない
- シングルトンパターン
1. まず触ってみる ― this の正体
obj.method() と呼ぶとき、コンパイラは裏で method(&obj) のようなものに変換しています。つまり「どのオブジェクトに対して呼ばれたか」を隠し引数として渡しているのです。その隠し引数が this。
▶ 2 つの Counter を操作
Counter a
count_ = 0
this = 0x1000
Counter b
count_ = 0
this = 0x2000
ボタンを押すと、どちらのオブジェクトが変わったか観察できます。
最小コード
first_this.cpp最小例
class Counter {
int count_ = 0;
public:
void inc() {
++count_; // this->count_ と同じ
}
int value() const { return count_; }
};
Counter a, b;
a.inc(); // 裏で Counter::inc(&a) のようなもの → a.count_ が +1
a.inc(); // a のみ +1
b.inc(); // b のみ +1
// a.count_ == 2, b.count_ == 1 (独立している)
ここまでで覚えること(2 つ):
- メンバ関数内の
x_ は暗黙的に this->x_(省略できる)
- 各オブジェクトは独立したメンバ変数を持つ
よくある素朴な疑問
Q. this って常に使える?
→ 非 static メンバ関数の中でのみ使えます。static メンバ関数(§5)には「どのオブジェクトか」がないので this がありません。
Q. 引数名とメンバ名が同じときは?
→ this-> を明示的に書いて区別。例:void set_x(int x) { this->x = x; }
2. this ポインタ
this は自分自身のオブジェクトを指すポインタ。型は「そのクラスへのポインタ」。const メンバ関数の中では「const クラスへのポインタ」になります。
this の型基本
class Foo {
public:
void f() { /* this: Foo* */ }
void g() const { /* this: const Foo* */ }
};
引数名との衝突this で区別
class Point {
int x, y;
public:
void set(int x, int y) {
this->x = x; // this-> で明示
this->y = y;
}
};
こっそりした暗黙変換
メンバ関数内で単に x_ と書くと、コンパイラは自動で this->x_ に変換しています。つまり:
同じ意味暗黙
void inc() {
++count_; // ← 明示すると以下と同じ
++this->count_;
}
3. メソッドチェーン return *this
this は「自分自身へのポインタ」、*this は「自分自身のオブジェクト」。メンバ関数から *this を返すと、呼び出しを繋げて書けるようになります(メソッドチェーン)。
Builder パターンチェーン
class StringBuilder {
std::string s_;
public:
StringBuilder& add(const std::string& t) {
s_ += t;
return *this; // 自分自身を返す
}
std::string build() const { return s_; }
};
std::string result = StringBuilder{}
.add("Hello, ") // チェーンして呼べる
.add("World")
.add("!")
.build();
// "Hello, World!"
STL の std::cout << ... << ... も、operator<< が *this を返しているのでチェーンできます。
4. static メンバ変数
static を付けたメンバ変数は、クラス全体で たった 1 つ だけ存在します。どのオブジェクトからも同じものを参照します。
🏢 Company クラス(static メンバ)
static int total_employees_ = 0;
↑ クラスに 1 つだけ。全オブジェクトで共有
Employee alice
name = "Alice"
static メンバ total_employees_ はオブジェクト数が増えるたびに増えます。
実装
Employee.hstatic メンバ
class Employee {
std::string name_;
public:
// クラスに 1 つだけの共有変数
static inline int total_ = 0; // C++17: inline でその場で定義可
Employee(std::string n) : name_(std::move(n)) {
++total_; // クラス共有カウンタを +1
}
~Employee() { --total_; }
};
// アクセスはクラス名::
std::cout << Employee::total_;
C++14 以前の static メンバ変数: クラス内では宣言のみ、.cpp 側で定義が必要でした(int Employee::total_ = 0;)。C++17 で inline 変数が入り、クラス内で初期化を完結できるように。本サイトでは C++17 のやり方を標準とします。
どんなときに使う?
- オブジェクトの総数・生成 ID などクラス全体の集計
- 定数(
static const int MAX = 100;)
- シングルトンパターン(全体で 1 つしか存在しないインスタンス)
- ログ用の共有リソース
⭐
ここまでで this と static の基本は OK
最後の §5 は「静的メンバ関数」。用途が似ているのでセットで。
5. static メンバ関数
static をメンバ関数に付けると、特定のオブジェクトに紐付かない関数になります。言い換えるとthis がないメンバ関数。
静的メンバ関数this なし
class MathUtil {
public:
static int square(int x) {
return x * x;
// this は使えない(オブジェクトなし)
}
};
// オブジェクト不要で呼べる
std::cout << MathUtil::square(5); // 25
static 変数を操作典型用途
class Counter {
static inline int count_ = 0;
public:
static int get() { return count_; }
static void increment() { ++count_; }
static void reset() { count_ = 0; }
};
Counter::increment();
std::cout << Counter::get();
いつ使う?
- クラスに関係するがインスタンスに依存しない処理(
Math::sqrt 的な)
- static メンバ変数を操作する処理
- シングルトンの
instance() 取得関数
- ファクトリ関数(
Foo::create())
ただし乱用注意: 全部 static だらけのクラスは「ただの namespace の代わり」になってしまうことがあります。本当にオブジェクト指向として設計する意義があるか、よく検討しましょう。C++ には namespace もあるので、単なる関数群なら namespace で十分。
確認クイズ
this と static を 4 問で確認。
Q1. this が使えないのは次のどれ?
非 const メンバ関数の中
const メンバ関数の中
コンストラクタの中
static メンバ関数の中
static メンバ関数は特定のオブジェクトに紐付かないため、this を持ちません。コンストラクタ内では this が使える(ただし初期化の途中なので注意)。
Q2. 次のコードの出力は?
class Counter { static inline int n_ = 0; public: Counter(){ ++n_; } static int count(){ return n_; } };
Counter a, b, c;
std::cout << Counter::count();
0
3
1
コンパイルエラー
static メンバ n_ はクラスに 1 つだけ共有される変数。a, b, c の 3 つが作られる過程でコンストラクタが 3 回呼ばれ、n_ は 3 に。
Q3. メソッドチェーン(obj.f().g().h())を実現するために f と g は何を返せばいい?
void
*this(自分自身への参照)
this(ポインタ)
新しいオブジェクト
メソッドチェーンには自分自身への参照(Foo&)を返すのが定石。return *this; と書きます。ポインタ(this)を返すと呼び出し側で -> を使う形になり不格好。
Q4. static メンバ変数について正しいのは?
オブジェクトごとに個別の値を持つ
const が必須
クラスに 1 つだけ存在し、全オブジェクトで共有
必ず public でなくてはいけない
static メンバ変数はクラスに 1 つ。a.n_ と b.n_ は同じものを指します。アクセス制御(public/private)は自由に付けられ、const とも関係ありません。