构造函数初始化列表中的异常
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;
}
};
b
在 c
之前初始化,所以如果 getC()
抛出然后 b
指向的内存将被泄漏。
您可以通过以下两种方式之一解决该问题:
- 将分配移动到构造函数主体并使用正常的异常处理:
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;
}
};
- 使用智能指针:
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;
};
假设我有以下代码
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? Willb
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;
}
};
b
在 c
之前初始化,所以如果 getC()
抛出然后 b
指向的内存将被泄漏。
您可以通过以下两种方式之一解决该问题:
- 将分配移动到构造函数主体并使用正常的异常处理:
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;
}
};
- 使用智能指针:
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;
};