4

私は、メンバー関数を通常の関数の特殊なケースとして考えることに慣れています。メンバー関数には、「this」ポインター、つまりメンバー関数が置かれるオブジェクトのパラメーターリストの先頭に追加のパラメーターがあります。行動することになっています。過去にこの方法で boost::function を使用しましたが、問題に遭遇したことはありません:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

しかし、メンバー関数ポインターの次の構文を見てきました。

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

この構文では、'this' パラメータは表示されません。内部では、メンバーへのポインター関数は本当に別の獣であり、そのブーストは私のために詳細を処理していたのではないかと思いました。

「this」パラメーターの配置について標準は何を規定していますか? おそらく、私のコンパイラでは余分な 'this' 引数が最初に来て、他のコンパイラでは最後になるのでしょうか? 私の考え方がコンパイラ (GCC4、VS2005) の処理方法と一致しているのは幸運でしょうか? メンバー関数へのポインターは常に、追加のパラメーターを持つ関数へのポインターの特殊なケースにすぎませんか、それともコンパイラーはそれらを別の方法で実装できますか?

4

9 に答える 9

9

ポインターは、メンバーへのthisポインターに沿って格納されません (メンバー関数ポインターは、この特殊なケースです)。あなたがするだけなら

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

保存されるのは、後で提供する必要があるオブジェクトでどのメンバー関数を呼び出す必要があるかという情報だけです。それを呼び出したい場合は、コンパイラがthisポインタを取得するオブジェクトを渡す必要があります。

MyObject o; (o.*f)(1, 2);

メンバー関数ポインターは、(指されている) 型が関数型であるメンバー ポインターです。標準では、メンバー関数ポインターには、それらが指す独自の「メンバー関数型」がなく、何らかの形で this ポインター型が含まれると述べています。

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

funそのコードでは関数型です。「通常の」関数が持つタイプ。メンバー関数ポインターとは対照的に、関数へのポインターは、その型を持つ関数への単なるポインターです。

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

メンバー関数へのポインターには、その関数型の上に追加のメンバーポインターレベルがあります。

ポインターと、それが指しているオブジェクトとの関係についての何かthis(技術的ではなく、理論的なもの):

各メンバー関数には、 const または非 const メンバー関数があるかどうかに応じて、implicit object parameter型を持つ、MyObject&またはという隠しパラメーターがあります。MyObject const&メンバー関数を呼び出すオブジェクトoimplied object argument、パラメーターに渡される です。メンバー関数の呼び出し方法を記述する規則を構成する標準の理論では、暗黙的なオブジェクト パラメーターは最初の隠しパラメーターです。これは概念的なものであり、実装における実際のケースであるという意味ではありません。次に、暗黙のオブジェクト引数がその暗黙のオブジェクト パラメーターにバインドされ、暗黙の変換が発生する可能性があります (そのため、非 const オブジェクトで const メンバー関数を呼び出すと、修飾変換が からMyObjectに変換されます)。MyObject const&. これが、非 const オブジェクトの場合、非 const 関数を呼び出すよりも非 const 関数の方が適している理由です)。たとえば、次のコードで言うことができます。

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

type の暗黙のオブジェクト引数aが typeAの暗黙のオブジェクト パラメーターにバインドされA const&、そのオブジェクトがここthisで型を持つポインターによってポイントされるA const*こと。注意すべき重要なことは、暗黙的なオブジェクト パラメーターは理論上の構成要素にすぎず、メンバー関数を呼び出すための規則がどのように構成されているかを形式化する (そしてコンストラクターにはそれらが含まれていない) 一方で、 this ポインターは実際に存在するということです。が導入されたとき、C++ にはまだ参照がなかったthisので、 はポインターです。this

それが問題を理解するのに役立つことを願っています。

于 2009-01-04T15:34:34.393 に答える
9

標準では、ポインターを配置する場所についてはほとんど何も述べていません。this実際、メンバー関数に対して異なる呼び出し規則を使用することはかなり一般的です。(したがって、'this' ポインターは単なる追加の最初の引数ではなく、実際には最初の引数とは異なる場所に格納されます)

特に、MSVC はthiscallメンバー関数などの呼び出し規約を使用しstdcallます。 http://www.hackcraft.net/cpp/MSCallingConventions/#thiscallはそれらの違いを説明していますがthiscallthisポインタはECXレジスタにstdcall格納され、すべてのパラメータはスタックに格納されることに注意してください。

それらを完全に異なるタイプとして扱う方が間違いなく優れています。メンバー関数へのポインターは、追加のパラメーターを持つ関数への単なるポインターではありません。

于 2009-01-04T15:15:26.427 に答える
6

メンバー関数ポインターに関する優れた記事は、CodeProjectのメンバー関数ポインターと可能な限り最速のC++デリゲートです。この記事では、単純なケースから多重継承を持つ仮想メンバー関数ポインターまでのメンバー関数ポインターについて説明します。ボーナスとして、それは本当に役立つことができるデリゲートの実装を提供します。

于 2009-01-04T22:59:15.913 に答える
3

メンバー関数へのポインターのサイズは、コンパイラーによって異なる場合があることに注意してください。

The Old New Thingブログに書かれているように、もう1つ注意すべき点があります。

メンバー関数へのポインタのサイズは、クラスによって変わる可能性があります。

于 2009-01-04T23:34:36.793 に答える
3

はい、関数へのポインターとメンバーへのポインターは完全に異なる獣です。->*メンバーへのポインターには、 or.*演算子を使用して逆参照されるオブジェクト インスタンスを指定する必要があります。は、メンバーへのポインターが使用されるときに決定されるため (またはの左側のオブジェクト)、メンバーthisへのポインターを作成するときに使用されるパラメーターはありません。this->*.*

メンバーへのポインター関数とメンバーへのポインター変数の違いは、メンバーへのポインター関数と通常の関数ポインターの違いよりもおそらく少ないことに注意してください。

通常、メンバー関数と通常の関数は完全に異なる呼び出し規則を持つ可能性があるため、それらの間でキャストすることはできません。

于 2009-01-04T15:16:50.700 に答える
1

それらは間違いなく異なるタイプであり、あなたが行う仮定はプラットフォーム/コンパイラ固有のものになります。

このページには、多数の一般的なコンパイラの実装の詳細を含む、メンバー関数ポイントの実装に関するより多くの情報が含まれています。

于 2009-01-04T15:19:35.077 に答える
1

すべての質問に答えるには: はい、通常のポインターとは異なる特別なポインターです。はい、boost::function はそれらを認識します。

標準では、コール スタックの内部の詳細については何も述べていません。実際、多くのコンパイラは、実際の引数リストに応じて、整数レジスタ、浮動小数点レジスタ、および/またはスタックを使用する場合があります。「this」ポインターは、もう 1 つの特別なケースです。

Boost::function は、内部で 2 つのコード パスを使用することでこれを解決します。これは、2 つのケースのコール スタックを調べることで確認できます。boost::function がメンバー関数へのポインターを格納する場合、operator() は引数リストを分割します。最初の引数は、メンバー関数が残りの引数と共に呼び出されるオブジェクトとして使用されます。

于 2009-01-05T09:47:58.803 に答える
0

このリンクは非常に興味深いと思うかもしれません。

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

これは、メンバーへのポインターについて知りたいすべてのことを非常によく説明しています。

于 2009-01-05T10:23:48.217 に答える
0

他のすべての人の答えを補足するために、Boost.Functionは、メンバー関数ポインターに代入演算子を特化することで機能し、代入演算子を渡したときにそれを検出できるようにします。その関数を呼び出すと、メンバー関数ポインター((obj->*fun)(args))を呼び出す適切なメソッドに内部的に再解釈されます。

于 2009-01-04T21:11:36.863 に答える