【问题标题】:Exception safety of new operator新操作员的异常安全
【发布时间】:2020-12-10 13:39:32
【问题描述】:

这是 implementation libstdc++ 中的新运算符:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
{
  void *p;

  /* malloc (0) is unpredictable; avoid it.  */
  if (__builtin_expect (sz == 0, false))
    sz = 1;

  while ((p = malloc (sz)) == 0)
    {
      new_handler handler = std::get_new_handler ();
      if (! handler)
        _GLIBCXX_THROW_OR_ABORT(bad_alloc());
      handler ();
    }

  return p;
}

谁保证构造函数中的异常会释放分配的内存?

更新:感谢评论员 - 实际上我的意思是新表达式的异常安全。

【问题讨论】:

  • 你能举个例子吗?异常是从哪里抛出的——来自new 还是来自构造函数?为正在构造的对象分配了什么内存或在构造函数中分配了一些内存?
  • 什么构造函数? new 运算符只是分配内存,它不构造对象。
  • 什么构造函数?
  • @bolov new 操作符 肯定会调用构造函数(除了new T 的普通类型)。 operator new 函数 没有。是的,C++ 术语令人困惑。
  • 因此,最好不要使用“new operator”这个词;事实上,标准本身只是偶尔地、不经常地这样做,而且我个人认为这是一种缺陷。 new 是关键字,表达式 new Tnew-expression

标签: c++ exception-safety


【解决方案1】:

您在问题中混合了“新表达式”和“新运算符”。

新的表达式是这样的:A* a = new A(); C++ 语言定义,这个表达式的计算结果是这样的(过度简化的伪代码):

void* __a = operator new(sizeof(A));
try {
   A::A(this = static_cast<A*>(__a));
} catch (...) {  operator delete (__a); throw; }

如您所见 - 如果发生异常,内存将被释放。

更详细的explanation

如果初始化因抛出异常而终止(例如,从 构造函数),如果 new-expression 分配了任何存储空间,它调用 适当的释放函数:非数组类型的操作符删除, 数组类型的运算符 delete[]。


对于编译器生成的真实代码 - 请参阅 here。 您可能会注意到对 operator delete 的调用 - 即使源代码不包含它:

struct A { A(); };
A::A() { throw 13; }

int main()
{
    auto a = new A();
}

汇编器:

 call 401040 <operator new(unsigned long)@plt>
...
 call 401172 <A::A()>
...
 call 401050 <operator delete(void*, unsigned long)@plt>
...
 call 401080 <_Unwind_Resume@plt>

【讨论】:

  • 但是实现这种逻辑的代码在哪里呢?
  • 由编译器生成
  • @ChrisUzdavinis 所以我们可以看出任何新表达式实际上都包含在 try-catch 块中?或者编译器在这种情况下以不同的方式执行它?我知道它是实现定义的,但让我们谈谈最流行的编译器,如 gcc 或 clang?
  • @MaxDmitrichenko - 我添加了指向示例的链接 - 从 CompilerExplorer 生成。它是如何生成的——它是编译器供应商特定的。不要忘记 - 我的答案第一部分中的代码是伪代码 - 不要太认真 - 这只是说明 - 不是真实世界的代码。
猜你喜欢
  • 2022-01-23
  • 2019-02-08
  • 2020-10-02
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 1970-01-01
  • 2016-06-16
  • 1970-01-01
相关资源
最近更新 更多