C++中的多态与虚函数的内部实现方法

2020-01-06 16:11:59王冬梅

好,那我们再看一个例子:


class A 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1" << endl; 
  }; 
  virtual void FunAA() 
  { 
    cout << "FunA2" << endl; 
  } 
}; 
class B 
{ 
public: 
  virtual void FunB() 
  { 
    cout << "FunB" << endl; 
  } 
}; 
class C :public A, public B 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1C" << endl; 
  }; 
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  C objC; 
  A *pA = &objC; 
  B *pB = &objC; 
  C *pC = &objC; 
 
  printf("%d %dn", &objC, objC); 
  printf("%d %dn", pA, *pA); 
  printf("%d %dn", pB, *pB); 
  printf("%d %dn", pC, *pC); 
 
  return 0; 
}

运行结果:

5241376 1563032

5241376 1563032

5241380 1563256

5241376 1563032

细心的同志一定发现了pB出了问题,为什么明明都是指向objC的指针,pB跟别人的值都不一样呢?

是不是编译器出了问题呢?

当然不是!我们先讲结论:

(1)每一个含有虚函数的类,都会生成虚表(virtual table)。这个表,记录了对象的动态类型,决定了执行此对象的虚成员函数的时候,真正执行的那一个成员函数。

(2)对于有多个基类的类对象,会有多个虚表,每一个基类对应一个虚表,同时,虚表的顺序和继承时的顺序相同。

(3)在每一个类对象所占用的内存中,虚指针位于最前边,每个虚指针指向对应的虚表。

先从简单的单个基类说起:


class A 
{ 
public: 
  virtual void FunA() 
  { 
    cout << "FunA1" << endl; 
  } 
  virtual void FunA2() 
  { 
    cout << "FunA2" << endl; 
  } 
}; 
 
class C :public A 
{ 
  virtual void FunA() 
  { 
    cout << "FunA1C" << endl; 
  }
}; 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
  A *pA = new A; 
  C *pC = new C; 
  typedef void (*Fun)(void); 
 
  Fun fun= (Fun)*((int*)(*(int*)pA)); 
  fun();//pA指向的第一个函数 
  fun = (Fun)*((int*)(*(int*)pA) +1); 
  fun();//pA指向的第二个函数 
   
  fun = (Fun)*((int*)(*(int*)pC)); 
  fun();//pC指向的第一个函数 
  fun = (Fun)*((int*)(*(int*)pC) + 1); 
  fun();//pC指向的第二个函数 
  return 0; 
}

运行结果:

FunA1
FunA2
FunA1C
FunA2 是不是有点晕?没关系。我一点一点解释:pA对应一个A的对象,我们可以画出这样的一个表:       C++,多态与虚函数 这就是对象*pA的虚表,两个虚函数以声明顺序排列。pA指向对象*pA,则*(int*)pA指向此虚拟表,则(Fun)*((int*)(*(int*)pA))指向FunA,同理,(Fun)*((int*)(*(int*)pA) + 1)指向FunA2。所以,出现了前两个结果。 根据后两个结果, 我们可以推测*pC的虚表如下图所示: