我有一个分配2D数组的功能,以免花费超出我需要的内存:
_>
template <class Xvar> Xvar** New2 (unsigned int rows,unsigned int cols)
{
Xvar** mem;
unsigned int size, i;
size = rows * cols;
mem = new Xvar* [rows];
mem [0] = new Xvar [size];
for (i=1;i<rows;i++)
mem [i] = &mem [0][i*cols];
return mem;
}
现在,我需要检查是否已分配内存。 (处理内存分配错误), 不降低功能的性能。
我应该为每个内存分配使用try-catch块,还是只为函数使用唯一的try-catch块。
template <class Xvar> Xvar** New2 (unsigned int rows,unsigned int cols)
{
Xvar** mem;
unsigned int size, i;
size = rows * cols;
try {
mem = new Xvar* [rows];
}
catch (...) { assert (...) }
try {
mem [0] = new Xvar [size];
} catch (...) { assert (...) }
for (i=1;i<rows;i++)
mem [i] = &mem [0][i*cols];
return mem;
}
或类似的东西:
template <class Xvar> Xvar** New2 (unsigned int rows,unsigned int cols)
{
try {
Xvar** mem;
unsigned int size, i;
size = rows * cols;
mem = new Xvar* [rows];
mem [0] = new Xvar [size];
for (i=1;i<rows;i++)
mem [i] = &mem [0][i*cols];
return mem;
}catch (...) { assert (...) }
}
我认为,不建议采用第二种方法,因为如果第一种新方法失败,则mem为NULL, 因此,如果我们执行mem [0],我们将访问未分配的内存,以便应用程序在此时失败,并且无法捕获错误。
答案 0 :(得分:3)
第二种方式,如果第一个new
失败,那么评估会立即跳转到catch块,甚至不会尝试访问mem[0]
。
在任何情况下,如果您希望允许分配失败并轻松检测到这种情况,您应该使用nothrow
变体,如果分配失败,它只返回NULL
。像
mem = new (nothrow) Xvar*[rows];
if (!mem) {
// allocation failed, do whatever you want
}
mem[0] = new (nothrow) Xvar[size];
if (!mem[0]) {
// allocation failed, do whatever you want
}
答案 1 :(得分:0)
根本不捕捉异常,只需在函数展开时使用RAII清理内存:
template <class Xvar> Xvar** New2 (unsigned int rows,unsigned int cols)
{
unsigned int size = rows * cols;
std::unique_ptr<Xvar*[]> mem(new Xvar* [rows]);
mem[0] = new Xvar [size];
for (i=1; i<rows; i++)
mem[i] = &mem[0][i*cols];
return mem.release();
}
new
失败,你的函数没有做任何事情并且没有泄露任何内存:std::bad_alloc
被抛出,就是这样unique_ptr
析构函数会处理那个内存,所以你也可以了。正如GmanNickG指出的那样,这仍然是可怕的代码。这是你要求的可怕代码,但我不想给人留下我赞同原始设计的印象。我不。太可怕了。
一旦来电者成功获得他们的Xvar**
,唯一可以解决这个问题的方法是:
int **ii = New2<int>(x, y);
...
delete [] ii[0];
delete [] ii;
这是脆弱和令人不快的。两个更好的设计是:
使用管理存储的真实矩阵/ 2d数组模板类,可以按值返回。然后,当此值对象超出范围
使用带自定义删除器的智能指针
template <typename T> struct 2dDelete
{
void operator() (T **rows) {
delete [] rows[0];
delete [] rows;
}
};
template <typename T>
std::unique_ptr<T*[], 2dDelete<T>> 2dNew (unsigned rows, unsigned cols)
{
unsigned size = rows * cols;
std::unique_ptr<Xvar*[]> tmp(new Xvar* [rows]);
tmp[0] = new Xvar [size];
for (i=1; i<rows; i++)
tmp[i] = &tmp[0][i*cols];
// hand-over from the intermediate pointer (which only owned the
// top-level row allocation) to the caller's pointer (which will
// own, and clean up, the whole thing).
return std::unique_ptr<T*[], 2dDelete<T>> arr(mem.release());
}