40

在 C++ 编程语言第 4 版中,有一个向量实现的示例,请参阅消息末尾的相关代码。

uninitialized_move()通过将新的 T 对象从旧的内存区域中移动来将它们初始化到新的内存区域中。然后它调用原始 T 对象(即移出对象)的析构函数。为什么在这种情况下需要调用析构函数?

这里是我的不完全理解:移动一个对象,就是将被移动对象所拥有的资源的所有权转移给被移动对象。移动对象中的剩余部分是不需要销毁的内置类型的一些可能成员,当 vector_base b 超出范围时(在reserve()内部,在swap()调用之后),它们将被释放)。移出对象中的所有指针都将被放置到 nullptr 或采用某种机制来删除这些资源上移出对象的所有权,以便我们安全,那么为什么在“vector_base b " 析构函数无论如何都会在交换完成后释放内存?

我理解在必须调用析构函数的情况下需要显式调用它,因为我们有一些要破坏的东西(例如删除元素),但是在 std::move + vector_base 的释放之后我看不到它的含义。我在网上阅读了一些文本,我看到从对象的析构函数调用作为一个信号(对谁或什么?)对象的生命周期已经结束。

请向我澄清析构函数还有哪些有意义的工作要做?谢谢!

下面的代码片段来自这里http://www.stroustrup.com/4th_printing3.html

template<typename T, typename A>
void vector<T,A>::reserve(size_type newalloc)
{
    if (newalloc<=capacity()) return;                   // never decrease allocation
    vector_base<T,A> b {vb.alloc,size(),newalloc-size()};   // get new space
    uninitialized_move(vb.elem,vb.elem+size(),b.elem);  // move elements
    swap(vb,b);                                 // install new base 
} // implicitly release old space

template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
    using T = Value_type<Out>;      // assume suitably defined type function (_tour4.iteratortraits_, _meta.type.traits_)
    for (; b!=e; ++b,++oo) {
        new(static_cast<void*>(&*oo)) T{move(*b)};  // move construct
        b->~T();                                // destroy
    }
    return oo;       
}
4

1 回答 1

46

从一个物体移动只是意味着被移动的物体可能会在它[可能]即将死亡之前不久捐赠它的内脏以在另一个活物体中继续存在。但是请注意,仅仅因为一个物体捐赠了它的内脏,这个物体并没有死!事实上,它可能会被另一个捐赠对象复活并依靠该对象的胆量生存。

此外,重要的是要了解移动构造或移动分配实际上可以是副本!事实上,如果要移动的类型恰好是具有复制构造函数或复制赋值的 C++11 之前的类型,它们将是副本。即使一个类有一个移动构造函数或一个移动赋值,它也可以选择它不能将其内脏移动到新对象,例如,因为分配器不匹配。

在任何情况下,移动的对象可能仍然有资源或需要记录统计信息或其他任何东西。为了摆脱对象,它需要被销毁。根据类的合同,它甚至可能在被移出后具有定义的状态,并且可以毫不费力地投入新的使用。

于 2013-12-14T22:40:14.607 回答