为什么不建议使用auto_ptr?


Answers:


91

直接替换为auto_ptr(或无论如何最接近的东西)unique_ptr。就“问题”而言,它非常简单:auto_ptr在分配所有权时转移所有权。unique_ptr也转让所有权,但是由于移动语义的编纂和右值引用的神奇性,它可以更自然地做到这一点。它还可以更好地与标准库的其余部分“匹配”(尽管公平地说,这部分要归功于库的其余部分进行了更改,以适应移动语义而不是始终需要复制)。

名称更改(IMO)也很受欢迎- auto_ptr并没有真正告诉您它试图实现自动化的目的,而是unique_ptr对所提供内容的合理描述(如果简洁)。


24
只是对auto_ptr名称的注释:auto暗示auto就像auto variable一样,它表示auto_ptr要这样做的一件事:销毁其析构函数中的托管资源(当它超出范围时)。
文森佐·皮

13
进一步的信息:以下是弃用的正式理由auto_ptropen-std.org/jtc1/sc22/wg21/docs/papers/2005/…–
Howard

@HowardHinnant有趣的文档!从某种意义上说,很奇怪,如果std :: sort()具有std :: unique_ptr的专门化,可以根据需要使用move语义。我不知道为什么std :: sort()不能专门用于std :: auto_ptr来更正文档中提到的复制问题。提前致谢。

2
@Hei: std::sort没有专门的unique_ptr。相反,它被重新指定为从不复制。所以,auto_ptr实际上符合现代工作sort。但是C ++ 98/03 sort只是此处的示例算法: 任何假定复制语法具有复制语义的通用算法(由std提供或由用户编写),如果与一起使用auto_ptr,可能会出现运行时错误,因为auto_ptr静默移动副本语法。问题不止于此sort
Howard Hinnant

35

我发现现有的答案很好,但是从指针的角度来看。IMO,一个理想的答案应该具有用户/程序员的观点答案。

首先第一件事(正如杰里·科芬在回答中所指出的)

  • auto_ptr可以根据情况由shared_ptr或unique_ptr代替

shared_ptr: 如果您担心释放资源/内存,并且有多个功能可能正在使用对象AT-DIFFERENT次,那么请使用shared_ptr。

通过“时差”,可以考虑将对象-ptr存储在多个数据结构中并随后进行访问的情况。当然,多线程是另一个示例。

unique_ptr:如果您所关心的只是释放内存,并且对对象的访问是SEQUENTIAL,那么请使用unique_ptr。

我所说的SEQUENTIAL是指在任何时候都可以从一个上下文访问对象。例如,创建者创建后立即使用的对象。创建对象后,将其存储在FIRST数据结构中。然后,要么在ONE数据结构之后销毁对象,要么将其移动到SECOND数据结构。

从这一行,我将共享/唯一的_ptr称为智能指针。(由于其设计中的缺陷,auto_ptr也是智能指针BUT,对此已不推荐使用,我想在下一行中指出,不应将它们与智能指针归为一类。)

关于为什么不赞成使用auto_ptr而使用智能指针的一个最重要的原因是 赋值语义。 如果不是出于这个原因,他们会在auto_ptr中添加移动语义的所有新优点,而不是不赞成使用。由于分配语义是最不喜欢的功能,因此他们希望该功能消失,但是由于编写了使用该语义的代码(标准委员会无法更改),因此他们不得不放弃auto_ptr而不是修改它。

通过链接:http : //www.cplusplus.com/reference/memory/unique_ptr/operator=/

unqiue_ptr支持的作业类型

  • 移动作业(1)
  • 分配空指针(2)
  • 类型转换作业(3)
  • 复制作业(已删除!)(4)

来自:http : //www.cplusplus.com/reference/memory/auto_ptr/operator=/

auto_ptr支持的分配类型

  • 复制作业(4) 罪魁祸首

现在说出为什么如此不喜欢副本分配本身的原因,我有这样一个理论:

  1. 并非所有的程序员都阅读书籍或标准
  2. 表面上的auto_ptr,向您承诺对象的所有权
  3. auto_ptr的little- *(双关语意味)子句(并非所有程序员都可以读取)允许将一个auto_ptr分配给另一个,并转让所有权。
  4. 研究表明,此行为打算用于所有使用的3.1415926535%,在其他情况下是意料之外的。

意外的行为确实令人讨厌,因此对auto_ptr也不喜欢。

(对于3.1415926536%的有意要转让所有权的程序员,C ++ 11给了他们std :: move(),这对于所有打算阅读和维护代码的实习生来说,他们的意图都非常清楚。)


1
由于您永远不希望两个auto_ptr值指向同一对象(因为它们不赋予共享所有权,因此第一个死亡的值将使另一个值具有致命的遗产;这在unique_ptr用法上也是如此),能否建议您打算使用什么值?剩余所有使用量的96.8584073465%?
Marc van Leeuwen 2014年

不能代表所有人,但是我想,他们会认为对象所有权正在转移, 而不仅仅是重复,这是错误的。
Ajeet Ganga 2014年

@AjeetGanga在下面的短语“ the Little-*(双关语意)”中,您提到的是“双关语意”。这个词组对我来说是新词,无论如何我用谷歌搜索后就知道这里故意开了一些玩笑。这是什么玩笑?只是好奇地知道这一点。
VINOTH ENERGETIC

@AjeetGanga您提到过“'auto_ptr的little-*(双关语意味)”子句,并非所有程序员都阅读,它允许将一个auto_ptr分配给另一个auto_ptr,并转移所有权”。假设我有两个自动ptr,a和b都是整数。我正在做分配,因为*a=*b;这里只有b的值被复制到a。我希望a和b的所有权仍然是同一个人。您提到的所有权将被转移。会怎么样?
VINOTH ENERGETIC

@VINOTHENERGETIC Ajeet在谈论分配给auto_ptr对象本身。赋予/从其指向的值进行赋值不会影响所有权,也不会与所有权相关。希望您还没有使用auto_ptr
underscore_d

23

shared_ptr可以存放在容器内。 auto_ptr不能。

BTW unique_ptr实际上是直接auto_ptr替代,它结合了std::auto_ptr和的最佳功能boost::scoped_ptr


11

还有另一种解释差异的看法。

从功能上讲,C ++ 11 std::unique_ptr是“固定的” std::auto_ptr:当-在执行过程中的任何时间点-指向对象的对象只有一个智能指针所有者时,它们都适用。

关键区别在于另一个未过期的智能指针的复制构造或赋值,如下=>行所示:

   std::auto_ptr<T> ap(...);
   std::auto_ptr<T> ap2(get_ap_to_T());   // take expiring ownership
=> std::auto_ptr<T> ap3(ap);  // take un-expiring ownership ala ap3(ap.release());
   ap->xyz;  // oops... can still try to use ap, expecting it to be non-NULL

   std::unique_ptr<T> up(...);
   std::unique_ptr<T> up2(get_up_to_T());   // take expiring ownership
=> std::unique_ptr<T> up3(up);  // COMPILE ERROR: can't take un-expiring ownership
=> std::unique_ptr<T> up4(std::move(up));  // EXPLICIT code allowed
=> std::unique_ptr<T> up4(up.release());   // EXPLICIT code allowed

上面,ap3悄悄地“窃取”了的所有权*ap,将其ap设置为nullptr,而问题是这样的事情很容易发生,而无需程序员考虑其安全性。

例如,如果class/ struct具有std::auto_ptr成员,则release对实例进行复制将使要复制的实例中的指针成为一个怪异且危险的语义,因为通常复制某些内容不会对其进行修改。对于类/结构作者,在进行不变式和状态推理时,很容易忽略指针的释放,因此偶然地尝试在null或没有预期的对指针数据的访问/所有权的情况下取消引用智能指针。


auto_ptr悄悄地“窃取”所有权+1
camino

3

auto_ptr不能在STL容器中使用,因为它的复制构造函数不满足容器CopyConstructible的要求。unique_ptr没有实现复制构造函数,因此容器使用其他方法。unique_ptr可以在容器中使用,并且在std算法中比shared_ptr更快。

#include <iostream>
#include <type_traits>
#include <vector>
#include <memory>

using namespace std;

int main() {
  cout << boolalpha;
  cout << "is_copy_constructible:" << endl;
  cout << "auto_ptr: " << is_copy_constructible< auto_ptr<int> >::value << endl;
  cout << "unique_ptr: " << is_copy_constructible< unique_ptr<int> >::value << endl;
  cout << "shared_ptr: " << is_copy_constructible< shared_ptr<int> >::value << endl;

  vector<int> i_v;
  i_v.push_back(1);
  cout << "i_v=" << i_v[0] << endl;
  vector<int> i_v2=i_v;
  cout << "i_v2=" << i_v2[0] << endl;

  vector< unique_ptr<int> > u_v;
  u_v.push_back(unique_ptr<int>(new int(2)));
  cout << "u_v=" << *u_v[0] << endl;
  //vector< unique_ptr<int> > u_v2=u_v;  //will not compile, need is_copy_constructible == true
  vector< unique_ptr<int> > u_v2 =std::move(u_v);  // but can be moved
  cout << "u_v2=" << *u_v2[0] << " length u_v: " <<u_v.size() << endl;

  vector< shared_ptr<int> > s_v;
  shared_ptr<int> s(new int(3));
  s_v.push_back(s);
  cout << "s_v=" << *s_v[0] << endl;
  vector< shared_ptr<int> > s_v2=s_v;
  cout << "s_v2=" << *s_v2[0] << endl;

  vector< auto_ptr<int> > a_v;  //USAGE ERROR

  return 0;
}

>cxx test1.cpp -o test1
test1.cpp: In function âint main()â:
test1.cpp:33:11: warning: âauto_ptrâ is deprecated (declared at /apps/hermes/sw/gcc/gcc-4.8.5/include/c++/4.8.5/backward/auto_ptr.h:87) [-Wdeprecated-declarations]
   vector< auto_ptr<int> > a_v;  //USAGE ERROR
           ^
>./test1
is_copy_constructible:
auto_ptr: false
unique_ptr: false
shared_ptr: true
i_v=1
i_v2=1
u_v=2
s_v=3
s_v2=3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.