我有下面的代码片段,这是为了演示函数-尝试块的构造函数用例。因为Controller
的构造函数抛出没有对象,它被成功创建,即client_
成员变量应该不再存在,当异常被抛出,是正确的吗?如果是这样,那么调用deinit()
来清理client_
是安全的吗?如果不安全,如果client_
是一个原始指针,那么我如何捕获异常并为它做清理工作?
class Client {
public:
Client() { std::cout << "Client()" << std::endl; }
~Client() { std::cout << "~Client()" << std::endl; }
void Start() { throw "Start() failed with exception"; }
};
class Controller {
private:
std::shared_ptr<Client> client_;
public:
Controller() try {
client_ = std::make_shared<Client>();
client_->Start();
} catch (...) {
deinit();
}
~Controller() {
deinit();
}
private:
void deinit() {
if (client_) {
client_.reset();
}
}
};
当抛出异常时,client_成员变量应该不再存在,这是正确的吗?
不,这是不正确的。在新类实例的client_
成员构造完成后抛出异常。
类的构造函数只有在新类实例的所有成员都构造完成后才会被调用;构造函数的工作是在构造了所有类成员之后,完成构造类本身的任务。
一旦进入构造函数,就可以保证新类实例的所有成员都被完全构造了。你可以把它带到银行去。
调用deinit()是否安全
好吧,这是"安全"的。因为它定义了行为。但是,在这种情况下,这是完全没有必要的。如果在构造函数中抛出异常,则按照相反的构造顺序自动销毁新类实例的所有构造成员。client_
将被正常销毁,就像类被完全构造并在稍后的某个时刻被销毁一样。
如果client_是一个原始指针,那么我如何捕获异常并为它做清理工作?
与所示代码的操作方式基本相同。但它不是一个原始指针,而是一个shared_ptr
,它可以处理这个问题。