虚函数的学习(2)——CPP

关于虚函数的学习二

纯虚函数

定义

纯虚函数是在基类中声明的基函数,它没有定义,并且要求任何派生类都必须实现自己的定义方法,否则该派生类不能实例化。

使用方法是在函数原型后面加“=0”

1
virtual void func()=0;

引入原因

  • 为了多态性,我们需要在基类中实现虚函数;
  • 某些情况下,基类不应该生成对象。例如,一个动物的基类可以派生出兔子、老虎等类,但动物基类不应该实例化。

抽象类

  • 带有纯虚函数的类为抽象类;
  • 抽象类的作用是为派生类提供一个公共的根,要求派生类实现抽象类描述的操作操作。
  • 如果派生类没有自定义抽象类中的虚函数,则该派生类仍然是一个抽象类。

虚析构函数

引入原因

当我们去delete一个动态分配的对象指针的时候将执行析构函数,该指针有可能指向继承体系中的某一个对象,出现指针的静态类型和所指对象的动态类型不符合的情况。因此,为了让编译器清楚执行哪个版本的析构函数,我们需要在基类中将析构函数定义为虚函数。

如果基类的析构函数不是虚函数,那么delete一个指向派生类对象的基类指针将会产生未定义行为。

构造函数中调用虚函数

先说结论,尽量不要这样使用

因为在构造函数中调用虚函数,会使得虚函数的机制失效。在构造函数中调用虚函数,由于构造顺序是先构造父类再构造子类,如果构造父类的时候调用了虚函数,这时查询的将是父类的虚函数表地址。然后再构造子类,此时查询的将是子类的虚函数表地址。

这是为了避免出现bug。因为如果构造父类时查询的是子类的虚函数表地址,而子类的虚函数又用到了子类自定义的数据成员,此时该数据成员还没完成初始化,这样做就会出现错误。

析构函数中调用虚函数

与上原因,同理。但析构函数的析构顺序与构造函数的构造顺序相反。当基类的析构函数执行时,派生类的析构函数已经执行完成,派生类的数据成员已经失效,如果基类还要调用派生类的虚函数,则可能会出错。

析构函数抛出异常

总结;

  • 析构函数尽量不要抛出异常;
  • 如果真的要抛,则在析构函数里面解决,但这种方法不好,因为不能及时地知道异常出现在什么地方;

因为类对象分配的资源由对象的析构函数执行,如果在释放资源之前,异常发生,那么分配的资源将无法被释放。所以我们必须要在类中控制资源的释放。