【问题标题】:Trivial Destructibility and Necessity of Calling Destructor调用析构函数的琐碎可破坏性和必要性
【发布时间】:2012-07-31 15:30:35
【问题描述】:

假设存在一个类型T 使得std::is_trivially_destructable<T>::value == true,并进一步假设T 是某个向量类的值类型。当向量的析构函数被调用,或者向量被分配给另一个向量时,它必须销毁并释放它当前的存储空间。由于T 是可破坏的,所以我有必要调用T 的析构函数吗?

感谢您的帮助!

【问题讨论】:

标签: c++ memory c++11 destructor typetraits


【解决方案1】:

根据 C++ 标准(第 3.8 节),您可以通过释放或重用其存储空间来结束对象的生命周期。没有必要调用什么都不做的析构函数。另一方面,让编译器优化掉一个空的析构函数通常会产生更简洁的代码。只有当您可以节省大量额外工作(例如遍历集合)时,您才会希望使用特殊情况的琐碎析构函数。

【讨论】:

  • 避免vector 的析构函数中的迭代开销是优化这种情况的一个很好的理由。
  • 是的,这是我在提问时想到的用例。如果编译器真的很好,它也许可以将while (l_ != f_) { --l_; l_->~value_type(); }优化成l_ = f_,但我宁愿依赖类型特征。
  • @void-pointer:启用优化是 C++ 标准语言要求 (1.10p24) 的重点,即每个线程必须执行易失性或同步操作、I/O 或终止。 while 循环不允许是无限的,并且由于它在有一个微不足道的析构函数时没有副作用,因此正式允许优化。即使编译器无法证明迭代器减量曾经达到f_
  • @void-pointer 检查你的标准库; std::vector 很有可能准确地实现了您建议的优化。
  • 确切的规则是 3.8.4:你没有义务调用任何析构函数。但是,如果您选择不调用它,任何依赖于破坏副作用的程序都会出现未定义的行为。这可以具体说明:由于微不足道的析构函数没有副作用,因此您始终可以决定不调用它们而不会产生任何影响。
【解决方案2】:

libstdc++(gcc 默认使用的标准库)应用precisely this optimisation:

  117   /**
  118    * Destroy a range of objects.  If the value_type of the object has
  119    * a trivial destructor, the compiler should optimize all of this
  120    * away, otherwise the objects' destructors must be invoked.
  121    */
  122   template<typename _ForwardIterator>
  123     inline void
  124     _Destroy(_ForwardIterator __first, _ForwardIterator __last)
  125     {
  126       typedef typename iterator_traits<_ForwardIterator>::value_type
  127                        _Value_type;
  128       std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
  129     __destroy(__first, __last);
  130     }

以及std::_Destroy_aux__has_trivial_destructor(_Value_type) == true 的特化:

  109   template<>
  110     struct _Destroy_aux<true>
  111     {
  112       template<typename _ForwardIterator>
  113         static void
  114         __destroy(_ForwardIterator, _ForwardIterator) { }
  115     };

我希望大多数其他编写良好的标准库实现也能做到这一点。

【讨论】:

  • 感谢您提供实际的源列表!很高兴知道我的实现利用了这种优化。我实际上想知道,因为我正在编写不同的数据结构。
【解决方案3】:

不,您不需要显式调用析构函数。这将由vector 的析构函数完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-06-13
    • 2020-11-19
    • 2010-10-01
    • 1970-01-01
    • 2018-03-10
    • 1970-01-01
    相关资源
    最近更新 更多