• C++学习笔记 虚析构函数与纯虚析构函数


    开始学C++了,所以又重拾以前学习过的相关概念…

    析构函数是当一个对象的生命周期结束时,会自动执行析构函数。

    析构函数的定义:


    #ifndef __A_H__
    #define __A_H__

    class A
    {
    public:
    A(void);
    A(int a, int b);
    ~A(void); //析构函数

    private:
    int a;
    int b;
    int c;
    };

    #endif

    虚析构函数与纯虚析构函数的定义(假定类名为A):


    #ifndef __A_H__
    #define __A_H__

    class A
    {
    public:
    A(void);
    A(int a, int b);
    virtual ~A(void); //虚析构函数

    private:
    int a;
    int b;
    int c;
    };

    #endif


    #ifndef __A_H__
    #define __A_H__

    class A
    {
    public:
    A(void);
    A(int a, int b);
    virtual ~A(void) = 0; //纯虚析构函数

    };

    #endif

    其中定义了纯虚函数后,类A就成为了“抽象类”,它是不能有实例对象的。否则会报错:

    “A”: 不能实例化抽象类

    由于下列成员:

    “A::~A(void)”: 是抽象的

    一个类维护一个虚函数相关的表--vtable(__vfptr指向它),函数声明前面包含关键字“virtual”的函数,就会创建一个指向该函数的指针(函数指针)被存入vtable中。

    虚函数表的作用是用来实现多态,但同时也带来了执行效率和额外内存空间的增加。

    再来看虚析构函数,它所存在的意义:基类的指针指向派生类对象,用基类的指针删除派生类对象。


    #include <iostream>
    using namespace std;

    class A
    {
    public:
    A()
    {
    cout <<"A..."<<endl;
    }
    ~A()
    {
    cout <<"~A..."<<endl;
    }
    };

    class B :public A
    {
    public :
    B()
    {
    cout <<"B..."<<endl;
    }
    ~B()
    {
    cout <<"~B..."<<endl;

    }
    };

    int main()
    {
    A *a = new B();
    delete a;

    return 0 ;
    }

    输出:

    A…

    B…

    ~A…

    派生类的析构函数未被调用,为什么呢?

    派生类继承自基类,那么基类就只会存在于派生类中,直到派生类调用析构函数后。

    假定:基类的析构函数调用比派生类要早,会造成的一种情况就是类成员不存在了,而类本身却还在,但是类存在的情况下,类成员应该还存在。所以这就矛盾了,所以派生类的析构函数会先被调用,基类的析构函数再被调用。

    修改一下代码:


    #include <iostream>
    using namespace std;

    class A
    {
    public:
    A()
    {
    cout <<"A..."<<endl;
    }
    virtual ~A()
    {
    cout <<"~A..."<<endl;
    }
    };

    class B :public A
    {
    public :
    B()
    {
    cout <<"B..."<<endl;
    }
    ~B()
    {
    cout <<"~B..."<<endl;

    }
    };

    int main()
    {
    A *a = new B();
    delete a;

    return 0 ;
    }

    输出:

    A…

    B…

    ~B…

    ~A…

    仅仅只是在基类的析构函数前面加了一个“virtual”,使它成为“虚析构函数”了,这就是“虚析构函数”存在的意义 :)

    析构函数的作用并不是删除对象,而是撤销对象占用内存之前完成的一些清理工作…

    //=========================================

    总结:如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,就不要定义虚析构函数了,因为它会增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移值性。

    所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。

    当且仅当类里包含至少一个虚函数的时候,才去声明虚析构函数。

    抽象类是准备被用做基类的,基类必须要有一个虚析构函数,纯虚函数会产生抽象类,所以在想要成为抽象类的类里声明一个纯虚析构函数。

    定义一个函数为虚函数,不代表该函数未被实现,只是为了来实现多态。

    定义一个函数为纯虚函数,才表示函数未被实现 ,定义它是为了实现一个接口,起一个规范作用。继承抽象类的派生类要实现这个函数…

    看下面的代码,如何输出:


    //A.h
    #ifndef __A_H__
    #define __A_H__

    class A
    {
    public:
    A(void);
    A(int a, int b);
    virtual ~A(void);

    virtual void numAdd() = 0;
    virtual void f();
    void ff();

    private:
    int a;
    int b;
    int c;
    };

    #endif


    //A.cpp
    #include "A.h"
    #include <iostream>

    using namespace std;

    A::A(void)
    {
    }

    A::A(int a, int b)
    {
    this->a = a;
    this->b = b;

    cout<<"a:"<<a<<" b:"<<b<<endl;
    }

    A::~A(void)
    {
    cout<<"~A"<<endl;
    }

    void A::f()
    {
    cout<<"A::f()"<<endl;
    }

    void A::ff()
    {
    cout<<"A::ff()"<<endl;
    }


    //B.h
    #ifndef __B_H__
    #define __B_H__

    #include "a.h"

    class B :public A
    {
    public:
    B(void);
    ~B(void);

    virtual void numAdd();

    void f();
    virtual void ff();
    };

    #endif


    //B.cpp
    #include "B.h"
    #include <iostream>

    using namespace std;

    B::B(void)
    {

    }


    B::~B(void)
    {
    cout<<"~B"<<endl;
    }

    void B::numAdd()
    {

    }

    void B::f()
    {
    cout<<"B::f()"<<endl;
    }

    void B::ff()
    {
    cout<<"B:ff()"<<endl;
    }

    main函数的定义:


    //main()
    B b;
    A * a = &b;

    a->f();
    a->ff();

    return 0;

    输出什么?

    B::f()

    A::ff()

    //定义指向基类对象的指针a,当调用f()方法时,因为f为虚函数,所以调用了派生类的f(),输出B::f();

    参考:

    详解C++中的纯虚函数(虚函数区别)&多态性以及实例应用

    C++析构函数、构造函数、虚函数关系

    C++中虚函数工作原理和(虚)继承类的内存占用大小计算

  • 相关阅读:
    高一下期末考试划水记
    BZOJ 1053
    积性函数与狄利克雷卷积(未完待更)
    Contest Hunter 3101
    POJ2689
    3.17爆零赛
    全概率公式
    矩阵快速幂
    模板练习
    _rqy's Code Style for OI
  • 原文地址:https://www.cnblogs.com/meteoric_cry/p/3067270.html
Copyright © 2020-2023  润新知