【问题标题】:Unique pointer still holds the object after moving移动后唯一指针仍然持有对象
【发布时间】:2016-06-25 10:23:43
【问题描述】:

我正在阅读一些关于智能指针如何在 C++ 中工作的教程,但我坚持使用我尝试的第一个:唯一指针。我遵循wikipediacppreferencecplusplus 的指导方针。我也看过this answer。如果我理解正确,唯一指针应该是唯一拥有某个内存单元/块所有权的指针。这意味着只有唯一指针(应该)指向该单元格,而没有其他指针。来自维基百科,他们使用以下代码作为示例:

std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; //Compile error.
std::unique_ptr<int> p3 = std::move(p1); //Transfers ownership. p3 now owns the memory and p1 is rendered invalid.

p3.reset(); //Deletes the memory.
p1.reset(); //Does nothing.

直到第二行,当我测试它时,这对我来说效果很好。但是,在将第一个唯一指针 移动 到第二个唯一指针之后,我发现两个指针都可以访问同一个对象。我认为整个想法是让第一个指针变得无用可以这么说?我期望一个空指针或一些未确定的结果。我运行的代码:

class Figure {
public:
    Figure() {}

    void three() {
        cout << "three" << endl;
    }

};

class SubFig : public Figure {
public:
    void printA() {
        cout << "printed a" << endl;
    }
};

int main()
{
    unique_ptr<SubFig> testing (new SubFig());
    testing->three();
    unique_ptr<SubFig> testing2 = move(testing);
    cout << "ok" << endl;
    int t;
    cin >> t; // used to halt execution so I can verify everything works up til here
    testing->three(); // why is this not throwing a runtime error?
}

这里,testing 已经移动到了testing2,所以我惊讶地发现我仍然可以在testing 上调用方法three()

此外,调用 reset() 似乎并没有像它所说的那样删除内存。当我将main方法修改为:

int main()
{
    unique_ptr<SubFig> testing (new SubFig());
    testing->three();
    unique_ptr<SubFig> testing2 = move(testing);
    cout << "ok" << endl;
    int t;
    cin >> t;
    testing.reset(); // normally this should have no effect since the pointer should be invalid, but I added it anyway
    testing2.reset();
    testing2->three();
}

这里我希望three() 不适用于testing2,因为来自维基百科的示例提到应该通过重置来删除内存。我仍在打印 printed a 好像一切都很好。这对我来说似乎很奇怪。

那么谁能给我解释一下原因:

  • 从一个唯一指针移动到另一个唯一指针不会使第一个指针无效吗?
  • 重置实际上并没有删除内存?调用 reset() 时实际发生了什么?

【问题讨论】:

    标签: c++ pointers


    【解决方案1】:

    本质上是通过空指针调用成员函数:

    int main()
    {
        SubFig* testing = nullptr;
        testing->three();
    }
    

    ...这是未定义的行为。

    从 20.8.1 类模板 unique_ptr (N4296)

    4 此外,您可以根据要求将所有权转让给其他人 唯一指针 u2。完成此类转让后,以下内容 后置条件保持:

    • u2.p等于预调u.p,
    • u.p 等于 nullptr,并且
    • 如果预转移 u.d 保持状态,则该状态已转移到 u2.d。

    (强调我的)

    【讨论】:

    • 我明白了。我的印象是,在空指针上调用方法会导致异常,就像 Java 在空引用上调用方法时一样。因此,当我说调用 reset() 会将 testing 变为空指针并且行为未定义时,这也是正确的?
    • @Babyburger 是的,reset 类似。
    【解决方案2】:

    std::move() 之后,原始指针testing 设置为nullptr

    std::unique_ptr 不检查空访问以引发运行时错误的可能原因是每次使用std::unique_ptr 时它都会变慢。通过不进行运行时检查,编译器能够完全优化 std::unique_ptr 调用,使其与使用 原始指针 一样高效。

    调用nullptr 时没有崩溃的原因可能是因为您调用的函数没有访问(不存在的)对象的内存。但这是未定义的行为,所以任何事情都可能发生。

    【讨论】:

      【解决方案3】:

      在调用std::unique_ptr&lt;int&gt; p3 = std::move(p1); 时,您的原始指针p1 处于未定义状态,因此使用它会导致未定义行为。简单地说,永远不要这样做。

      【讨论】:

      • 原件不需要持有nullptr。 (参见:20.8.1 类模板 unique_ptr)
      • @Dieter,不知道这一点。谢谢。
      • 我认为“未指定”是对您想说的更好的描述。
      猜你喜欢
      • 1970-01-01
      • 2018-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-30
      • 2021-03-31
      • 2013-12-20
      • 2018-06-03
      相关资源
      最近更新 更多