【问题标题】:Memory leak in class Constructor of c++c ++的类构造函数中的内存泄漏
【发布时间】:2014-07-22 05:44:28
【问题描述】:

我有以下课程

class CSample
{
   char* m_pChar;
   double* m_pDouble;

 CSample():m_pChar(new char[1000]), m_pDouble(new Double[1000000])
{
}
~CSample()
{
   if(m_pChar != NULL) delete [] m_pchar;
   if(m_pDouble != NULL) delete [] m_pDouble;
}
};

在我的 main() 函数中,我正在尝试创建 CSample 的对象

int main()
{
    try
  {
    CSample objSample;
  }

catch(std::bad_alloc)
{
  cout<<"Exception is caught !!! Failed to create object";
}

}

说在构造函数的初始化列表中为 m_pDouble 分配内存时,由于可用内存不足而引发异常。但是对于 m_pChar 它已经分配了。由于没有创建对象本身,因此不会调用析构函数。那么m_pChar 就会出现内存泄漏。

如何避免这种内存泄漏?

【问题讨论】:

  • @jxh 实际上,没有。 std::default_deleteT[] 有一个专门化的处理方法。
  • 一般来说,要么一个类管理一个资源,要么它有业务相关的方法。试图同时做这两件事会导致灾难。
  • @T.C.:在 ideone 上,我必须传递专业化才能使 unique_ptr 使用数组分配。
  • @jxh 好吧,你确实需要std::unique_ptr&lt;char []&gt;,但你不应该明确传递default_delete&lt;char []&gt;...
  • @T.C.:那不是让unique_ptr 存储char (*)[] 吗?

标签: c++ memory memory-management memory-leaks


【解决方案1】:

改用vector 可以轻松避免此类问题。

class CSample
{
   std::vector<char> m_pChar;
   std::vector<double> m_pDouble;

   CSample():m_pChar(1000), m_pDouble(1000000)
   {
   }
};

一般来说,您的目标应该是编写不需要析构函数的类。这使他们微不足道地服从Rule of Three

【讨论】:

【解决方案2】:

有几种方法可以安全地做到这一点:

  • 将内存管理委托给另一个类,例如std::unique_ptr (C++11) 或std::vector

    class CSample
    {
       std::unique_ptr<char []> m_pChar;
       std::unique_ptr<double []> m_pDouble;
    
       CSample():m_pChar(new char[1000]), m_pDouble(new double[1000000])
       {
       }
    };
    

    语言保证如果抛出异常,任何已经构造的类成员都将被销毁,这将释放分配的内存。

  • 改为在构造函数体中执行内存分配,并使用本地 try 块:

    class CSample
    {
       char* m_pChar;
       double* m_pDouble;
    
        CSample() : m_pChar(nullptr), m_pDouble(nullptr)
        {    
            try {
                m_pChar = new char[1000];
                m_pDouble = new double[1000000];
            }
            catch(...){
                if(m_pChar) delete [] m_pChar;
                if(m_pDouble) delete [] m_pDouble;
                throw;
            }    
        }
        CSample(const CSample &other) { /* perform deep copy */ }
        CSample &operator=(const CSample &other) { /* perform deep copy of other and release my resources */ }        
        ~CSample()
        {
           if(m_pChar) delete [] m_pchar;
           if(m_pDouble) delete [] m_pDouble;
        }
    
    };
    
  • 使用 C++11 委托构造函数。当目标(非委托)构造函数完成执行时,该对象被视为已构造,因此如果委托构造函数稍后抛出,则将调用析构函数。

    class CSample
    {
        char* m_pChar;
        double* m_pDouble;
    
        CSample(int) : m_pChar(nullptr), m_pDouble(nullptr) { }
        CSample() : CSample(0)
        {    
            m_pChar = new char[1000];
            m_pDouble = new double[1000000];
        }
        CSample(const CSample &other) { /* perform deep copy */ }
        CSample &operator=(const CSample &other) { /* perform deep copy of other and release my resources */ }
        ~CSample()
        {
           if(m_pChar) delete [] m_pchar;
           if(m_pDouble) delete [] m_pDouble;
        }
    
    };
    

如果您不将资源管理委托给另一个类,您还需要supply proper copy constructors and copy assignment operators,因为默认的(按成员复制/分配)具有错误的语义。很明显,第一种方法是迄今为止最简单且最不容易出错的方法。

【讨论】:

  • 您介意解释一下为什么您宁愿这样做而不是使用标准容器的其他答案吗?
  • @BenjaminGruenbaum 为了说明不使用 vectorunique_ptr 的情况下正确地做这件事有多难?
  • 如果您要描述如何以艰难的方式做到这一点,那么至少要提及Rule of Three - 这里发布的示例是死亡陷阱。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-28
  • 2021-06-17
  • 2013-01-12
  • 2019-08-19
  • 1970-01-01
  • 2014-08-23
相关资源
最近更新 更多