C++ Learning

第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"
Employee bob
name = "Bob"
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 のやり方を標準とします。

どんなときに使う?

ここまでで 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();

いつ使う?

ただし乱用注意: 全部 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())を実現するために fg は何を返せばいい?

void
*this(自分自身への参照)
this(ポインタ)
新しいオブジェクト
メソッドチェーンには自分自身への参照Foo&)を返すのが定石。return *this; と書きます。ポインタ(this)を返すと呼び出し側で -> を使う形になり不格好。

Q4. static メンバ変数について正しいのは?

オブジェクトごとに個別の値を持つ
const が必須
クラスに 1 つだけ存在し、全オブジェクトで共有
必ず public でなくてはいけない
static メンバ変数はクラスに 1 つ。a.n_b.n_ は同じものを指します。アクセス制御(public/private)は自由に付けられ、const とも関係ありません。