Cpp クラス メンバ関数ポインタ

提供:yonewiki
2022年9月26日 (月) 11:14時点におけるYo-net (トーク | 投稿記録)による版 (ページの作成:「C++に戻る <table class="mbox-small" style="border:1px solid #aaa; background-color:#f9f9f9; width:22em;" id="RealTitleBanner"> <tr> <td style="width:1px;"></td> <td class="mbox-text plainlist" style="">本来の表記は「<b><span id="RealTitle" style="font-size:large;">C++(Cpp) クラス メンバ関数ポインタ</span></b>」です。この記事に付けられた題名は{{記事名の制約}}から不正確なものと…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)

C++に戻る


本来の表記は「C++(Cpp) クラス メンバ関数ポインタ」です。この記事に付けられた題名はテンプレート:記事名の制約から不正確なものとなっています。

※このページではC++にのみ存在する機能として、記事タイトルがC++ クラス メンバ関数ポインタになっています。

クラス メンバ関数ポインタ

メンバ関数ポインタは関数ポインタの拡張的な考え方だと思えば良いです。関数ポインタと全く同じではないので、使い方を覚えないと駄目です。といっても関数ポインタ自体もそれほど活用されているという風には思わない技術ですが、使ってる人は使ってるし、これをうまく使いこなす頭のいい人もいるのは確かです。というわけで、このクラスのメンバ関数ポインタも一応、抑えておきます。


関数ポインタと異なるのは、名前解決スコープ演算子(::)を使わないと駄目そうだということです。それは関数名を定義するときにも名前解決スコープ演算子を使わないといけないので、なんとなく予想できることです。でも具体的にはどうするべきなのか?ということになります。


具体的には以下のようになります。


FunctionPointer001.cpp

#include "stdafx.h"
#include "FunctionPointer001.h"

void CFunctionPointer001::example_function1(int n,int m){
 printf("example_function1 %d %d\n",n, m);
}

CFunctionPointer001::CFunctionPointer001(void)
{
  void (CFunctionPointer001::*pfuncExample1)(int,int);//★1.メンバ関数ポインタ変数宣言
  pfuncExample1 = &CFunctionPointer001::example_function1;//★2.変数に関数のアドレスを代入

  //pfuncExample1 = example_function1;//ダメな例:これだとダメ

  (this->*pfuncExample1)(100,200);//★3.呼び出し
}

CFunctionPointer001::~CFunctionPointer001(void)
{
}

FunctionPointer001.h

#pragma once

class CFunctionPointer001
{
public:
  CFunctionPointer001(void);
  void example_function1(int n,int m);
  ~CFunctionPointer001(void);
};

メインプログラムソース sample_main.cpp

#include "stdafx.h"
#include "FunctionPointer001.h"

int _tmain(int argc, _TCHAR* argv[])
{
  printf("★メイン関数アドレス\n");
  printf("%08x\n",_tmain);
  printf("\n");

  CFunctionPointer001* CFunctionPointer001_Instance = new CFunctionPointer001;
  return 0;
}

FunctionPointer001.cppの10/11/15行目がメンバ関数ポインタを実際に利用しようとしている部分になります。サンプルを作ると、どうしてもどこが重要なのかわかり辛くなってしまうのは申し訳ないです。


コメント文に★印を入れました。★1.部分のように名前解決をするためにポインタ変数名の前にクラス名を入れたり、★2.部分の代入のときにもアドレス演算子付きで名前解決演算子とともに関数名を指定したり、★3部分の呼び出しでもthisポインタによってポインタの指し示す内容がメンバ関数であり名前解決をすることを実施するような構文になっています。thisポインタについても別の記事で詳細は解説の予定ですが、クラスが変数として定義されたり、ポインタによってクラス変数が実体化されたときに自分自身のアドレスを保持する特別な変数がthisポインタです。このthisというキーワードのポインタ変数に実体化された時のアドレスが格納されています。★3.部分のように記述しなければメンバ関数を呼び出せないのは、通常省略できるthisポインタによる指定が必須になるあたりは、なんだかややこしく感じますが、他の通常のポインタと区別する部分であったり、クラス独自の名前解決を必要とする都合を考えればこうするしかなさそうなルールです。あえて、置き換えれる部分があるとしたら★3.部分はアロー演算子を利用しているだけなので


(*this.*pfuncExample1)(100,200);


とすることはできます。他にも★2.部分の代入処理を変数宣言時に初期化という形でまとめることもできます。そうすると★2.部分は不要で、★1.部分がいかのように書き換えられます。


void (CFunctionPointer001::*pfuncExample1)(int,int)= &CFunctionPointer001::example_function1;


★3.部分の関数の引数の前のカッコや★1.部分の宣言時における引数部の前のカッコは必ず必要になります。あとは余計なカッコとかをつけると文法上の扱いに問題がでるため大きく変更できる部分はないです。おぼえにくい部分ですが、こういったややこしいメンバ関数ポインタとマクロを組み合わせたり、typedefステートメントで型定義の置き換えを組み合わせたりすることもよく実施されるので、更にややこしさが倍増する表現が増えます。自分のような知能の低い人間は、できるだけ、こういう複雑な記述をさける方向でプログラムするのですが、頭の構造が違う人はかなり駆使される人もいるので、理解するのに無駄に時間がかかってしまいます。


どういう風に記述するかは、人がきめることだし、文法上は便利だから用意されているものなので、どういうプログラムが正しいとか、そういうのはありませんし、なんでもありです。せめて人が書いたプログラムを理解できる程度に頭を鍛えるしかないです。


C++に戻る