美文网首页C++ 杂记
C++ 多态性 虚函数、抽象类(二)

C++ 多态性 虚函数、抽象类(二)

作者: 赵者也 | 来源:发表于2017-03-21 14:35 被阅读57次

    注意:本文中代码均使用 Qt 开发编译环境

    在C++中不能声明虚构造函数,但是可以声明虚析构函数。析构函数没有类型,也没有参数,和普通成员函数相比,虚析构函数相对简单。
    语法:

    virtual ~className();
    

    如果有可能通过基类指针调用对象的析构函数(通过delete),并且被析构的对象是有重要的析构函数的派生类的对象,就需要让基类的析构函数成为虚函数。

    未使用虚析构函数的情况举例:

    #include <QCoreApplication>
    #include <QDebug>
    
    class Base{
    public:
        ~Base(){ qDebug() << "Basedestructor"; }
    };
    
    class Derived: public Base{
    public:
        Derived();
        ~Derived();
    private:
        int * i_pointer;
    };
    
    Derived::Derived(){
        i_pointer = new int(0);
    }
    
    Derived::~Derived(){
        qDebug() << "Derived destructor";
        delete i_pointer;
    }
    
    void fun(Base *b){
        delete b;
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        Base *b= new Derived();
        fun(b);
    
        return a.exec();
    }
    

    本例使用了Qt5.8的开发和编译环境,因此有一些Qt中的输出log的方法,上述测试代码的输出为:

    Basedestructor
    

    这说明,通过基类指针删除派生类对象时调用的是基类的析构函数,派生类的析构函数没有被执行,因此派生类对象中动态分配的内存空间没有被释放,造成了内存泄漏。

    避免上述情况的方法就是将基类的析构函数声明为虚函数:

    class Base{
    public:
        virtual ~Base(){ qDebug() << "Basedestructor"; }
    };
    

    运行结果为:

    Derived destructor
    Basedestructor
    

    这说明派生类的析构函数被调用了,派生类对象中动态申请的内存空间被正确的释放了。这是由于使用了虚析构函数,实现了多态。

    《抽象类》

    抽象类是一种特殊的类,他为一个类族提供统一的操作界面。抽象类是为了抽象和设计的目的而建立的,可以说,建立抽象类,就是为了通过它多态地使用其中的成员函数。抽象类处于类层次的上层,一个抽象类自身无法实例化,也就是说我们无法定义一个抽象类的对象,只能通过继承机制,生成抽象类的非抽象派生,然后再实例化。

    <纯虚函数>

    纯虚函数是一个在基类中声明的虚函数,它在该基类中没有定义具体的操作内容,要求派生类根据实际需要定义自己的版本,声明格式:

    virtual returnType functionName(params) = 0;
    

    实际上,它与一般虚函数成员的原型在书写格式上的不同就在于后面加了“=0”。声明为纯虚函数之后,基类中就不再给出函数的实现部分。纯虚函数的函数体由派生类给出。

    还有一种情况是函数体为空的虚函数,请注意它和纯虚函数的区别。纯虚函数根本就没有函数体,而空的虚函数的函数体为空,前者所在的类是抽象类,不能直接进行实例化,而后者所在的类是可以实例化的。他们共同的特点是都可以派生出新的类,然后在新的类中给出虚函数的实现,而且这种新的实现可以具有多态特征。

    <抽象类>

    带有纯虚函数的类就是抽象类。抽象类的主要作用就是通过它为一个类族建立一个公共接口,使它们能够更有效地发挥多态特性。抽象类声明了一族派生类的共同接口,而接口的完整实现,即纯虚函数的函数体,要由派生类自己定义。

    抽象类派生出新的类之后,如果派生类给出所有纯虚函数的函数实现,这个派生类就可以定义自己的对象,因而不再是抽象类;反之,如果派生类没有给出所有纯虚函数的函数实现,这时派生类仍然是一个抽象类。

    抽象类不能实例化,即不能定义一个抽象类的对象,但是,我们可以声明一个抽象类的指针和引用。通过指针或引用,我们就可以指向并访问派生类对象,进而访问派生类的成员,这种访问是具有多态特征的。

    示例:

    #include <QCoreApplication>
    #include <QDebug>
    
    class B {
    public:
        virtual void display() = 0;
    };
    
    class C: public B {
    public:
        void display() { qDebug() << "C::display()"; }
    };
    
    class D: public C {
    public:
        void display() { qDebug() << "D::display()"; }
    };
    
    void fun(B *ptr) {
        ptr->display();
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        B *p;
        C c;
        D d;
        p = &c; fun(p);
        p = &d; fun(p);
    
        return a.exec();
    }
    

    该测试示例的输出结果是:

    C::display()
    D::display()
    

    同时,程序中派生类的虚函数并没有用关键字virtual显示说明,因为它们与基类的纯虚函数具有相同的名称、参数及返回值,由系统自动判断确定其为虚函数。

    相关文章

      网友评论

        本文标题:C++ 多态性 虚函数、抽象类(二)

        本文链接:https://www.haomeiwen.com/subject/jrcgnttx.html