C++--问题2--虚函数

C++--问题2--虚函数


1.概念       

       虚函数:在某基类中声明为 virtual, 并在一个或多个派生类中被重新定义的成员函数。

  纯虚函数:是一种特殊的虚函数,使用virtual关键字,并且在其后面加上 =0。

  抽象类:在基类中加入至少一个纯虚函数,使基类成为抽象类。

2.为什么要使用虚函数?

         答:先了解晚捆绑,捆绑:把函数体与函数调用相联系称为捆绑;

                早捆绑:当捆绑在程序运行之前完成时,也就是编译阶段,这称为早捆绑;

                晚捆绑:那么当捆绑根据对象的类型,发生在运行时,就称为晚捆绑。

                而使用晚捆绑,无需检查对象的类型,只需要检查对象是否支持特性和方法即可。

           为了引发晚捆绑,C++要求在基类中声明这个函数时使用virture关键字。晚捆绑只对virtual函数起作用,而且只在使用含有virtual函数的基类的地址时发生。

3.重写

                 如果一个函数在基类中被声明为virtual,那么在所有的派生类中它都是virtual,在派生类中virtual函数的重定义通常称为重写。

     问:重定义和重写的关系?

     答:重定义就是重载,覆盖就是重写。重载是静多态,在编译时确定函数的调用。

             覆盖是动多态,在运行时确定函数的调用,动多态就是虚函数的处理。

         在编译阶段,针对虚函数做一个备份,在.rodata里面生成一个虚函数表存放虚函数的入口地址,数据段加载到内存中,在内存中拿到虚函数的入口地址,针对地址确定函数调用,来实现动多态。

4.在C++中如何实现晚捆绑

  虚函数主要有两个步骤:

    (1)每一个类产生出一堆指向虚函数的指针,放在表格中。这个表格被称为virtual table(vtbl)

    (2)每一个类对象被安插一个指针,指向相关的virtual table,通常这个指针被称为vptr

  结构图如下:

5.关于抽象类和纯虚函数

   (1)当继承一个抽象类时,必须实现所有的纯虚函数,否则继承出的类也将是一个抽象类。

        (2)声明一个纯虚函数,就等于告诉编译器在vtbl中为函数保留一个位置,但在这个位置不放地址。只要有一个函数在类中被声明为纯虚函数,则vtbl就是不完全的。

        (3)纯虚函数禁止对抽象类的函数以传值方式调用,这是一种防止对象切片的方法。抽象类可以保证在向上类型转换期间总是使用指针或引用。

       (4)对于纯虚函数,如果要创建对象,必须要在派生类中定义。

注:= 0 告诉编译器,函数没有主体,上面的虚函数是纯虚函数

我们可以把基类中的虚函数 area() 改写如下:

class Shape {
protected: int width, height; 
public: Shape( int a=0, int b=0) 
{
 width = a; height = b; 
}
 // pure virtual function 
virtual int area() = 0; //纯虚函数
};

6.什么是对象切片

  在继承的过程中,通常派生类不仅具有基类的特征,也具有自身的一些特征。当派生类向上进行类型转换称为基类时,就会发生那些自身的特征被切除,只保留继承了基类的特征,这种现象就是对象切片。

  例如:狗类继承了宠物类,具有宠物类的名称这个属性,同时又有啃骨头的特性,当狗类要被转换为宠物类时,就必须抛弃自己爱啃骨头的爱好,这样只保留了对应于宠物类的那部分。

7.虚析构函数和析构函数

        (1)析构函数自最晚派生的类开始,并向上到基类。这就意味着每个析构函数知道它所在类从哪一个类派生而来,但不知道从它派生出哪些类。

       (2)析构函数可以为虚函数,因为这个对象已经知道它是什么类型,但是在构造期间就不知道了。一旦对象已被构造,它的vptr就已经被初始化,所以能发生虚函数调用。

       (3)虚构函数的纯虚性的唯一效果是阻止基类的实例化 

 

还有继承,多态,一起学习比较容易学懂。可以参考我的其他博客。

猜你喜欢

转载自blog.csdn.net/qq_41103495/article/details/108500042
今日推荐