最近在看Qwt给的示例的源码,遇到这么一句代码:
dataCurve->setSymbol(new QwtSymbol(QwtSymbol::Ellipse, QBrush( Qt::red ), QPen( Qt::red ), QSize( 9, 9 )));
我在想,这样直接new 了一个指针出来作为实参,什么时候delete 掉的呢,难道不会发生内存泄露吗?
函数的原型如下:
void QwtPolarCurve::setSymbol (QwtSymbol * symbol)
难道在函数的内部最后会delete 掉,那么传入一个栈对象的地址,也就是一个栈对象的指针,如果存在delete,难道不是会发生错误吗?
于是有了下边的实验:
QwtSymbol symObj(QwtSymbol::Ellipse,QBrush( Qt::red ), QPen( Qt::red ), QSize( 9, 9 ) );
dataCurve->setSymbol(&symObj );
先在栈上构造一个对象,然后传入这个对象的地址;一编译,无错,运行也没有问题,但是在程序退出的过程中,会发生异常。说明的确需要传入一个堆上的地址才行。
这里插播一句,QT中的对象的拷贝构造函数和赋值构造函数都是private 的,这样
QwtSymbol symObj = QwtSymbol((QwtSymbol::Ellipse,QBrush( Qt::red ), QPen( Qt::red ), QSize( 9, 9 ) ));
和
QwtSymbol symObj(QwtSymbol((QwtSymbol::Ellipse,QBrush( Qt::red ), QPen( Qt::red ), QSize( 9, 9 ) )));
显然都是错误的。
回到正题,那问题是,申请的堆上的地址到底有没有释放掉呢?
下边进一步实验,这下传入一个堆上的地址,不过是一个具名的实参:
QwtSymbol* ptrSym =new QwtSymbol(QwtSymbol::Ellipse,QBrush( Qt::red ), QPen( Qt::red ), QSize( 9, 9 ));
dataCurve->setSymbol(ptrSym);
然后在语句块的结束处,手动释放分配的内存:
delete ptrSym;
结果一样,程序启动和运行都没有什么问题,但是在退出的过程中发生了异常,说明不需要去显示的释放。
那么也就是示例代码中,已经帮我们释放了。
进一步实验验证内存确实被释放了。如果delete 指向堆的指针,是会调用析构函数的,那就从从析构函数的调用与否来验证。
查看帮助文档,发现QwtSymbol类 的析构函数刚好为virtual 的,如果派生自QwtSymbol类,析构的时候刚好可以正确的调用派生类的析构函数。
在派生类的析构函数中打印输出,作为标记。其他的什么都添加,只是对QwtSymbol 类的一个简单包装。查看帮助文档,使用QwtSymbol 的一个构造函数的版本(就是示例代码中用来的那个),来设计派生类的构造函数,派生类的构造函数只需要在初始化列表中调用基类QwtSymbol 的构造函数即可,最终的设计如下:
class QMySymbol: public QwtSymbol
{
public:
QMySymbol( QwtSymbol::Style style,
const QBrush & brush,
const QPen & pen,
const QSize & size
) : QwtSymbol(style, brush, pen, size)
{
}
~QMySymbol()
{
qDebug() <<"this is my symbol";
}
};
将最初的那句改成如下,参数使用派生类QMySymbol 指针。
编译,运行,都正常。然后程序退出的时候,成功的打印出了析构函数中的标记:
说明,最后内存还是在某个地方被释放掉了,由于不是在那个语句结束的语句块,而是在程序退出的时候,说明是在随着某个大对象一起析构的。
Qt的对象树机制(父对象与子对象,区别于标准C++继承关系中的父类与子类)帮助缓解了一部分的内存管理工作。可是今天这个例子,显然的并没有设置父指针(一般父指针的设置都是在构造函数中传入一个指针,而且QwtSymbol 这个类也没有setParent() 成员函数),所以这里的内存释放机制并不是透明的,暂且只能当作经验来记住了。
C++继承时,派生类对象的构造过程中会先构造基类的subobject,然后才是成员;实际上,QT中的子对象通常是成员指针所指向的堆上的一个对象(或者是派生类的构造函数中的一个局部指针),当然在析构是,这个成员指针的释放是自动的,但是,这个成员指针所指的堆上的对象,必须显示的在派生类的析构函数中delete 掉,QT的对象树的好处就在于,设置了父指针之后,再也可以不用去显示的释放掉这个对象,而会跟随着派生类对象的析构而析构。