c++ - 如果构造函数抛出 RAII 和成员?
问题描述
我之前曾在异常已关闭且内存分配失败意味着我们终止程序的环境中工作。现在处理异常我想知道以下内容的精确语义:
class Foo {
std::unique_ptr<Bar> x;
std::unique_ptr<Bar> y;
public:
Foo(): x{new Bar}, y{new Bar} {}
};
我的问题是在分配时new Bar
抛出时会发生什么?y
我假设x
调用了析构函数,以便清理第一个分配。语言如何保证这一点?任何人都知道解释精确语义的标准引用吗?
解决方案
是的,所有完全构建的成员都将被销毁。您的对象不会处于任何“半生不死”状态。不会发生内存泄漏。
[except.ctor]/3
:如果一个对象的初始化或销毁(不是通过委托构造函数)被异常终止,则为对象的每个直接子对象调用析构函数,对于完整的对象,调用已完成初始化的虚拟基类子对象([dcl .init]) [..]子对象的销毁顺序与其构造完成的相反。[..]
我们可以自己证明这一点:
#include <memory>
#include <iostream>
struct Bar
{
Bar(const char* name, bool doThrow = false) : m_name(name)
{
if (doThrow)
{
std::cout << name << ": Bar() throwing\n";
throw 0;
}
std::cout << name << ": Bar()\n";
}
~Bar() { std::cout << m_name << ": ~Bar()\n"; }
private:
const char* m_name;
};
class Foo {
std::unique_ptr<Bar> x;
std::unique_ptr<Bar> y;
public:
Foo(): x{new Bar("A")}, y{new Bar("B", true)} {}
};
int main()
{
try
{
Foo f;
}
catch (...) {}
}
// g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
// A: Bar()
// B: Bar() throwing
// A: ~Bar()
(现场演示)
事实上,这就是所谓的“智能指针”的主要好处之一:异常安全。如果x
是原始指针,您会泄露它指向的东西,因为原始指针的破坏不会做任何事情。在异常安全的情况下,您可以拥有 RAII;没有它,祝你好运。
推荐阅读
- assembly - 了解 LEA 指令 x86
- php - 数组和 if 语句以及访问每个索引位置
- javascript - 从 3D 对象的形状变形 2D 形状
- swift - 为什么列表行有黑色背景?
- jupyter-notebook - TypeError: stat: path 应该是字符串、字节、os.PathLike 或整数,而不是 TrainingResult
- r - 在 R 中使用 dplyr 替换时间点
- python-3.x - 我无法弄清楚为什么它会打印出已从列表中删除的数字
- python - python tkinter中的按钮被按住检测
- typescript - TypeScript - 函数断言的窄类型
- python - 如何掷骰子