C++ QT内存泄漏总结

50 篇文章 2 订阅

对于编程来讲,每种语言都有各自的内存管理机制,他们并不是完全相同。有的语言比如JAVA、H5等无需编程人员关心内存是怎样创建,怎么释放的,系统会对内存自动管理。但是有些语言比如C、C++等,就需要程序员手动进行内存管理,比如需要的时候进行内存申请,不用的时候需要进行手动释放。

在C++中,new与delete必须配对使用(既,有一个new,有且只能有一个delete)。delete少了,可能会造成内存泄漏;delete多了,麻烦更加大了,会造成未知的错误,一般情况下都会造成程序崩溃。所以使用类似C++这种需要手动进行内存管理的语言,一定要特别注意他们各自的内存管理机制。

本次我们讨论一个与上述两个都有区别的内存管理机制-QT的内存半自动管理。
QT的内存管理机制与上面两个都有区别,既不是完全系统管理,也不是完全用户管理,它是半自动管理,也就是特定情况下只需要new,不需要delete。

因为QT中大部分可见控件,都是继承于QObject,而所有继承于QObject的子类,只需要设置了parent(也可以在构造时,使用setParent函数,或者parent的addChild函数),所以,当parent被delete的时候,它的左右子类也会被自动delete,无需手动处理。并且parnet是不区分他的所有child是new出来的,还是在栈上自动分配的,这充分体现delete的强大,可以释放掉任何子对象。所以我们在QT程序中,经常能够看到很多new出来对象,而很少看到对应的delete的原因。

QT怎么能够做到“父销亡,子必毁”呢?我们先来了解一下QObject对象:
在这里插入图片描述
解释之前,我们说明一下我们这里面说的parent。我们说的父子关系包含:父对象、子对象、父子关系。这是QT中特有的,与类的继承无关。我们的parent主要是通过参数传递,或函数调用进行设置两个对象的父子关系的,并非继承。
有一下几点需要我们注意一下:

  1. QObject及其派生类的对象,如果parent不为0,既一个对象有parent时,当其parent析构时会析构该对象。

  2. QWidget及其派生类的对象,可以设置Qt::WA_DeleteOnClose标志后,当close时也会析构该对象。

  3. QAbstractAnimation的派生类对象,可以设置QAbstractAnimation::DeleteWhenStoped后,当动画停止,也可以将其析构。

  4. QRunnable::setAutoDelete() 、 MediaSource::setAutoDelete可以达到特定条件能够自动销毁的效果。

接下来,我们说一下C++程序中内存泄漏,对于C++的内存泄漏,总结起来就是一句话:

“new出来的内存没有通过delete合理的释放掉。”

其中有可能是以下几种情况:

  1. new 出来的对象,没有及时的delete掉。
  2. new出来一个数组,删除的时候只调用了delete,而非delete[]。导致只有数组第一个对象的析构函数得到执行并回收。其他对象所占用的内存没有回收。
  3. delete掉一个void*类型的指针,导致没有调用到对象的析构函数,析构的多有清理工作都没有去执行从而导致内存泄漏。

一、对于第一种情况,new出来的对象没有及时delete掉是什么样的?

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QLabel *label = new QLabel("123");
    label->show();
}

其中 new出来的label 并没有地方delete掉,所以,这个内存是肯定泄漏了。
但是鉴于QT程序的半自动内存管理机制,我们来讲一下怎麽避免内存泄漏:
我们可以设置label的parent设置成非0,这样我们就可以不用手动delete了。比如:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QLabel *label = new QLabel("123", this);
    label->show();
}

这样就会避免内存泄漏的问题,我们只是在new label 的时候,传入了this作为label的parent。所以这时候只要mainwindow析构的时候,会自动将label删除,不会造成内存泄漏。所以也不必手动delete label。这就说明了QT的内存半自动管理机制。

二、第二种情况可能遇到的会比较少。我们先定义一个类:

class Object {
private:
    void* data;
    const int size;
    const char id;

public:
    Object(int sz, char c):size(sz), id(c){
    	data = new char[size];
    	qDebug() << "Object() " << id << " size = " << size;
    }
    ~Object(){
    	qDebug() << "~Object() " << id << endl;
    	delete []data;
    }
};

借着,我们在main函数里进行测试:

int main() {
Object* a = new Object(10, 'A');//Object*指针指向一个Object对象;
void* b = new Object(20, 'B');//void*指针指向一个Object对象;
delete a;//执行delete,编译器自动调用析构函数;
delete b;//执行delete,编译器不会调用析构函数,导致data占用内存没有得到回收;

return 0;
}

执行结果如下图:
在这里插入图片描述
从执行结果我们可以看到,程序并没有执行b指针(void*)所指对象的析构函数,所以delete一个void*的指针可能会造成内存上的泄漏。

三、第三种情况可能大多数初学者都会遇到:

class MyClass
{
    int a;
    int b;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    MyClass* arry1 = new MyClass[100];//创建包含100个MyClass的对象数组arry1并返回数组首地址;
    MyClass* arry2 = new MyClass[100];//创建包含100个MyClass的对象数组arry2并返回数组首地址;
    delete []arry1;//回收了数组MyClass里的所有对象动态创建时占用的内存空间;
    delete  arry2;//回收了数组MyClass里的第一个对象动态创建时占用的内存空间,导致其他99个对象的内存空间泄露;

    return a.exec();
}

最后总结一句,所有的内存泄漏都是因为new出来的内存没有及时的delete掉。

**

关于使用工具来检测内存是否泄漏看这里:

**https://blog.csdn.net/xiezhongyuan07/article/details/102585577

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值