1

过去,我们在软件中遇到过各种内存泄漏。我们发现这些发生主要是由于我们自己的“免费”-Methods 的错误使用,它释放了 Queue-Message-Data 等。

问题是,在我们最深入的工具函数中,有两种方法可以释放动态分配的内存,具有以下签名:

void free (void *pData);
void free (void **ppData);

两种方法基本上都做同样的事情,除了第二种方法首先取消引用数据。我知道只用这两者之一就可以做所有事情,但可以说该软件多年前就是这样设计的,现在到处都有代码使用这两者。

当有人像这样实现对这些方法的调用时,问题就来了:

QueueMessage *pMsg;
pMsg = myQueue.read(...); // dynamically allocates space for the msg and fills it
// Do something
myQueue.free(&pMsg); // << WRONG!

上面的代码应该将指向消息的指针传递给自由方法。基本上它会工作,但由于编译器不知道在这种情况下使用哪个函数,它使用free(void *pData)然后尝试释放Pointer的方法,而不是Pointer 指向的内存。当然,解决方案很简单,要么:要么

myQueue.free(pMsg);

myQueue.free((void**)&pMsg);

两者都会起作用。既然我已经描述了问题和解决方案,我想知道:有什么方法可以确保使用这些方法的程序员以正确的方式使用它们?我已经阅读了VS2005 中的标头注释,它们非常有用,但似乎并没有满足我的需要。如果指针的引用被传递给第一个方法,如果有一种方法可以产生警告,那就太好了,这样程序员至少会得到他的代码有问题的提示。

顺便说一句,我正在使用 Microsoft VS2005,如果需要,可以升级到 VS2008。这是一个迁移到 VS2005 的 C++ 应用程序,因此与 .NET 兼容。

4

1 回答 1

5

尝试模板:

template <class T> void free (T *pData);
template <class T> void free (T **ppData);

这应该为您提供参数类型的精确匹配,因此编译器应该调用正确的实现。

您可以free用模板化版本替换原始的 void-pointer 实现,或者让模板化版本调用原始的 void-pointer 版本:

template <class T> void free (T *pData)
{
    free(static_cast<void *>(pData));
}

template <class T> void free (T **ppData)
{
    free(static_cast<void **>(ppData));
}

编辑:那么原始版本中发生了什么?

重载函数解析的规则相当复杂,但原则上,编译器首先尝试找到参数类型的精确数学,如果找不到它们,它可以应用各种自动类型转换。

这些自动类型转换之一是任何指针(包括指向指针的指针)都可以转换为void *指针。但是,没有规则可以将类型化的指针指针 ( T **) 自动转换为 void 指针指针 ( void **)。这就是为什么你所有的电话都去的原因free(void *)

当您引入模板化版本时,情况会发生变化。编译器现在可以为您进行的每个调用找到完全匹配的 -free(T *)或者free(T **)- 这使它能够调用正确的版本。

于 2009-09-04T09:26:27.113 回答