ZMonster's Blog 巧者劳而智者忧,无能者无所求,饱食而遨游,泛若不系之舟

C++函数内部new操作并返回对象给外部导致的内存泄漏

最近在为公司一个项目的一个模块写API,在API基本完成后,我写了几个demo把各种情况测试了一下。通过简单测试后,一时兴起,用valgrind进行了一下内存检查,真是不看不知道,一看吓一跳。

mem-leak.jpg

当时为了更快地看到结果,只拿了一个小的音频文件来进行处理,大概就几百个KB吧,然后内存泄漏的量就达到了3,481,396个字节!

花了一两天的时间,借助valgrind以及代码阅读工具(sublime text + ctags),我发现了内存泄漏的源头。

一方面,有项目本身的代码编写不规范导致的泄漏;另一方面,也有项目使用的开源库自身的问题。

项目本身存在的问题有:

  1. new/delete 操作不成对
  2. 拷贝构造函数/赋值运算符重载 在进行拷贝时只进行了浅拷贝

项目本身的问题不太多,而且容易发现,所以被我优先解决了,此时内存泄漏的量减少了一半左右。

接下来进入正题,在我们项目使用的开源库中,存在大量的同一个问题。

对于下面这个类

class A
{
public:
    A &create() {A *p = new A; return *p;}
    virtual ~A();
private:
    A();
};

首先,对于类 A 的方法 create ,如果按下面的形式调用:

A a;
A b = a.create();

是必然会发生内存泄漏的,因为 b 不是引用类型,在赋值时发生的是拷贝操作, create() 内部产生的指针已经被丢失了。所幸这种行为在项目以及项目使用的开源库中并没有发现。但在该开源库自身的代码中,大量存在另外一种错误。

A a;
A &b = a.create();

使用引用对象来接受 create() 返回的对象是正确的,但是,由于对象所使用的内存空间是通过 new 得到的,也就是说,其占用的内存,是在堆上的,对于这样的内存空间,如果不手动进行回收,则在程序退出时也不会被回收。也就是说,下面的操作才是正确的:

A a;
A &b = a.create();
// ...
delete &b;

然而很不幸的是,这个开源库里面对于大量调用 create() 及类似方法返回的对象,都没有进行回收,这是它自身的问题。如果要彻底解决目前我面临的内存泄漏问题,只能去修改这个开源库了,想想就觉得是一件很麻烦的事情_(:3」∠)_