解决项目中内存泄漏的问题

根据大佬的文章而来,加上了自己代码的具体例子

关于内存泄漏的问题,根据经验总结如下(持续补充):

原则是,**有 ** new 就有delete

# 普通C++

1 Static对象

尤其是单例模式,目的是整个程序的生命周期只有一个对象实例,但在退出时必须记得删除。(比如绑定mainwindow的析构函数,或者closeEvent等)。

/**
 * @brief The MAP_GR_DIS class
 * 采用单例模式,在private区有单一实例
 * 获取实例的方法:
 *  MAP_GR_DIS::getInstance
 * 举个例子:
 * 比如我想调用NoMeasuringDistance()这个函数
 * 就可以MAP_GR_DIS::getInstance()->NoMeasuringDistance();
 */

class MAP_GR_DIS: public MAP_OSG_BaseHandler
{


public:
     MAP_GR_DIS();
    static MAP_GR_DIS * getInstance();      //获取单一实例的办法
    static bool clickState;
    static void deleteDistanceObject();
private:
    static MAP_GR_DIS * disObject;

比如说这个类,MAP_GR_DIS,它是单一实例的,所以在退出时,应该清空这个类的m_instance;

那么实现的最后方法是

声明:

static void deleteDistanceObject();

定义:

void MAP_GR_DIS::deleteDistanceObject()
{
    if(disObject)
        delete disObject;
    disObject=nullptr;
}

2 类成员指针

所有类都提供析构函数,检查类内有无指针成员,需不需要负责其销毁。注意,并不是有指针就要delete,比如类内保存的观察指针,属于观察者模式,其销毁责任不在该类中。要特别注意析构函数的删除和调用,析构时很可能整个程序都退出了,很多类外的变量实际上已经访问不到了,即已先于该类被析构。此时要是调用即属于野指针,导致崩溃(见文档《堆内存出现野指针记录》)。同时不属于该类销毁的指针被销毁了会导致重复delete,同样导致崩溃。

# osg相关

3 osg对象树

osg对象树是建立在osg::Referenced基类上的,即引用计数,库内绝大部分指针都使用osg::ref_ptr或osg::observer_ptr,在引用计数为0时会自动释放内存,属于有new就要delete的特例(其实只是库帮你做了)。

所以在根据osg项目中的文档,建议osg相关所有指针使用osg::ref_ptr(有特例,后面提到)。这时,基本不需要你去维护内存了。比如一个类成员为osg::ref_ptr,那在该类析构时,osg::ref_ptr对象(指保持指针的类)析构,就会自动delete掉指针对应的内存。需要做的只是m_mypointer = nullptr。

另外,父控件销毁时,会自动销毁子控件,所以只要控制根节点即可。一般情况下,即osgView::View,项目中即removeView(MAP_OSG_BaseView)的接口,删除根节点。

特例情况是,观察者指针,即指针的所有权不在你身上,你只有观察、调用、修饰等职责,尽量不要使用osg::ref_ptr了,而是改用osg:: observer_ptr,该指针的作用是:

Smart pointer for observed objects, that automatically set pointers to them to null when they are deleted.

或者干脆直接用普通的指针也可。如使用到osg::ref_ptr,即使你没用销毁责任,也必须在析构时调用m_mypointer = nullptr。

4 Qt对象树

跟osg类似,同样是具有对象树的结构,父亲会负责孩子的销毁。但,特别强调一点,并不是说使用了Qt的类就不用负责内存管理了,其判断标准为,当且仅当一个对象满足如下条件:
1.该对象是QObject的派生类
2.该对象的父类不为NULL
那么,该对象的父类删除时,该对象会被自动删除,无需手动释放。

可能出现的错误就在于父类,习惯性传入nullptr作为父指针的情况下,即顶级控件,就要负责其内存管理,当然很多情况下Qt窗口之类的,退出即删除,自动delete ui,但要考虑一点,如果该窗口打开的情况下,程序就退出了,涉及模态之类的设置。总之,推荐传入父指针初始化控件。

一个最常见的例子就是,最顶级的界面,即mainwindow,该类在main中初始化,肯定没有父亲,所以必须自己负责其销毁。可以看到绝大部分Qt官方实例的写法是,Mainwindow w; w.show(); 而不是Mainwindow *w = new Mainwindow; w->show(); 即堆内存和栈内存的区别。

posted @ 2020-03-13 19:59  Yan_Hao  阅读(394)  评论(0编辑  收藏  举报