【问题标题】:C++ app: How to properly delete/release an allocated object?C++ 应用程序:如何正确删除/释放分配的对象?
【发布时间】:2017-06-02 14:11:53
【问题描述】:

我在 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()并多次调用它,我会有很多内存不是de -已分配。
上面的代码是一个简化版本。事实上,代码有很多退出点/返回。它还有一些抛出异常的函数(我不能 100% 确定它们都被正确捕获和处理)。
所以,我想在这种情况下,在所有 returns 之前添加 delete commInterface; 是不够的......
因此,我的问题是:有没有办法正确删除/释放commInterface,以便嵌入和使用上述模块而不必担心内存泄漏?可能是智能指针或其他想法...?
两点说明
1)我已启用 C++ 11;
2) 我不使用(也不想使用)boost
提前感谢您的时间和耐心。

【问题讨论】:

  • 我建议考虑智能指针。
  • 只有一个commInterface 可以在应用程序的整个生命周期内使用是否明智? - 即确保您只调用一次commInit
  • 真的需要动态分配吗?
  • 我很好奇,boost 过敏背后的原因是什么?并不是说在这种情况下需要提升。
  • @user2079303:不是过敏。但有些同事建议的正是你所说的:不需要提升......

标签: c++ c++11 pointers memory-management smart-pointers


【解决方案1】:

在我看来,这是一个应该避免global variables 的方案。 使用智能指针以下列方式完成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 对象,您可以将singleton pattern 用于您的UartComm 对象。

【讨论】:

  • @groenhen 如果答案对您有用,请点赞;)这是免费的!
  • 同意,只说一句:使用make_unique时,也要使用auto:auto commInterface = std::make_unique...
  • @Rene Thnx,被采纳。
  • @YSC:我总是这样做,别担心 :) 99% 的时间。哦,我从不投反对票:)
  • @YSC 它不会改变变量的结果类型中的任何内容。但它的类型较少,如果类型名称发生变化,您只需在一个地方更改它。
【解决方案2】:

我想commInterface 是一个单例,可以提供给应用程序的其他部分。我进一步认为,您希望确保在应用程序中使用单例对象时对其进行初始化。

然后,我建议创建变量static,以免将其暴露给其他翻译单元(然后可能会在未初始化的状态下访问它);相反,提供一个访问器函数,如UartComm *getCommInterface ()。在访问变量之前,请检查它是否已初始化。以下面的代码为例:

static UartComm* commInterface = nullptr;

void commInit()
{
    if (!commInterface)
        commInterface = new UartComm;
}

UartComm *getCommInterface () {
    commInit();
    return commInterface;
}

请注意,当您使用 C++ 编程时,您还应该考虑将这种单例机制封装在某个类中;但这是一个不同的话题。

【讨论】:

  • 另一种 C 风格的翻译单元静态变量是嵌入在匿名命名空间中的变量。
  • + 它没有回答问题
【解决方案3】:
  1. 删除commInterface;就是这样!:) 在末尾全部删除 程序。

  2. 为什么会有很多对象。我正在为 RS-232 开发应用程序 端口(COM 端口),它有 1 个对象“RS-232”用于通信。要么 你有以太网连接吗?

【讨论】:

    【解决方案4】:

    有没有办法正确删除/释放 commInterface 以便嵌入和使用上述模块而不必担心内存泄漏?也许是智能指针或其他一些想法......?

    这是另一个想法:使用静态实例。它将在首次使用时构建,并在程序结束时销毁。这也将比动态分配更快(不过,对于单个分配来说并不重要)。

    UartComm& commInterface() {
       static UartComm interface;
       return interface;
    }
    

    【讨论】:

      【解决方案5】:

      一般规则是必须释放动态分配的对象。并且每种分配形式都需要相应的解除分配。

      尽管现代操作系统通常会在程序结束时为您清理(无需使用运算符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
        }
        

      当然,还有许多其他选择。比如根本不使用动态分配,直接按值返回一个对象。

      【讨论】:

        【解决方案6】:

        如果您可以删除全局变量,我会使用 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 是线程安全的)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多