虚函数详解

虚函数

虚函数在C++中的基类由关键字vritual声明,允许在一个或多个派生类中重新定义。虚函数通过指向派生类中的基类指针或引用来实现多态性,多态性就是将接口与实现进行分离,能且仅能通过指针或引用可以访问派生类中的同名覆盖函数。

多态是什么

联编:联编就是将模块或者函数合并在一起生成可执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址,它是计算机程序彼此关联的过程。
多态也称为动态联编或迟后联编,因为到底调用哪一个函数,在编译时不能确定,而要推迟到运行时确定。也就是说,要等到程序运行时,确定了指针所指向的对象的类型时,才能够确定。
C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
实现多态的三个条件:

  1. 存在继承关系
  2. 重写父类的virtual function
  3. 子类以父类的指针或者是引用的身份出现

为什么需要虚函数

多态性是面向对象设计语言的基本特征。仅仅是将数据和函数捆绑在一起,进行类的封装,使用一些简单的继承,还不能算是真正应用了面向对象的设计思想。

实例

Class Base{
public:
    void print(){

cout<<"This Class is Base Class"<

    }

}

Class A : public Base{

    void print(){

        cout<<"This Class is A Class"<

    }

}

int main(){

    Base ba;

    A b;

    ba.print();

    b.print();

}

上述代码输出

This Class is Base Class

This Class is A Class

此实例并没有实现多态,只是单纯的调用而已。
只有子类以父类的引用或函数的指针形式调用时才能出现多态。
若以指针方式调用而不以virtual修饰父类函数:

Class Base{
public:
    void print(){
        cout<<"This Class is Base Class"<print();
    ptr_b->print();

}

 

Class A : public Base{

    void print(){

        cout<<"This Class is A Class"<

    }

}

 

int main(){

    Base ba;

    A b;

    Base* ptr_base = &ba;

    Base* ptr_b = &b;

    ptr_base->print();

    ptr_b->print();

}

输出的是:

This Class is Base Class
This Class is Base Class

这是因为两个指针的类型都是Base指针,调用的当然是Base的成员函数。用virtual修饰父类的函数就可以实现用父类指针调用。

Class Base{
public:
   virtual void print(){
        cout<<"This Class is Base Class"<print();
    ptr_b->print();

}

Class A : public Base{

    void print(){

        cout<<"This Class is A Class"<

    }

}

int main(){

    Base ba;

    A b;

    Base* ptr_base = &ba;

    Base* ptr_b = &b;

    ptr_base->print();

    ptr_b->print();

}

输出 This Class is Base Class This Class is A Class

虚函数如何实现多态

虚函数通过虚函数表指针(vptr),和虚函数表(vftable)实现
在有虚函数存在的类中,编译器会在类对象前插入一张虚函数表,类创建的每个对象都有它的虚函数指针,指向类的虚函数表

对于虚函数表

  •  继承的体系越复杂,子类的体积越大。

  •  子类中普通成员顺序按照继承的先后顺序来的。

  •  多重继承,子类中含有多个vptr,分别指向不同的vftable。

  •  vftable中的虚函数地址和在类中声明的顺序一致。

  •  如果子类override父类中虚函数,那么子类vftable中就会替换原来父类的虚函数。

  •  如果子类自己含有额外的虚函数,则会附加到第一个vftable中。

  •  vftable中的最后一个值可能为0x0,有时候并不是为0,上图中红色字部分。

  •  子类有vftable,同时父类也有一份vftable,两个vftable没有关联。每个实例化子类都共享一个vftable,同样父类所有实例化对象也共享一份vftable,非常类似类的静态变量。vftable里的内容在编译期间初始化。

纯虚函数

在虚函数原型的后面加上=0(virtual void func()= 0),同时这个函数是没有实现的。
有纯虚函数的类表示这是一个抽象类,抽象类不能实例化。关于抽象类不能实例化。比如说:动物,老虎,狮子,人都是动物。但 说动物没人能理解你说的动物到底指的是什么东西。
由纯虚函数的引出了抽象类,抽象类的出现是为了解决是为了被继承的,它为子类实例化提供蓝图。在相关的组织继承层次中,它来提供一个公共的根。其他相关子类都是这里衍生出来。
它与接口的区别是什么?
接口是对动作的抽象,抽象类是对根源的抽象。比如说人,有五官,有其他属性。但是吃这个动作应该定义为接口更合适。因为其他动物也有吃的动作。

虚函数的缺点

  1.  间接寻址造成的效率慢。
  2.  继承关系带来的强耦合关系,父类动子类可能地动山摇,对象关系复杂度上升。
  3.  体积的增加,尤其是多继承时体现的更明显,引入额外的空间复杂。
  • 微信
  • 快加我微信吧~
  • QQ
  • 快加我QQ吧~
  • weinxin
Betterts

发表评论 取消回复

    • avatar 中南二五仔

      评论测试