zoukankan      html  css  js  c++  java
  • C++ virtual虚函数

     1 #include<iostream>
     2 using namespace std;
     3 class Base{
     4 public:
     5     void m()
     6     {
     7         cout << "it's Base's m() " << endl;
     8     }
     9     virtual void f()
    10     {
    11         cout << "it's Base's f()" << endl;
    12     }
    13 };
    14 class Sub: public Base 
    15 {
    16     void m()
    17     {
    18         cout << "it's Sub's m()" << endl;
    19     }
    20     void f()
    21     {
    22         cout << "it's Sub's f()" << endl;
    23     }
    24 };
    25 void main()
    26 {
    27     Base* base = new Sub;
    28     base->f();
    29     base->m();
    30 }

    上述是以Base为基类,并且Sub派生了Base,同时复写了两个函数f(),m()

    当在主函数main里用Base类型的指针指向Sub类型对象,此时利用Base指针调用f()和m(),那么这个时候问题就来了。

    在默认情况下Base类的指针会调用当前类型的方法,也就是Base::f(),Base::m(),那么应该输出的都是Base方法中的语句

    然而结果输出的是

    为什么会产生这种原因呢?

    因为父类的f()是虚函数,那么当基类指针调用它的派生类对象时,会默认调用多态性对象的相应方法,于是就解释了为什么Base* base->f()调用了派生类Sub的f()的方法,

    由此我们可以知道虚函数Virtual主要是为了解决多态性问题,因为一个基类方法定义后,它的派生类对象会针对此方法进行不同的覆盖实现,即多态性,若你此时利用基类的指针来指向基类的多态性子类,那么就需要利用virtual修饰基类方法,从而可以来调用你所需要的派生类的方法而避免了调用基类的此方法。

    Virtual是C++ OO机制中很重要的一个关键字。在类Base中加了Virtual关键字的函数就是虚拟函数(例如函数print),于是在Base的派生类Derived中就可以通过重写虚拟函数来实现对基类虚拟函数的覆盖。当基类Base的指针point指向派生类Derived的对象时,对point的print函数的调用实际上是调用了Derived的print函数而不是Base的print函数。这是面向对象中的多态性的体现。

    还有虚函数析构函数,应用在这样一个问题:在基类为抽象类,并且基类指针指向派生类,想要删除这个基类指针指向的内存时

    先看第一种情况

     1 de<iostream>
     2 using namespace std;
     3 class Base{
     4 public:
     5     Base()
     6     {
     7     }
     8     ~Base()
     9     {
    10         cout << "Base has deleted" << endl;
    11     }
    12 };
    13 class Sub: public Base 
    14 {
    15 public:
    16     Sub()
    17     {
    18     }
    19     ~Sub()
    20     {
    21         cout << "Sub has delete" << endl;
    22     }
    23 };
    24 void main()
    25 {
    26     Base* base = new Sub;
    27     delete base;
    28     base = NULL;
    29 }

    看运行结果

    此时是用基类Base指针去操作继承类Sub,在delete Base*指针后,我们可以从结果得到只调用了基类的析构函数,但是派生类的析构函数并没有调用,这相当于内存只是删除了一一半,还有一半内存为删除,导致了内存泄漏,此时则需要用到虚析构函数,来达到调用派生类的析构函数来删除内存空间的目的,也就是下面这种情况:

     1 #include<iostream>
     2 using namespace std;
     3 class Base{
     4 public:
     5     Base()
     6     {
     7     }
     8     virtual ~Base()
     9     {
    10         cout << "Base has deleted" << endl;
    11     }
    12 };
    13 class Sub: public Base 
    14 {
    15 public:
    16     Sub()
    17     {
    18     }
    19     ~Sub()
    20     {
    21         cout << "Sub has delete" << endl;
    22     }
    23 };
    24 void main()
    25 {
    26     Base* base = new Sub;
    27     delete base;
    28     base = NULL;
    29 }

    运行结果如下图所示

    此时我们可以看到在基类析构函数加了Virtual变成虚函数后,成功地调用了基类和派生类的析构函数,从而实现了基类指针指向内存的完全释放。

    虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。

    但是有一点要注意,当某个类没有虚函数时,那么此类不会被当作基类,此时定义虚析构函数只会增加内存开销,因为需要开启一个虚函数列表来储存。

    以上只是个人的小总结,若有错误,欢迎前来纠正!

    未经博主同意该原创文章不得转载
  • 相关阅读:
    ImageLoader
    Matrix(单点移动,多点缩放)
    自定义各种图形
    自定义圆形图片
    将博客搬至CSDN
    拉格朗日乘子法的证明
    周志华《机器学习》课后答案——第4章.决策树
    [转] 现实•理论•证据──谈如何做研究和写论文
    高维度下的数据科学——线性空间(下)
    线性模型——异方差、序列相关、多重共线性与内生性的处理
  • 原文地址:https://www.cnblogs.com/Cc1231/p/4784242.html
Copyright © 2011-2022 走看看