C++メンバ関数ポインタの使い道を徹底解説!クラスの動きを操るプロの技
生徒
「先生、以前教わった『関数ポインタ』は便利でしたが、クラスの中にある関数(メンバ関数)もポインタで指し示すことはできるんですか?」
先生
「もちろん可能です!それを『メンバ関数ポインタ』と呼びます。ただし、普通の関数ポインタとは少しだけ書き方が違うんですよ。」
生徒
「普通の関数と何が違うんでしょうか?」
先生
「メンバ関数は『クラス』という設計図に基づいた『オブジェクト(実体)』がないと動かせない、というルールがあるからです。今日はその仕組みを、パソコン初心者の方にも分かりやすく説明しますね!」
1. メンバ関数ポインタとは?
C++のプログラミングでは、データと処理をひとまとめにした「クラス」という仕組みを使います。このクラスの中に定義された関数のことを「メンバ関数」と呼びます。
メンバ関数ポインタは、その「クラスの中にある特定の動き」の住所を保存するための変数です。 普通の関数ポインタが「独立した一軒家の住所」を指すとすれば、メンバ関数ポインタは「マンションの特定の部屋にある設備(キッチンや風呂)の使い方」を指すようなものです。 設備を使うには、「どの部屋(オブジェクト)の設備か」を指定する必要がありますよね?この「どの部屋か」をセットで扱うのが、メンバ関数ポインタの最大の特徴です。
2. なぜメンバ関数ポインタが必要なの?
パソコンを触ったことがない方にとって、「なぜわざわざポインタで関数を呼び出すの?」と不思議に思うかもしれません。 例えば、ゲームのキャラクターを想像してください。 「攻撃する」「守る」「魔法を使う」というボタンがあるとします。
もし、選択されたキャラクターや状況に応じて「ボタンを押した時の動き」を柔軟に切り替えたい場合、プログラムを直接書き換えるのではなく、「実行する動きの住所(ポインタ)」だけをすり替える方が圧倒的に効率的です。 メンバ関数ポインタを使いこなせば、「状況に合わせてクラスの動きを自由自在にスイッチする」という高度なプログラムが作れるようになります。
3. メンバ関数ポインタの宣言と代入
メンバ関数ポインタの書き方は少し複雑に見えますが、パターンを覚えれば大丈夫です。
キーワードは、クラス名と ::* という記号です。
戻り値の型 (クラス名::*ポインタ名)(引数の型);
実際に、ロボットの動きを制御するシンプルな例を見てみましょう。
#include <iostream>
// ロボットの設計図(クラス)
class Robot {
public:
void walk() {
std::cout << "ロボットが歩きます。" << std::endl;
}
void run() {
std::cout << "ロボットが走ります!" << std::endl;
}
};
int main() {
// 1. メンバ関数ポインタを宣言する
// 「Robotクラスの、戻り値なし(void)、引数なし」の関数を指すポインタ
void (Robot::*action)();
// 2. 関数の住所を代入する(&をつけるのがルール)
action = &Robot::walk;
// 3. 実際に使うには「ロボットの実体(オブジェクト)」が必要
Robot myRobot;
// 特殊な記号 (.*) を使って呼び出す
(myRobot.*action)();
return 0;
}
実行結果:
ロボットが歩きます。
4. オブジェクトを指すポインタと一緒に使う
C++では、オブジェクト自体もポインタ(住所)で扱うことがよくあります。
「オブジェクトのポインタ」を使ってメンバ関数ポインタを呼び出すときは、->* という矢印のような記号を使います。
#include <iostream>
class Speaker {
public:
void say(int volume) {
std::cout << "音量 " << volume << " で話します。" << std::endl;
}
};
int main() {
// メンバ関数ポインタの準備(引数に int を取る関数)
void (Speaker::*func)(int) = &Speaker::say;
// オブジェクトのポインタを準備
Speaker mySpeaker;
Speaker* ptrSpeaker = &mySpeaker;
// オブジェクトポインタ経由で、メンバ関数ポインタを実行!
(ptrSpeaker->*func)(80);
return 0;
}
実行結果:
音量 80 で話します。
5. 応用:状況に応じた機能の切り替え
メンバ関数ポインタが本当に輝くのは、「どの関数を呼ぶかを配列などで管理する」ときです。 例えば、リモコンの「1ボタン」ならこれ、「2ボタン」ならこれ、という設定をリスト化できます。
#include <iostream>
class GamePlayer {
public:
void attack() { std::cout << "斬りつける!" << std::endl; }
void defense() { std::cout << "盾を構える!" << std::endl; }
};
int main() {
GamePlayer player;
// メンバ関数ポインタの配列を作る
void (GamePlayer::*actions[])() = { &GamePlayer::attack, &GamePlayer::defense };
int input;
std::cout << "行動を選んでください(0:攻撃, 1:防御): ";
std::cin >> input;
if (input == 0 || input == 1) {
// 入力に応じて実行する関数を切り替える
(player.*actions[input])();
}
return 0;
}
6. 注意すべきポイント
メンバ関数ポインタを扱う際に、初心者がハマりやすいポイントがいくつかあります。
- カッコを忘れない:
(myRobot.*action)()の最初のカッコは省略できません。優先順位の関係で、これがないと正しく動きません。 - 静的(static)メンバ関数は別物:
staticがついたメンバ関数は、普通の関数ポインタと同じ扱いになります。クラス名::*は使いません。 - 型の不一致: 引数の数や型、戻り値が少しでも違うとエラーになります。設計図をよく確認しましょう。
7. 現代のC++での代替案
実は、今のC++(C++11以降)では、std::function や std::bind といった、より読みやすく安全な仕組みが用意されています。
しかし、それらの便利な道具の内部では、今回学んだメンバ関数ポインタが頑張って動いています。
「根本的な仕組み」を知っていることは、バグの原因を探したり、より高速なプログラムを書いたりする際に、必ずあなたの強力な武器になります。
ポインタは「住所」を扱うもの。メンバ関数ポインタは「クラスという建物の中にある機能への案内図」です。 このイメージを持って、少しずつ練習していきましょう!