2009/01/16

C++ vtable

在 C++ 的OO模型中,很重要的一个特性是运行时多态。运行时多态是通过类的虚方法实现的。看下面这个例子:


struct IObject {
  virtual void MethodA() = 0;  
  virtual int  MethodB(char *) = 0;
};

class A : public IObject {
public:
  virtual void MethodA() {
    std::cout << "method A" << std::endl;
  };

  virtual int MethodB(char *p) {
    std::cout << "method B " << p << std::endl;
    return 0;
  };

public:
  int MethodC(int c) {
    std::cout << "method C non-virtual" << std::endl;
    return ++c;
  }
private:
  int mem_;
};

int main(int argc, cahr *argv[])
{
  IObject *pObj = new A;

  pObj->MethodA();
  pObj->MethodB(NULL);

  // FIXME: This is a compile error!
  pObj->MethodC(1); // error

  delete A;

  return 0;
};

C++ 运行时多态, 在执行代码中,主要通过 vtable 实现。让我们用C来重新描述上面的例子。


// VTABLE of class IObject
struct I_IObject {
  void *pMethodA(void);
  int  *pMethodB(char *);
};

// Instance variant type of IObject
struct IObject {
  struct I_IObject *vtable_this;
};

// VTABLE of class A
struct A;
struct I_A {
  void *pDtor(A*)
};

// Instance object variant type of class A
struct A {
  struct I_IObject *vtable_IObject;
  struct I_A *vtable_this;

  int *pMethodC(int);
  int mem_;
};

void A_MethodA (void) {
  printf("method A\n");
}

int  A_MethodB (char *p) {
  printf("method B %s\n", p);
}

int  A_MethodC (int c) {
  printf("method C non-virtual\n");
  return ++c;
}

void A_dtor(struct A *_this) {
  free(_this);
}

struct A *A_ctor() {
  struct A *p = malloc(sizeof(struct A));

  // Non-virtual function will be assign the value directly.
  p.pMethodC = &A_MethodC;

  /////////////////////////////////////////////////
  // vtable initialization code
#ifdef A_MethodA
  p.vtable_IObject->pMethodA = &A_MethodA;
#else
  p.vtable_IObject->pMethodA = &IObject_MethodA; // Warning!
#endif

#ifdef A_MethodB
  p.vtable_IObject->pMethodB = &A_MethodB;
#else
  p.vtable_IObject->pMethodB = &IObject_MethodB; // Warning!
#endif

  // vtable of A itself
  p.vtable_self->pDtor = &A_dtor;

  return p;
}

int main(int argc, char *argv[])
{
  IObject *pObj = (IObject*) A_ctor();

  pObj->vtable_self->pMethodA();
  pObj->vtable_self->pMethodB(NULL);

  A_dtor(pObj);

  return 0;
}




C的定价物确实有些繁琐,不过基本的结构就是这样了。具体说来是这样:
1,如果一个类 virtual function, 这些 virtual funciton 就会被统一在一个 vtable 中;
2,包含 virtual function 的class 都会有一个指向 vtable structure的指针,如果当前类重写了 virtual function, 在对象初始化的时候相应的虚函数指针就会被替换;
3,虚函数的调用一律通过vtable定位,因为存在vtable这一层中继,就可以保证在运行态可以通过父类的指针执行指向子类对象的方法,因为类对象初始化的时候正确的重写了vtable的内容。
4,一个对象多一个包含virtual function的基类,就会在类变量的结构中多引入一个指向父类vtable structure的指针。在类对象初始化的时候,就需要更多的 vtable 初始化代码。:D

多重继承会导致 vtable 膨胀, Microsoft ATL 引入了一个 ATL_NO_VTABLE __declspec(novtable) C++ 扩展。关于这个,下回再说~~~

其实这个例子里面还有一个小问题, 不知道大家能不能看出来啊!
发表评论