美文网首页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显示说明,因为它们与基类的纯虚函数具有相同的名称、参数及返回值,由系统自动判断确定其为虚函数。

相关文章

  • GeekBand-C++面向对象高级编程(下)-Week2

    对象模型:虚函数表(vtbl)与虚表指针(vptr) 我们知道,C++中,可以通过虚函数来实现多态性,而虚函数是通...

  • c++语法4

    接上一篇继续学习抽象类、函数模板 抽象类 c++中的抽象类是通过纯虚函数体现的,凡是含有纯虚函数的类叫做抽象类纯虚...

  • C++虚函数

    C++虚函数 C++虚函数是多态性实现的重要方式,当某个虚函数通过指针或者引用调用时,编译器产生的代码直到运行时才...

  • C++面试题系列:纯虚函数,虚函数,抽象类,纯抽象类

    C++面试题系列:纯虚函数,虚函数,抽象类,纯抽象类 2020年10月15日 14:40

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

    注意:本文中代码均使用 Qt 开发编译环境 在C++中不能声明虚构造函数,但是可以声明虚析构函数。析构函数没有类型...

  • C++第六篇多态

    C++中的多态性分为编译时多态性和运行时多态性,编译时多态通过函数重载和模板体现,运行多态通过虚函数体现编译、连接...

  • 慕课网-C++远征之多态篇(中)-学习笔记

    c++远征之多态篇 纯虚函数 & 抽象类 例子: 纯虚函数: 没有函数体 直接等于0 在虚函数表中直接写为0, 包...

  • 纯虚函数和抽象类

    纯虚函数和抽象类 概念 纯虚函数:没有函数体的虚函数。 抽象类:包含纯虚函数的类就称为抽象类。 纯虚函数就是在函数...

  • 面试题目收集总结

    C++: 多态: 多态性都有哪些?(静态和动态,然后分别叙述了一下虚函数和函数重载) c语言和c++有什么区别?(...

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

    注意:本文中代码均使用 Qt 开发编译环境 虚函数是动态联编的基础。虚函数必须是基类的非静态成员函数,其访问权限可...

网友评论

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

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