Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/typescript/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::move和unique_ptr::reset之间有什么区别?_C++_C++11_Unique Ptr - Fatal编程技术网

C++ std::move和unique_ptr::reset之间有什么区别?

C++ std::move和unique_ptr::reset之间有什么区别?,c++,c++11,unique-ptr,C++,C++11,Unique Ptr,对于std::unique_ptrsp1和p2,std::move()和std::unique_ptr::reset()之间有什么区别 例如,第一个可以在析构函数不匹配时警告您。此外,release()是一个非常危险的函数,您的简单示例是正确的,但其他许多用法都不正确。最好永远不要使用这个功能。我认为第二个版本可能不会异常安全。这相当于: auto __tmp = p2.release(); p1.reset(__tmp); 因此,如果调用std::unique_ptr::reset抛出(如果

对于
std::unique_ptr
s
p1
p2
std::move()
std::unique_ptr::reset()
之间有什么区别


例如,第一个可以在析构函数不匹配时警告您。此外,
release()
是一个非常危险的函数,您的简单示例是正确的,但其他许多用法都不正确。最好永远不要使用这个功能。

我认为第二个版本可能不会异常安全。这相当于:

auto __tmp = p2.release();
p1.reset(__tmp);
因此,如果调用
std::unique_ptr::reset
抛出(如果删除托管对象抛出,可能就是这种情况),那么您就拥有一个永远不会被销毁的未报告对象。在移动分配的情况下,
std::unique_ptr
可以(也应该)等待实际移动,直到
p1
的原始对象被正确销毁

但请注意,只有当托管对象的析构函数可能抛出(这在几乎所有情况下本身都是错误的)时,或者如果使用可能抛出的自定义删除器时,这才是一个问题。因此,在实践中,这两个代码片段之间通常没有任何行为差异


EDIT:最后Jonathan在他的评论中指出,标准要求自定义删除程序不能抛出,这确实使得抛出
std::unique\u ptr::reset
非常不可能/不一致。但他也指出了另一个区别,只有移动赋值也会移动任何自定义的删除器,他也为此写了一个答案



但不管实际结果如何,两者在概念上存在巨大差异。如果移动分配是合适的,则执行移动分配,并尝试不通过其他代码模拟它。事实上,我想不出任何理由用第二个代码段一对一地替换第一个代码段。DeadMG的正确之处在于,只有当您真正了解自己在做什么,以及在什么环境下处理非托管动态对象时,才应该使用
std::unique\u ptr::release

答案应该从[unique.ptr.single.assign]/2中的标准移动分配规范中显而易见:

效果:通过调用
reset(u.release())
然后从
std::forward(u.get_deleter())
分配,将所有权从
u
转移到
*此

显然,移动分配不同于重置(u.release()),因为它做了一些额外的事情

附加效果很重要,如果没有它,您可以使用自定义删除器获得未定义的行为:

#include <cstdlib>
#include <memory>

struct deleter
{
  bool use_free;
  template<typename T>
    void operator()(T* p) const
    {
      if (use_free)
      {
        p->~T();
        std::free(p);
      }
      else
        delete p;
    }
};

int main()
{
  std::unique_ptr<int, deleter> p1((int*)std::malloc(sizeof(int)), deleter{true});
  std::unique_ptr<int, deleter> p2;
  std::unique_ptr<int, deleter> p3;

  p2 = std::move(p1);  // OK

  p3.reset(p2.release());  // UNDEFINED BEHAVIOUR!
}
#包括
#包括
结构删除器
{
bool免费使用;
模板
void运算符()(T*p)常量
{
如果(免费使用)
{
p->~T();
std::游离(p);
}
其他的
删除p;
}
};
int main()
{
std::unique_ptr p1((int*)std::malloc(sizeof(int)),deleter{true});
std::unique_ptr p2;
std::唯一的ptr p3;
p2=标准::移动(p1);//确定
p3.reset(p2.release());//未定义的行为!
}

@Ali因为
reset
没有任何先决条件,所以您可以随时调用它,因为指定的任何移动都会使moved from对象处于未定义但有效的状态,这使得使用任何无先决条件的函数始终有效。只是在依赖某些先决条件时(例如指针为
nullptr
,但根据标准,
std::unique_ptr
)可能会保证这一点),这要求您首先检查相应的条件。移动不会以任何方式使对象无效,这是一个常见的误解。@Ali在任何移动之后,对象都保证处于有效但不确定的状态。std::unique\u ptr进一步保证它从移动后将是一个空指针。@Ali:这太危险了,因为基本上你能用返回值做的唯一安全的事情就是把它放回另一个
unique\u ptr
。实际上,任何其他操作都会导致诸如删除程序不匹配、内存泄漏或双重删除之类的情况。如果删除程序不匹配,则第一个删除程序不仅能够发出警告,而且会拒绝编译。这也是一件好事,我相信第一个将移动必要的删除器,而第二个将不会更改任何删除器状态。“因此在实践中,两个代码段之间通常没有任何行为差异。”这不是真的,一个移动删除器,一个不移动。如果删除程序是有状态的,这一点非常重要。你知道,
std::unique_ptr
的移动分配操作符被指定为“将所有权从
u
转移到
*这
就像通过调用
reset(u.release())
[…]”。“标准在很大程度上依赖于析构函数,而不是投掷。@JonathanWakely-Hah,对!没有过多考虑自定义删除程序。另外,删除托管对象不能引发异常,请参阅[unique.ptr.single.modifiers]/3@Xeo但它是否也依赖于不投掷的删除者?在Jonathan的评论之后,这个标准的引用似乎有点太笼统了,即使是对于一个“似乎”的句子.TL;医生:第二张表格坏了。永远不要使用它。你是说对于
p3
deleter
use\u free
未初始化?@chaosink否,它初始化为false,这意味着它将执行
delete p
,这对于从
malloc
获取的指针是错误的。
#include <cstdlib>
#include <memory>

struct deleter
{
  bool use_free;
  template<typename T>
    void operator()(T* p) const
    {
      if (use_free)
      {
        p->~T();
        std::free(p);
      }
      else
        delete p;
    }
};

int main()
{
  std::unique_ptr<int, deleter> p1((int*)std::malloc(sizeof(int)), deleter{true});
  std::unique_ptr<int, deleter> p2;
  std::unique_ptr<int, deleter> p3;

  p2 = std::move(p1);  // OK

  p3.reset(p2.release());  // UNDEFINED BEHAVIOUR!
}