内存分配和Try-Catch Block

时间:2013-01-08 22:29:24

标签: c++ memory-management

我有一个分配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],我们将访问未分配的内存,以便应用程序在此时失败,并且无法捕获错误。

2 个答案:

答案 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;

这是脆弱和令人不快的。两个更好的设计是:

  1. 使用管理存储的真实矩阵/ 2d数组模板类,可以按值返回。然后,当此值对象超出范围

  2. 时,将回收内存
  3. 使用带自定义删除器的智能指针

    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());
    }