【问题标题】:move constructor/assignment of std::unique_ptr: memory reallocation?移动 std::unique_ptr 的构造函数/赋值:内存重新分配?
【发布时间】:2020-08-03 16:36:27
【问题描述】:

在使用 std::unique_ptr 的移动构造函数/赋值时,是否可以假设底层对象没有在内存中重新分配,这样指向它的原始指针仍然有效?

考虑以下程序:

#include <memory>
#include <utility>
#include <iomanip>
#include <iostream>

template <class T>
struct A {
    std::unique_ptr<T> data = nullptr;
    T *p;
    template <class... U>
    A(U&&... x) : data{std::make_unique<T>(std::forward<U>(x)...)}, p{data.get()} { }
};

int main()
{
    A<int> v{2};
    std::cout << std::hex << v.p << std::endl;
    A<int> w{std::move(v)};
    std::cout << w.p << std::endl;
}

这里w是用A的默认移动构造函数构造的,它调用std::unique_ptr的移动构造函数。

从输出中可以看出,底层分配的int 并未真正在内存中移动,因此默认移动构造函数将w.p 正确初始化为与v.p 相同。 尝试使用其他类型的 T 会得到模拟结果。

是否可以假设std::unique_ptr 的移动构造函数并没有真正移动内存中的对象,因此在上述程序中默认的移动构造函数是正确的? 是语言规定的吗?

或者为了避免悬空指针必须显式添加一个移动构造函数,如下所示?

A(A<T>&& other) : data{std::move(other.data)}, p{data.get()} { }

【问题讨论】:

    标签: c++ c++11 unique-ptr move-semantics


    【解决方案1】:

    来自标准,[unique.ptr.single.ctor]/20

    unique_ptr(unique_ptr&& u) noexcept;
    

    后置条件:get() 产生构造前产生的值 u.get()。 u.get() == nullptr.

    在移动构造中不会发生内存分配;在A&lt;int&gt; w{std::move(v)}; 之后,w.data.get()(和w.p)将与之前的v.data.get() 相同,即v.p

    并且std::unique_ptr的move构造函数被标记为noexcept,这也意味着这里没有内存分配。

    IMO 甚至隐式生成的移动构造函数在这里也能正常工作,添加一个用户定义的更好,尤其是您可以在移动对象中将原始指针 p 设置为 nullptr(以保持一致性)。

    A(A<T>&& other) : data{std::move(other.data)}, p{data.get()} { other.p = nullptr; }
    

    【讨论】:

    • p(std::exchange(other.p, nullptr))
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多