C++のインスタンスメンバの関数ポインタ

解決


関数ポインタ天国  2008-12-19 06:54:50  No: 69285

C++で関数ポインタを勉強中です。それで色々試して以下のソースのような
ことをしていたところ、コンソールに表示される関数のアドレスが同じ
ことに気づきました。

インスタンスを作るごとにメンバ変数、関数ともに作られると思って
いましたが、もしやメンバ関数は変数と違いどのインスタンスでも同じもの
で、インスタンス関数を呼び出すのは、それ自体が関数ポインタ呼び出し
のようなもの?なのでしょうか?

また、static付のメンバ関数との違いは?と混乱している状態ですので、
ここに質問させていただきました。
すみませんがこの辺りを教えてもらえないでしょうか。

class CSample
{
protected:
  typedef void ( CSample::*FPSAMPLE )();
  static FPSAMPLE table[];

  void func1();
public:
  void func();
};

void CSample::func1()
{
  _tprintf( _T( "%p\n" ), &CSample::func1 );
}

void CSample::func()
{
  FPSAMPLE func = table[ 0 ];

  ( this->*func )();
}

CSample::FPSAMPLE CSample::table[] = {
  &CSample::func1,
};

int _tmain(int argc, _TCHAR* argv[])
{
  CSample sample1, sample2;

  sample1.func();
  sample2.func();

  return 0;
}


επιστημη  URL  2008-12-19 15:15:10  No: 69286

コンパイラがどんな機械語を吐くかはコンパイラの勝手なので"必ず"というわけではないけれど:

> メンバ関数は変数と違いどのインスタンスでも同じもので、

YES. メンバ関数がインスタンスごとに異ならなければならない理由がない。

> インスタンス関数を呼び出すのは、それ自体が関数ポインタ呼び出し
のようなもの?なのでしょうか?

仮想関数の呼び出しについては YES.
仮想関数は実行時に適切な関数を呼び分ける必要があるため、
インスタンスごとに飛び先が異なる。

> static付のメンバ関数との違いは?

static関数はその呼び出しにthisを伴わない。


仲澤@失業者  2008-12-19 19:22:58  No: 69287

>インスタンスを作るごとにメンバ変数、関数ともに作られると思って
>いましたが、・・・(引用者省略)

メンバ変数はインスタンス毎です。っつうか、
クラスのインスタンスとはクラス名で修飾された
メンバ変数の集合体と思ったほうが吉。

メンバ関数については、そんな非効率なことはしませんよ。
全インスタンスで共用されるのが普通です。
しかし、もちろん例外があります。

>もしやメンバ関数は変数と違いどのインスタンスでも同じもの
>で、インスタンス関数を呼び出すのは、それ自体が関数ポインタ呼び出し
>のようなもの?なのでしょうか?

微妙(笑)。しかし、確実に言えるのは。
「staticでないクラスメンバ関数のポインタを利用してはならない」
ということです。なぜなら、staticでないクラスメンバ関数は

1.複数のインスタンスで共用されるように配置される場合。
2.利用された場所にインライン展開され、関数ですら無くなる場合。
3.しかも、上記2種が混載されちまう場合(笑)。
4.επιστημηさんの指摘のように、virtual
    つまり、「暫定的」に実装されていて、
    実行時にならないとどこに行くやらさっぱり不明な場合。

等のケースがあり、コンパイラの判断に任されてしまいます。
従って、当然そのポインタなどに何の意味もありません。
もちろん実装方法とキーワードである程度コントロール
することもできますが・・・。

一方staticなメンバ関数とは、
「全インスタンス及び、クラス外から共用することを強制されている」
もので、インスタンスが一つも存在していなくても関数の実体を持ち、
かつ、機能しなければなりません。
従って、その性質上、自クラスのstaticなメンバ(関数/変数)にしか、
アクセスできないわけです。当然ですね。


επιστημη  URL  2008-12-19 19:38:35  No: 69288

>「全インスタンス及び、クラス外から共用することを強制されている」

これはどーかしら。
private な staticメンバとかあるですから。

「this(すなわちインスタンス)を必要としないメンバ関数」に一票。


仲澤@失業者  2008-12-19 22:36:15  No: 69289

>private な staticメンバとかあるですから。

ぁ゛・・・ orz


関数ポインタ天国  2008-12-20 06:13:56  No: 69290

回答ありがとうございます。
C++でも関数ポインタテーブルを使えば、継承を使うよりずっと
コードを書く量が減るのではないのかと思い色々試していました。
回答を見る限り、static付きより危険性を持っているようなので、
使用は見合わせようと思います。


επιστημη  URL  2008-12-22 18:14:58  No: 69291

> static付きより危険性を持っているようなので、
> 使用は見合わせようと思います。

危険性と言いますと?


maru  2008-12-22 21:03:06  No: 69292

仲澤@失業者 さんに質問ですけど、
> 「staticでないクラスメンバ関数のポインタを利用してはならない」
とは mem_fun も使用してはいけないという事でしょうか?
仮想関数に適用しても正しく動作しているように思えますが。
# 勘違いかな。


tetrapod  2008-12-22 23:30:19  No: 69293

仲澤@失業者さんはメンバ関数へのポインタを知らないのでは?

ISO/IEC 14882:1998 において
5.2.2:Function Call と 5.3.1 Unary operators と、どちらにおいても
> 「staticでないクラスメンバ関数のポインタを利用してはならない」
そんな旨の記載は無いので規格上問題ないっす。
仮想関数やインライン関数へのポインタをとっても、とったポインタ経由で
呼び出しを行っても、まったく問題ないっす。
# インライン展開されるかどうかは別として。
規格非合致な処理系はさておき。

struct testbase {
    inline virtual void func() { std::cout << "base\n"; }
};
struct test : testbase {
    inline virtual void func() { std::cout << "derived\n"; }
};

int main() {
    test t;
    t.func();
    void (testbase::*p)() = &testbase::func;
    (t.*p)();
    return 0;
}

元発言者さんが static と非 static の違いをあっさり流しているほうが
よっぽど危険だと思う。この両者の違いはクラス設計の根幹にかかわる
ほどの違いなわけで、「ようなので」とか「見合わせよう」とか、
そういう一言で片付けられるような代物ではないっつーことで。


yoh2  2008-12-24 00:34:41  No: 69294

最初の投稿内容に気になる点が。

> _tprintf( _T( "%p\n" ), &CSample::func1 );

非staticメンバへのポインタは、「ポインタ」という名前ではありますが、
いわゆる普通のポインタと全く別物。相互に変換ができないものなので、
%pで出力するのは不適切です。

非staticメンバ関数へのポインタはvoid*のサイズを超えることがある
(少なくともVC++では超える)ので、無理矢理%pで出力させようとすると、
メンバ関数へのポインタの一部しか表示されなかったり、後ろにさらに
引数があった場合には、フォーマットとの対応付けがずれたりと切ないことに。


関数ポインタ天国  2008-12-25 07:50:31  No: 69295

>元発言者さんが static と非 static の違いをあっさり流しているほうが
>よっぽど危険だと思う。この両者の違いはクラス設計の根幹にかかわる
それほど重要だったのとは理解できていませんでした。
結局static付のメンバ関数はインスタンスに関わらず、使えると言う事
は分かりましたが、static付の関数の使用用途が考えれば考えるほど
不明にもなってきました。

>非staticメンバ関数へのポインタはvoid*のサイズを超えることがある
>(少なくともVC++では超える)ので、
自分でも調べたところ確かにポインタのサイズがC言語と違い
常に同じではないのですね。ありがとうございます。


tetrapod  2008-12-25 18:15:41  No: 69296

struct person {
  static int howmany_persons;
  date birthday;
  string firstname;
  string lastname;
  person(const date& bd, const string& fn, const string& ln) :
    birthday(bd), firstname(fn), lastname(ln) {
    ++howmany_persons;
  }
  ~person() { --howmany_persons; }
  static int get_howmany_persons() { return howmany_persons; }
  int get_age(date d=get_todays_date());
};
とか書いたら static と non-static の違いがわかるかしら?

non-static は「ブツ(この場合人)を特定しないと原理的に意味の無いデータや処理」
static は「ブツ(この場合人)によらないデータや処理」
ブツを特定する = インスタンスを特定する = this を指定する
よってこの両者が互換になることは基礎設計レベルでありえない。


関数ポインタ天国  2008-12-26 05:08:43  No: 69297

>static は「ブツ(この場合人)によらないデータや処理」
>ブツを特定する = インスタンスを特定する = this を指定する
例まで示していただいてありがとうございます。
参考になります。なるほど特定するかどうかですか。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加