1. 运行结果是什么?

father *pss1 = new sonson();
pss1->f();
delete pss1;
son *pss2 = new sonson();
pss2->f();
delete pss2;
sonson *pss3 = new sonson();
pss3->f();
delete pss3;

2. 运行结果

sonson::f()
sonson::f()
sonson::f()

3. 解释与结论

3.1. 构造对象时

构造对象时,先构造基类部分,此时发现f()带有关键字virtual,就把f()的地址填入vtable中。

vtable中f()的地址:father::f()

再构造派生类部分时,因为在构造基类时已经知道f()是个virtual了,(不管f()是否带关键字virtual),所以直接认为派生类的f()也是virtual的,按照virtual来处理f(),即“把派生类的f()的地址更新到vtable中”。

son类型对象的vtable中f()的地址:son::f()
sonson类型对象的vtable中f()的地址:sonson::f()

这样这个对象的vtable表里f()地址永远都是最后更新的那一层的f()的地址。如果对象是son类型的,最后构造的son部分,vtable存的就是son::f()的地址。如果对象是sonson类型的,最后构造的是sonson部分,vtable存的就是sonson::f()的地址。

3.2. 调用f()时

调用f()时,先根据指针的类型来分析f()是不是virtual,根据上文可知,不管哪一层,都会认为f()是virtual的。

既然f()是virtual的,就不直接根据指针类型调用f(),而是查vtable表获取f()的地址,根据地址调用f(),也就是多态的效果。

3.3. 结论

由此可见,只要在基类里写了virtual,派生类里写不写都没有影响的。最后都能起到多态的效果。

此例是virtual关键字的基本用法,用于与后面其它的测试场景做对比。

4. 其它测试

virtual关键字在子类

析构函数的virtual在子类

间接调用虚函数

5. 完整代码

#include <iostream>
using namespace std;

class father
{
public:
  virtual void f()
  {
    cout<<"father::f()"<<endl;
  }
};

class son : public father
{
public:
  void f()
  {
    cout<<"son::f()"<<endl;
  }
};

class sonson : public son
{
public:
  void f()
  {
    cout<<"sonson::f()"<<endl;
  }
};

int main()
{
  father *pss1 = new sonson();
  pss1->f();
  delete pss1;
  son *pss2 = new sonson();
  pss2->f();
  delete pss2;
  sonson *pss3 = new sonson();
  pss3->f();
  delete pss3;
  return 0;
}

results matching ""

    No results matching ""