C++ “资源获取是初始化”;";

C++ “资源获取是初始化”;";,c++,raii,C++,Raii,在下面的例子中 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;

在下面的例子中

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";
    };
};
X类
{
int*r;
公众:
X(){

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

不必。如果失败,构造函数将抛出
std::bad\u alloc

  • 调用Y构造函数
  • 调用X构造函数
  • newint[]
    分配失败,抛出
    std::bad_alloc
    。内存从未分配
  • 因为X从未完成构造,所以Y构造函数失败,Y也从未完成构造
  • 因此没有泄漏

    在这里,Y的析构函数是安全的,因为在Y构造函数中,析构函数不分配任何内存。如果我们也需要在Y构造函数中分配内存,该怎么办?

    您仍然没有问题。分配失败将抛出
    std::bad\u alloc
    。该失败是使用您的类的人的责任

  • 调用Y构造函数
  • 调用X构造函数
  • newint[]
    分配成功
  • Y构造函数现在以某种方式失败,需要抛出异常(例如分配失败)
  • 异常抛出机制展开调用堆栈,并对任何局部变量(在本例中包括X)调用析构函数
  • X的析构函数
    delete[]
    s
    newint[]
  • 同样,这里没有泄露任何资源

    请注意,您确实需要警惕多重分配。例如:

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

    现在我们有一个资源泄漏。当从构造函数抛出异常时,对象从来没有完全构造过。因为它从来没有完全构造过,所以它永远不会调用它的析构函数。因此,我们泄漏
    r

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

    不必。如果失败,构造函数将抛出
    std::bad\u alloc

  • 调用Y构造函数
  • 调用X构造函数
  • newint[]
    分配失败,抛出
    std::bad_alloc
    。内存从未分配
  • 因为X从未完成构造,所以Y构造函数失败,Y也从未完成构造
  • 因此没有泄漏

    在这里,Y的析构函数是安全的,因为在Y构造函数中,析构函数不分配任何内存。如果我们也需要在Y构造函数中分配内存,该怎么办?

    您仍然没有问题。分配失败将抛出
    std::bad\u alloc
    。该失败是使用您的类的人的责任

  • 调用Y构造函数
  • 调用X构造函数
  • newint[]
    分配成功
  • Y构造函数现在以某种方式失败,需要抛出异常(例如分配失败)
  • 异常抛出机制展开调用堆栈,并对任何局部变量(在本例中包括X)调用析构函数
  • X的析构函数
    delete[]
    s
    newint[]
  • 同样,这里没有泄露任何资源

    请注意,您确实需要警惕多重分配。例如:

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

    现在我们有一个资源泄漏。当从构造函数抛出异常时,对象从未完全构造。因为它从未完全构造过,所以它永远不会调用它的析构函数。因此,我们在
    X
    的构造函数中泄漏
    r
    ,如果
    new
    失败,它将抛出异常(
    std::bad_alloc
    )。这意味着构造函数永远不会完成,因此对象的生存期永远不会开始,因此其析构函数永远不会被调用(没有对象),并且
    new[]
    delete[]
    之间没有不匹配。(
    X
    应该有一个用户声明的复制构造函数和一个用户声明的复制赋值操作符,因为如果构造成功并且对象被复制或赋值,提供的实现将破坏这一保证。)

    Y
    中,如果它在其构造函数中分配内存,并且此分配成功,则需要确保在其余的构造在任何点抛出异常时释放此内存,并且如果构造函数完成,则在析构函数中释放内存(假设内存设计为持续对象生命周期的长度)


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

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

    Y
    中,如果它在其构造函数中分配内存,并且此分配成功,则需要确保在其余的构造在任何点抛出异常时释放此内存,并且如果构造函数完成,则在析构函数中释放内存(假设内存设计为持续对象生命周期的长度)

    为了简化这一过程,任何分配的内存都应该立即交给一个对象,该对象的唯一职责就是释放内存。让一个类管理指向多个分配内存块的原始指针是一个复杂的过程