3

vptr と vtable について説明している記事を見つけました。仮想関数が格納されているクラスの場合、オブジェクトの最初のポインターはvtableへのvptrであり、vtableの配列エントリは、クラスで発生するのと同じ順序で関数へのポインターであることを知っています(テストで確認しました)プログラム)。しかし、適切な関数を呼び出すためにコンパイラがどの構文を配置する必要があるかを理解しようとしています。

例:

class Base
 {
   virtual void func1() 
   { 
       cout << "Called me" << endl; 
   }
};
int main()
{
  Base obj;
  Base *ptr;
  ptr=&obj;

// void* is not needed. func1 can be accessed directly with obj or ptr using vptr/vtable
  void* ptrVoid=ptr; 

// I can call the first virtual function in the following way:
  void (*firstfunc)()=(void (*)(void))(*(int*)*(int*)ptrVoid); 
  firstfunc();
}

質問:

1.しかし、私が本当に理解しようとしているのは、コンパイラが呼び出しを ? に置き換える方法ptr->func1()ですvptr。通話をシミュレートする場合、どうすればよいですか? ->演算子をオーバーロードする必要があります。しかし、実際の名前がわからないので、それでも役に立ちませんfunc1。コンパイラが vptr を介して vtable にアクセスするとしても、のエントリがfunc1最初の配列であり、のエントリが配列func2の 2 番目の要素であることをどうやって知るのでしょうか? 関数の名前から配列の要素へのマッピングが必要です。

2.どうすればシミュレートできますか? コンパイラが関数を呼び出すために使用する実際の構文を提供できますfunc1か (どのように を置き換えptr->func1()ますか)?

4

2 に答える 2

2

vtable を配列と考えないでください。メンバーのサイズ以外に C++ が認識しているすべてのものを取り除いた場合、それは単なる配列です。代わりに、structメンバーがすべて関数へのポインターである秒と考えてください。

次のようなクラスがあるとします。

struct Foo {
    virtual void bar();
    virtual int baz(int qux);
    int quz;
}

int callSomeFun(Foo* foo) {
    foo->bar();
    return foo->baz(2);
}

1 ステップの分解:

class Foo;
// adding Foo* parameter to simulate the this pointer, which
// in the above would be a pointer to foo.
struct FooVtable {
    void (*bar)(Foo* foo);
    int (*baz)(Foo* foo, int qux);
}
struct Foo {
    FooVtable* vptr;
    int quz;
}

int callSomeFun(Foo* foo) {
    foo->vptr->bar(foo);
    return foo->vptr->baz(foo, 2);
}

それがあなたが探しているものであることを願っています。

于 2015-09-20T10:54:15.560 に答える