C++应用程序:如何正确删除/释放已分配的对象

C++ app: How to properly delete/release an allocated object?

本文关键字:释放 分配 对象 删除 应用程序 何正确 C++      更新时间:2023-10-16

我在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++中编程时,您还应该考虑将这种单例机制封装在某个类中;但这是一个不同的话题。

  1. 删除commInterface;仅此而已!:)在结尾处全部删除程序

  2. 为什么要做很多对象。我正在开发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是线程安全的)。