Why is auto_ptr being deprecated?

2019-01-10 04:42发布

问题:

I heard auto_ptr is being deprecated in C++11. What is the reason for this?

Also I would like to know the difference between auto_ptr and shared_ptr.

回答1:

The direct replacement for auto_ptr (or the closest thing to one anyway) is unique_ptr. As far as the "problem" goes, it's pretty simple: auto_ptr transfers ownership when it's assigned. unique_ptr also transfers ownership, but thanks to codification of move semantics and the magic of rvalue references, it can do so considerably more naturally. It also "fits" with the rest of the standard library considerably better (though, in fairness, some of that is thanks to the rest of the library changing to accommodate move semantics instead of always requiring copying).

The change in name is also (IMO) a welcome one -- auto_ptr doesn't really tell you much about what it attempts to automate, whereas unique_ptr is a fairly reasonable (if terse) description of what's provided.



回答2:

I found the existing answers great, but from the PoV of the pointers. IMO, an ideal answer should have the user/programmer's perspective answer.

First thing first (as pointed by Jerry Coffin in his answer)

  • auto_ptr could be replaced by shared_ptr or unique_ptr depending upon situation

shared_ptr : If you are concerned about freeing of resource/memory AND if you have more than one function that could be using the object AT-DIFFERENT times, then go with shared_ptr.

By DIFFERENT-Times, think of a situation where the object-ptr is stored in multiple data-structure and later accessed. Multiple threads, of course is another example.

unique_ptr : If all you are concerned is freeing memory, and the access to object is SEQUENTIAL, then go for unique_ptr.

By SEQUENTIAL, I mean, at any point object will be accessed from one context. E.g. a object that was created, and used immediately after creation by the creator. After creation the object is stored in FIRST data-structure. Then either the object is destroyed after the ONE data-structure or is moved to SECOND data-structure.

From this line, I will refer shared/unique _ptr as smart-pointers. (auto_ptr is also smart-pointer BUT because of flaws in it's design,for which they are being deprecated, and which I think I will point out in next lines, they should not be grouped with smart-pointer. )

Single most important reason as to why auto_ptr was deprecated in favor of smart-pointer is assignment-semantics If it wasn't for that reason, they would have added all the new goodies of move semantics to the auto_ptr instead of deprecating it. Since the assignment-semantics was most-disliked feature, they wanted that feature to go away, but since there is code written that uses that semantics, (which standards-committee can not change), they had to let go of auto_ptr, instead of modifying it.

From the link : http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Kind of assignments supported by unqiue_ptr

  • move assignment (1)
  • assign null pointer (2)
  • type-cast assignment (3)
  • copy assignment (deleted!) (4)

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

Kind of assignments supported by auto_ptr

  • copy assignment (4) culprit

Now coming to the reason WHY the copy assignment itself was so disliked, I have this theory :

  1. Not all programmers read books or standards
  2. auto_ptr on the face of it, promises you ownership of the object
  3. the little-* (pun intended), clause of the auto_ptr, which is not read by all the programmers, allows, assignment of one auto_ptr to another, and transfers the ownership.
  4. Research has shown this behavior is intended for 3.1415926535 % of all usage, and unintended in other cases.

The unintended behavior is really disliked and hence the dislike for the auto_ptr.

(For the 3.1415926536% of programmers who intentionally wants to transfer the ownership C++11 gave them std::move(), which made their intention crystal clear for all the interns who are going to read and maintain the code.)



回答3:

shared_ptr can be stored inside containers. auto_ptr can't.

BTW unique_ptr is really the direct auto_ptr replacement, it combines the best features of both std::auto_ptr and boost::scoped_ptr.



回答4:

Yet another take on explaining the difference....

Functionally, C++11's std::unique_ptr is the "fixed" std::auto_ptr: both of them are suitable when - at any point in time during execution - there should be a single smart-pointer owner for a pointed-to object.

The crucial difference is in copy-construction or assignment from another un-expiring smart pointer, shown on the => lines below:

   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

Above, ap3 quietly "steals" ownership of *ap, leaving ap set to a nullptr, and the problem is that can happen too easily, without the programmer having thought through its safety.

For example, if a class/struct has a std::auto_ptr member, then making a copy of an instance will release the pointer from the instance being copied: that's weird and dangerously confusing semantics as usually copying something doesn't modify it. It's easy for the class/struct author to overlook the release of the pointer when reasoning about invariants and state, and consequently accidentally attempt to dereference smart-pointer while null, or just not still have expected access/ownership of the pointed-to data.



回答5:

auto_ptr cannot be used in STL containers because it has a copy constructor that does not meet requirements of container CopyConstructible. unique_ptr does not implement a copy constructor, so containers use alternate methods. unique_ptr can be used in containers and is faster for std algorithms than 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