构造函数初始化列表中的异常

Exception in constructor initialization list

假设我有以下代码

class B
{ /* implementation*/ };

class A
{
    B b;
    char * c;
    A() : b(), c(new char[1024])
    {}
    ~A()
    {
        delete[] c;
    }
};

int main()
{
     A* a = nullptr;
     try 
     {
          a = new A();
     }
     catch(...)
     {
         
     }
}

我想了解如果c(new char[1024])会抛出异常会发生什么?请问b正确销毁了吗?调用者可以捕获这个异常吗?如果是,a 的值是多少? ~A()会被调用吗?调用构造函数初始化列表中可能抛出异常的函数是一种好习惯吗?

I want to understand what will happen if c(new char[1024]) will throw exception? Will b correctly destroyed?

是的。当构造函数抛出时,任何 已经 构造的成员和基础 类 都会自动销毁。

Can caller catch this exception?

是的。

If yes, what will be the value of a?

nullptr,因为这是您初始化 a 的内容,并且在您可以将新值分配给 a.

之前抛出异常

Will ~A() be called?

没有。如果 A() 没有完全完成,则不会调用 ~A() 析构函数。而且您也没有在任何完全构造的 A 对象上调用 delete

Is it good practice to call functions in the constructor initialization list that can throw an exception?

很好。只需确保任何以前构造的成员都被正确清理以避免任何泄漏。

例如:

class A
{
    B * b;
    char * c;

    B* getB() { return new B; }
    char* getC() { return new char[1024]; }

public:
    A() : b(getB()), c(getC())
    {}

    ~A()
    {
        delete b;
        delete[] c;
    }
};

bc 之前初始化,所以如果 getC() 抛出然后 b 指向的内存将被泄漏。

您可以通过以下两种方式之一解决该问题:

  1. 将分配移动到构造函数主体并使用正常的异常处理:
class A
{
    B * b;
    char * c;

    B* getB() { return new B; }
    char* getC() { return new char[1024]; }

public:
    A()
    {
        b = getB();
        try
        {
            c = getC();
        }
        catch(...)
        {
            delete b;
            throw;
        }
    }

    ~A()
    {
        delete b;
        delete[] c;
    }
};
  1. 使用智能指针:
class A
{
    std::unique_ptr<B> b;
    std::unique_ptr<char[]> c;

    std::unique_ptr<B> getB() { return std::make_unique<B>(); }
    std::unique_ptr<char[]> getC() { return std::make_unique<char[]>(1024); }

public:
    A() : b(getB()), c(getC())
    {}

    ~A() = default;
};