注意:本文中代码均使用 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显示说明,因为它们与基类的纯虚函数具有相同的名称、参数及返回值,由系统自动判断确定其为虚函数。
网友评论