1

在下面的例子中

class X
{
    int *r;
public: 
    X() {
        cout << "X is created";
        r = new int[10];
    };
    ~X() {
        cout<< "X is destroyed";
        delete [] r;
    };
};
class Y
{
public: 
    Y() {
        X x;
        throw 44;
    }; 
    ~Y() {
        cout << "Y is destroyed";
    };
};

我从一个站点获得了这个 RAII 示例,并提出了一些疑问。请帮忙。

  1. 在 x 的构造函数中,我们没有考虑“如果内存分配失败”的场景。
  2. 这里 Y 的析构函数是安全的,因为 y 构造函数没有分配任何内存。如果我们还需要在 y 构造函数中进行一些内存分配怎么办?
4

2 回答 2

9

在 的构造函数中X,如果new失败则抛出异常 ( std::bad_alloc)。这意味着构造函数永远不会完成,因此对象的生命周期永远不会开始,因此它的析构函数永远不会被调用(没有对象)并且和之间没有不new[]匹配delete[]。(X应该有一个用户声明的复制构造函数和一个用户声明的复制赋值运算符,因为如果构造成功并且对象被复制或分配,则提供的实现将破坏此保证。)

Y,如果它在它的构造函数中分配内存并且这个分配是成功的,那么它需要确保这个内存被释放如果构造的其余部分在任何时候抛出一个异常,并且如果构造函数完成了内存在析构函数中被释放(假设内存被设计为持续对象生命周期的长度)。

为了使这更容易,任何分配的内存都应该立即交给一个对象,该对象的唯一职责是释放内存。让一个类管理指向多个分配内存块的原始指针是复杂且容易出错的管理代码的秘诀。

于 2010-03-20T17:32:43.220 回答
8

在 x 的构造函数中,我们没有考虑“如果内存分配失败”的场景。

你不必。如果失败,构造函数将抛出std::bad_alloc. IE:

  1. Y 构造函数被调用。
  2. X 构造函数被调用。
  3. new int[]分配失败,抛出std::bad_alloc. 永远不会分配内存。
  4. 因为 X 从未完成构造,所以 Y 构造函数失败并且 Y 也从未完成构造。

因此没有泄漏。

这里 Y 的析构函数是安全的,因为 y 构造函数没有分配任何内存。如果我们还需要在 y 构造函数中进行一些内存分配怎么办?

你仍然没有问题。分配失败会抛出std::bad_alloc. 这种失败是使用你的班级的人的责任。

  1. Y 构造函数被调用。
  2. X 构造函数被调用。
  3. new int[]分配成功。
  4. Y 构造函数现在以某种方式失败并且需要抛出异常(例如分配失败)。
  5. 异常抛出机制展开调用堆栈并在任何局部变量上调用析构函数,在本例中包括 X。
  6. X 的析构函数delete[]new int[].

同样,这里没有资源泄漏。

请注意,您确实需要警惕多次分配。IE:

class Foo
{
    int * r;
public:
    Foo() {
        r = new int;
        throw myException;
    };
    ~Foo() {
        delete r;
    };
};

现在我们有资源泄漏。当构造函数抛出异常时,对象永远不会完全构造。由于它从未完全构造,因此永远不会调用它的析构函数。因此我们泄漏r

于 2010-03-20T17:32:37.253 回答