我有以下课程
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会有内存泄漏。
你如何避免这种内存泄漏?
答案 0 :(得分:5)
您可以使用vector
来轻松避免此类问题。
class CSample
{
std::vector<char> m_pChar;
std::vector<double> m_pDouble;
CSample():m_pChar(1000), m_pDouble(1000000)
{
}
};
一般来说,您的目标应该是编写不需要析构函数的类。这使得他们琐碎地服从Rule of Three。
答案 1 :(得分:3)
有几种方法可以安全地执行此操作:
将内存管理委派给另一个类,例如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,因为默认的(成员复制/赋值)具有错误的语义。显而易见的是,第一种方法既是最简单的,也是最不容易出错的。