c++构造函数类中的内存泄漏

Memory leak in class Constructor of c++

本文关键字:内存 泄漏 构造函数 c++      更新时间:2023-10-16

我有以下类

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就会有内存泄漏。

如何避免内存泄漏?

使用vector可以很容易地避免这种问题。

class CSample
{
   std::vector<char> m_pChar;
   std::vector<double> m_pDouble;
   CSample():m_pChar(1000), m_pDouble(1000000)
   {
   }
};

一般来说,应该以编写不需要析构函数的类为目标。这使得他们简单地遵守"三原则"。

有几种安全的方法:

  • 将内存管理委托给其他类,如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;
        }
    };
    

如果不将资源管理委托给另一个类,则还需要提供适当的复制构造函数和复制赋值操作符,因为默认的(按成员复制/赋值)具有错误的语义。显然,第一种方法是最简单的,也是最不容易出错的。