我在Ubuntu 14.04上使用GCC编译器。
我有一个小型通信应用程序(不是我写的),我打算将它嵌入到我自己的应用程序中
小型通信应用程序代码如下所示:
UartComm * commInterface;
...
void commInit()
{
commInterface = new UartComm;
}
// other functions here that use "commInterface"
// ...
int main()
{
commInit();
...
if(smthing_1)
return (-1);
if(smthing_2)
return (-2);
...
if(smthing_n)
return (-n);
//
return (0);
}
--
如上所述,原始代码中没有delete commInterface;
,因此,如果我将上述代码嵌入到我的应用程序中,将main()
重命名为commFunction()
并多次调用它,我将有大量内存未被取消分配
以上代码是一个简化版本。事实上,该代码有许多退出点/返回点。它还有一些抛出异常的函数(我不能100%确定它们是否都被捕获并正确处理)
所以,我想在所有returns
之前添加delete commInterface;
在这种情况下是不够的
因此,我的问题是:是否有一种方法可以正确删除/释放commInterface
,以便嵌入和使用上述模块,而不必担心内存泄漏?也许是智能指针或其他想法
两个备注:
1)我启用了C++11
2)我不使用(也不想使用)boost
提前感谢您的时间和耐心。
在我看来,这是一种避免全局变量的方案。使用智能指针以以下方式完成RAII,特别是std::unique_ptr
:
int commFunction() {
auto commInterface = std::make_unique<UartComm>();
...
if(smthing_1)
return (-1);
if(smthing_2)
return (-2);
...
if(smthing_n)
return (-n);
//
return (0);
}
但是请注意,std::make_unique
是C++14,或者对于C++11使用以下内容:
std::unique_ptr<UartComm> commInterface(new UartComm);
还要注意,使用建议的方案,每次调用commFunction
时都会创建一个新的UartComm
对象。如果创建UartComm
是一项昂贵的操作,或者您的设计需要重用单个UartComm
对象,那么您可以为UartComm
对象使用singleton模式。
有没有办法正确删除/释放commInterface,以便嵌入和使用上述模块,而不用担心内存泄漏?也许是智能指针或其他想法。。。?
这里有另一个想法:使用静态实例。它将在首次使用时构建,并在程序结束时销毁。这也将比动态分配更快(不过,对于单个分配来说并不重要)。
UartComm& commInterface() {
static UartComm interface;
return interface;
}
我假设commInterface
是要提供给应用程序其他部分的单例。我进一步认为,您希望确保在应用程序中使用singleton对象时初始化它。
然后,我建议使变量static
不暴露给其他翻译单元(这些翻译单元可能会在未初始化的状态下访问它);相反,提供一个类似UartComm *getCommInterface ()
的访问器函数。在访问变量之前,请检查变量是否已初始化。请参阅下面的代码作为示例:
static UartComm* commInterface = nullptr;
void commInit()
{
if (!commInterface)
commInterface = new UartComm;
}
UartComm *getCommInterface () {
commInit();
return commInterface;
}
注意,当您在C++中编程时,您还应该考虑将这种单例机制封装在某个类中;但这是一个不同的话题。
-
删除
commInterface
;仅此而已!:)在结尾处全部删除程序 -
为什么要做很多对象。我正在开发RS-232应用程序端口(COM端口),并且它有一个用于通信的对象"RS-232"。或你有以太网连接吗?
一般规则是必须释放动态分配的对象。每种分配形式都需要相应的解除分配。
尽管现代操作系统通常会在程序结束时为您清理(无需使用运算符delete
),但依赖这一点是一个坏习惯——并非所有操作系统都能保证这一点。操作系统确实存在漏洞。此外,还有你的情况——如果你发现你想把main()
重命名为其他东西来重用它,你就有内存泄漏的问题——如果你在代码中清理,而不是在程序退出时依赖清理,你的问题就会很容易避免。
在您的情况下,分配是
commInterface = new UartComm;
因此相应的解除分配是
delete some_pointer;
其中some_pointer
可以是commInterface
(假设它是可见的)或存储相同值的相同类型的另一指针。
一般来说,还有许多其他方法可以确保对象被正确释放。例如
- 在分配它的同一个函数中释放它。问题是在函数返回后无法使用对象
将指针返回给调用者,调用者将其释放。
UartComm *commInit() { UartComm *commInterface = new UartComm; // whatever return commInterface; } int main() { UartComm *x = commInit(); // whatever delete x; }
与上面一样,但使用智能指针,因此没有必要手动解除分配(智能指针会这样做)。
std::unique_pointer<UartComm> commInit() { std::unique_pointer<UartComm> commInterface(new UartComm); // whatever return commInterface; } int main() { std::unique_pointer<UartComm> x = commInit(); // whatever // x will be cleaned up as main() returns - no need to release }
当然,还有许多其他选择。例如,根本不使用动态分配,只需按值返回一个对象。
如果您可以删除全局变量,我会使用user2079303提供的单例解决方案或101010的本地unique_ptr版本。哪一个取决于每次调用commFunction()
时是否要重新创建接口。
如果删除/更改全局变量是不可能的我看到了两种代码更改最小的可能性(同样取决于您是否要重新创建对象):
UartComm * commInterface = nullptr; //nullptr not necessary but makes it more explicit
...
void commInit()
{
//only create commInterface the first time commInit() is called
if( !commInterface )
commInterface = new UartComm;
}
或
UartComm * commInterface = nullptr; //nullptr not necessary but makes it more explicit
...
void commInit()
{
//destruct old UartComm and create new one each time commInit is called
delete commInterface;
commInterface = new UartComm;
}
同样,这些不是我通常推荐的解决方案,但在前面提到的限制下,它们可能是你能做的最好的
还要记住,这些解决方案都不是线程安全的(即使UartComm是线程安全的)。