C ++抛出异常会导致内存泄漏

时间:2014-02-15 18:42:44

标签: c++ exception memory-leaks throw

我最近遇到了内存泄漏问题。我在很长一段时间内解决了这个问题,随后发现抛出异常(我使用自己的异常类)会导致内存泄漏。抛出异常的代码如下:

HINSTANCE lib = LoadLibrary(path.c_str());

if(!lib)
{
    DWORD werror = GetLastError();
    ostringstream stream;
    stream << werror;
    string errstring = "Error " + stream.str();
    errstring.append(" - " + libraryName);

    throw LibraryLoadException(MOD_ERROR_LIB_LOAD, errstring.c_str());
}

结果输出如下:

Detected memory leaks!
Dumping objects ->
{351} normal block at 0x0044D208, 32 bytes long.
 Data: <Error 126 - note> 45 72 72 6F 72 20 31 32 36 20 2D 20 6E 6F 74 65 
{347} normal block at 0x0043BD98, 8 bytes long.
 Data: <4 >     > 34 F2 3E 00 00 00 00 00 
{344} normal block at 0x0043FDE8, 32 bytes long.
 Data: <126             > 31 32 36 CD CD CD CD CD CD CD CD CD CD CD CD CD 
{302} normal block at 0x004409D8, 8 bytes long.
 Data: <4 >     > 34 F3 3E 00 00 00 00 00 
{301} normal block at 0x0043FAF0, 8 bytes long.
 Data: <P >     > 50 F3 3E 00 00 00 00 00 
Object dump complete.

如在visual studio leak CrtDbg的输出中所见,if块中使用的对象的实际值。所有这些对象,包括异常本身(及其所有属性)都是在堆栈上分配的,所以我不会忘记忘记在堆上释放某些东西。 我凭经验对此进行了测试,泄漏肯定是由if块中的对象引起的(在删除了几个对象之后,如字符串,DWORD和流,泄漏变得更少)。

有谁能告诉我我在做什么(或者什么)错了?

提前谢谢

2 个答案:

答案 0 :(得分:0)

至于要求更详细代码的注释,以下是导致内存泄漏的方法:

void ModuleLoader::load(std::string libraryName, std::string type, void(CALLBACK * receiveData)(Message))
{
path = s2ws(libraryName);   // conversion to wide string

HINSTANCE lib = LoadLibrary(path.c_str());

if(!lib)
{
    DWORD werror = GetLastError();
    ostringstream stream;
    stream << werror;
    string errstring = "Error " + stream.str();
    errstring.append(" - " + libraryName);

    throw LibraryLoadException(MOD_ERROR_LIB_LOAD, errstring.c_str());
}

DllModule *module = new DllModule(libraryName, lib);
module->setModType(type);

try
{
    startModule(module, receiveData);
    moduleMap.insert(std::pair<std::string, DllModule *>(type, module));
}
catch (ProbeCoreException e)
{
    delete module;
    throw e;
}
}

这是一个加载动态模块的单例类的方法,定义如下:

class ModuleLoader 
{
// Function pointer definitions
typedef void (*StopFuncPointer)();
typedef int (*StartFuncPointer)(void(CALLBACK * receiveData)(Message));
typedef void (*SetDataFunctionPointer)(Message);

private:

std::map<std::string, DllModule *> moduleMap;   // map of loaded modules

std::wstring path;

std::wstring s2ws(const std::string &s);

void startModule(DllModule * module, void(CALLBACK * receiveData)(Message));    

void stopModule(DllModule * module);

// singleton functions
ModuleLoader() {};  // private constructor
ModuleLoader(ModuleLoader const&);
void operator = (ModuleLoader const&);

public:

void load(std::string libraryName, std::string type, void(CALLBACK * receiveData)(Message));

void unload(std::string libraryType);

void unloadAll();

vector<DllModule> getLoadedModules();
int containsModuleType(string modType);

HINSTANCE getModuleLibraryByType(std::string type);

// singleton getInstance function
static ModuleLoader & getInstance()
{
    static ModuleLoader instance;
    return instance;
}
};

s2ws方法将公共字符串转换为宽字符串(我发布它以防万一):

std::wstring ModuleLoader::s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}

我多次检查并在应用程序的所有级别执行异常抛出时释放堆对象。 此外,如果我删除了DWORD,ostringstream和字符串对象(在堆栈上分配),内存泄漏会增加...所以它也必须与这些相关联。我无法想象如何删除这部分代码应该有助于其他地方的堆内存释放:

DWORD werror = GetLastError();
ostringstream stream;
stream << werror;
string errstring = "Error " + stream.str();
errstring.append(" - " + libraryName);

答案 1 :(得分:0)

好的,我设法将泄漏减少到原来的两个中只有两个:

Dumping objects ->
{312} normal block at 0x0045FDC8, 8 bytes long.
Data: <( (     > 28 ED 28 00 00 00 00 00 
{311} normal block at 0x0045F810, 8 bytes long.
Data: <D (     > 44 ED 28 00 00 00 00 00 
Object dump complete.

我使用了_CrtSetBreakAlloc(x)函数,其中x是泄漏的数量(例如在上面的情况下为311或312)并且找到了,其中分配了未分配的内存。很难相信它,但分配确实发生在这些方面:

string errstring = "Error " + stream.str();
and
errstring.append(" - " + libraryName);

我通过在堆上动态分配字符串和流来删除泄漏,然后创建异常并将其存储在临时变量中,随后解除分配字符串和流变量,最后抛出异常本身:

DWORD werror = GetLastError();
    ostringstream *stream = new ostringstream();
    *stream << werror;
    string *errstring = new string("Error ");
    errstring->append(stream->str());
    errstring->append(" - ");
    errstring->append(libraryName);

    ProbeCoreException e = LibraryLoadException(MOD_ERROR_LIB_LOAD,
            errstring->c_str());

    delete errstring;
    delete stream;

    throw e;

在将字符串参数传递给“加载”函数本身时,最后两次分配(再次令人难以置信):

loader.load("notexisting.dll", "TEST", &callbackFunction);

我正在考虑由于类是单例而发生的泄漏,该类是根据防漏单例规则创建的,但是,这里提到:

C++ Singleton design pattern

似乎唯一可能摆脱隐藏泄漏的机会是将字符串指针作为参数传递,然后明确地将它们解除错误...