C++ QT内存泄漏总结

对于编程来讲,每种语言都有各自的内存管理机制,他们并不是完全相同。有的语言比如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

发布了128 篇原创文章 · 获赞 346 · 访问量 46万+
展开阅读全文

C++内存泄露,只是动态申请对象,然后delete就发生了内存泄露,真奇怪。

12-13

今天写了个样例程序,程序主要是动态创建一个类的对象,存入list链表中,然后再把链表中的对象delete掉回收内存,理论上并不应该会产生内存泄露,但是从现象上来看确实是发生了内存泄露。程序启动后我分别在“创建对象前”、“创建对象后”、“释放对象内存后”三个阶段使用命令ps -aux|grep a.out查看了程序使用内存情况,发现在“释放对象内存后”阶段并没有释放对象的资源,回收内存。 使用命令查看程序使用内存情况如下: [root@test2 ~]# ps -aux|grep a.out Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ root 37559 0.0 0.0 11752 960 pts/0 S+ 20:21 0:00 ./a.out root 37561 0.0 0.0 105308 868 pts/2 S+ 20:21 0:00 grep a.out [root@test2 ~]# ps -aux|grep a.out Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ root 37559 6.0 0.4 324328 313496 pts/0 S+ 20:21 0:00 ./a.out root 37563 0.0 0.0 105308 872 pts/2 S+ 20:21 0:00 grep a.out [root@test2 ~]# ps -aux|grep a.out Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ root 37559 4.6 0.4 324328 313540 pts/0 S+ 20:21 0:00 ./a.out root 37565 0.0 0.0 105308 872 pts/2 S+ 20:21 0:00 grep a.out [root@test2 ~]# ps -aux|grep a.out Warning: bad syntax, perhaps a bogus '-'? See /usr/share/doc/procps-3.2.8/FAQ root 37559 3.2 0.4 324328 313540 pts/0 S+ 20:21 0:00 ./a.out root 37567 0.0 0.0 105308 868 pts/2 S+ 20:21 0:00 grep a.out 程序如下: ``` #include <iostream> #include <stdio.h> #include <string.h> #include <list> using namespace std; class basicClass { public: basicClass() { a = 0; b = 0; c = 0; } virtual ~basicClass() { // cout<<"basic class release\n"; } public: virtual int print() { cout<<"a:"<<a<<endl; return 0; } protected: int a; int b; int c; int arr; }; int main(void) { printf("init stat\n"); getchar(); list<basicClass*> classList; for (int i = 0; i < 5000000; i++) { basicClass *pClass = new basicClass(); classList.push_back(pClass); } printf("insert finish\n"); getchar(); unsigned int i = 0; for (list<basicClass*>::iterator iter = classList.begin(); iter != classList.end(); iter++) { i++; delete *iter; } classList.clear(); printf("release finish\n"); printf("release count:%d\n", i); getchar(); return 0; } ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览