2012-08-17 62 views
25

ポインタは単なるアドレスではありませんか?または私は何かを欠いている?私はポインタのいくつかのタイプを使用してテストなぜ関数へのポインタのサイズがメンバ関数へのポインタのサイズと異なるのですか?

:任意の変数へ

  • ポインタは変数(再び8B)へのポインタとして、機能へ
  • ポインタが同じサイズで同じ(私のプラットフォーム上8B)であります異なるパラメータを持つ関数へ
  • ポインタ - 私のプラットフォーム上で16B - まだ同じ(8B)

しかし、ポインタメンバ関数には大きなされています。

3つのこと:

  1. なぜメンバ関数へのポインタを大きくしていますか?彼らはさらに多くの情報を必要としていますか?
  2. 私の知る限りでは、標準void*は、任意のポインタ型「が含まれている」ことができなければならないということ除き、ポインタのサイズについては何も言いません。言い換えれば、どのポインタもvoid*にキャストできる必要があります。その場合、なぜsizeof(void*)が8であるのに対して、sizeofのメンバー関数へのポインタは16ですか?
  3. 異なるサイズのポインタの例がありますか(つまり、標準プラットフォームであり、まれな特殊なものではありません)。
+1

本質的に、標準では、データポインタ、関数ポインタ、およびメンバ関数ポインタのサイズがすべて同じである必要はありません。彼らがあなたのプラットフォームで同じサイズを持っていない理由を知りたければ、あなたのC++コンパイラの保守担当者に尋ねる必要があります。 http://www.parashift.com/c++-faq/cant-cvt-memfnptr-to-voidptr.html http://www.parashift.com/c++-faq/cant-cvt-fnptr-to-voidptr.html – Cubic

+0

@KirilKirov問題ありません。私はいつも悪口ではない:) –

答えて

26

EDIT:だから、私はまだ(私も、私は一度考えていたものを覚えて、それができない私の元の答えは悪いと誤解されていても、後でこの数ヶ月で票を得ている気づきました)私は、人々がまだ検索を通してここに来なければならないので、私は状況を明確にしようと思うと思った。最も通常の状況では

、あなたは

struct A { int i; }; 
int A_foo(A* this) { return this->i; }; 
A a; A_foo(&a); 

として

struct A { int i; int foo() { return i; } }; 
A a; a.foo(); 

のかなりのシンクタンク(右、C見えるように起動する?)ことができますので、あなただけのポインタ&A::fooは、と思うだろう通常の関数ポインタと同じでなければなりません。しかし、いくつかの合併症があります:多重継承と仮想関数。

だから、我々が持っている想像:

struct A {int a;}; 
struct B {int b;}; 
struct C : A, B {int c;}; 

それはこのようにレイアウトされる可能性があります:あなたが見ることができるようにあなたがA*かでオブジェクトを指すようにしたい場合は、

Multiple inheritance

C*の場合、開始点を指していますが、B*を指す場合は、中央のどこかを指す必要があります。だからCBからいくつかのメンバ関数を継承し、それを指し示したい場合は、C*の関数を呼び出して、thisポインタをシャッフルすることを知る必要があります。その情報はどこかに保存する必要があります。だから関数ポインタでまとめられます。

virtualの関数を持つすべてのクラスについて、コンパイラはという仮想テーブルという名前のリストを作成します。その後、このテーブルへの余分なポインタをクラスに追加します(vptr)。したがって、このクラス構造のために:

struct A 
{ 
    int a; 
    virtual void foo(){}; 
}; 
struct B : A 
{ 
    int b; 
    virtual void foo(){}; 
    virtual void bar(){}; 
}; 

コンパイラはこのようにそれを作るに終わるかもしれない: enter image description here

ので、仮想関数へのメンバ関数ポインタは、実際には仮想テーブルへのインデックスである必要があります。 したがって、メンバー関数ポインタは、実際には1)関数ポインタ、2)場合によってはthisポインタの調整、3)場合によってはvtableインデックスが必要です。一貫性を持たせるためには、すべてのメンバ関数ポインタがこれらすべてのものに対応可能である必要があります。したがって、ポインタの場合は8バイト、調整の場合は4バイト、インデックスの場合は4バイト、合計で16バイトです。

これは実際にはコンパイラ間で多くのものが変わっていると思うし、可能な最適化がたくさんある。おそらく私が記述した方法で実際に実装する人はいません。

ロット詳細については、this(「メンバー関数ポインタの実装」までスクロール)を参照してください。

+0

私はあなたが探していたと思う標準からテキストを挿入する自由を取った。私はそれが大丈夫だと思います。 – jogojapan

+0

標準に関する限り、 'int *'は 'void *'より小さくてもよいことに注意してください。非常に古いC実装の中には、それがありました。 'void *'が保証されているのはメンバー以外のデータです:例えば、メンバーデータへのポインタが 'void *'よりも大きくないことは保証されていません(しかし、私は妥当な実装を想像できません。それはそのようになります)。 –

+1

これはオブジェクトへのポインタのようなものとして実装されるは明らかに正しくありません。オブジェクトポインタは、メソッドが呼び出されるポイントに提供されます。しかし、私はちょうどあなたの言葉を少しミスマッチしたと思います。多形的な振る舞いをサポートするためには、より多くの情報が必要であることを示すために、それをもう一度言い換えることにします。 –

6

基本的には、多態性の動作をサポートする必要があるためです。レイモンド・チェンの素敵なarticleを見てください。

0

私はそれがthisポインタと関係があると思います...つまり、各メンバ関数は、それらがあるクラスのポインタも持っていなければなりません。ポインタは関数のサイズを少し大きくします。

+3

いいえ、間違っていると思っています.Dirkの答えを参照してください。 –

+0

この回答も間違っています。 Dirk Holsoppleさんと一緒に –

+0

:)私は同じように思っていましたが、それは真実ではありません。例えば、そのようなポインタを初期化することはオブジェクトとは関係ありませんが、クラス宣言のみと関係しています。違う。 –

2

いくつかの説明はここで見つけることができる: メンバーへのポインタは、彼らの表現はかなり異なっている背後で、通常のポインタのように振る舞うがThe underlying representation of member function pointers

。実際、メンバーへのポインターは、通常、4つまでのフィールドを含む構造体で構成されていることがあります。これは、メンバへのポインタが通常のメンバ関数だけでなく、仮想メンバ関数、複数の基本クラスを持つオブジェクトのメンバ関数、および仮想基本クラスのメンバ関数をサポートしなければならないためです。したがって、最も単純なメンバ関数は、メンバ関数の物理メモリアドレスを保持するポインタと、このポインタを保持する第2ポインタの2つのポインタのセットとして表すことができる。しかし、仮想メンバ関数、多重継承、仮想継承のような場合、メンバへのポインタは追加情報を格納しなければなりません。したがって、メンバーへのポインタを普通のポインタにキャストすることはできません。また、異なるタイプのメンバーへのポインタ間で安全にキャストできません。 BLOCKQUOTE

0

{this, T (*f)()}としてメンバ関数へのポインタを表すための主な理由のいくつかは、次のとおりです。

  • それは、T (*f)()

  • としてメンバ関数へのポインタを実装するよりも、コンパイラでシンプルな実装を持っていますランタイムコード生成や付加的な簿記を伴わない。

  • それはT (*f)()

  • に比べてかなりよく実行する実行中sizeof(void*)

  • ランタイムコード生成に等しくなるようにメンバ関数へのポインタのサイズのためにC++プログラマから十分な需要が存在しない事実上のAでありますtaboo for C++ code

関連する問題