Tại sao auto_ptr không được dùng nữa?


Câu trả lời:


91

Sự thay thế trực tiếp cho auto_ptr(hoặc điều gần nhất với một) là unique_ptr. Về "vấn đề", nó khá đơn giản: auto_ptrchuyển quyền sở hữu khi nó được chỉ định. unique_ptrcũng chuyển quyền sở hữu, nhưng nhờ mã hóa ngữ nghĩa chuyển động và sự kỳ diệu của các tham chiếu rvalue, nó có thể làm như vậy một cách tự nhiên hơn đáng kể. Nó cũng "phù hợp" với phần còn lại của thư viện tiêu chuẩn tốt hơn đáng kể (mặc dù, công bằng mà nói, một số điều đó là nhờ phần còn lại của thư viện thay đổi để phù hợp với ngữ nghĩa chuyển động thay vì luôn yêu cầu sao chép).

Việc thay đổi tên cũng là (IMO) một điều đáng hoan nghênh - auto_ptrkhông thực sự cho bạn biết nhiều về những gì nó cố gắng tự động hóa, trong khi đó unique_ptrlà một mô tả khá hợp lý (nếu ngắn gọn) về những gì được cung cấp.


24
Chỉ cần một lưu ý về auto_ptrtên: tự động đề xuất tự động như trong biến tự động và nó đề cập đến một điều phải auto_ptrlàm: phá hủy tài nguyên được quản lý trong trình hủy của nó (khi nó vượt ra khỏi phạm vi).
Vincenzo Pii

13
Thông tin thêm: Đây là lý do chính thức cho việc ngừng sử dụng auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard Hinnant

@HowardHinnant tài liệu thú vị! thật kỳ lạ theo nghĩa là nếu std :: sort () có một chuyên biệt cho std :: unique_ptr để sử dụng ngữ nghĩa di chuyển khi cần thiết. Tôi tự hỏi tại sao std :: sort () không thể chuyên dụng cho std :: auto_ptr để khắc phục sự cố sao chép được đề cập trong tài liệu. Cảm ơn trước.
Hei

2
@Hei: std::sortkhông có chuyên môn cho unique_ptr. Thay vào đó, nó đã được chỉ định lại để không bao giờ sao chép. Vì vậy, auto_ptrthực sự làm việc với hiện đại sort. Nhưng C ++ 98/03 sortchỉ là một thuật toán ví dụ ở đây: Bất kỳ thuật toán chung nào (do std cung cấp hoặc do người dùng viết) giả định rằng cú pháp sao chép có ngữ nghĩa sao chép sẽ có thể có lỗi thời gian chạy nếu được sử dụng với auto_ptr, vì chuyển độngauto_ptr âm thầm với cú pháp sao chép . Vấn đề là nhiều lớn hơn chỉ . sort
Howard Hinnant

35

Tôi thấy các câu trả lời hiện có rất tuyệt, nhưng từ PoV của các con trỏ. IMO, một câu trả lời lý tưởng nên có câu trả lời theo quan điểm của người dùng / lập trình viên.

Điều đầu tiên trước tiên (như Jerry Coffin đã chỉ ra trong câu trả lời của anh ấy)

  • auto_ptr có thể được thay thế bằng shared_ptr hoặc unique_ptr tùy theo tình huống

shared_ptr: Nếu bạn lo lắng về việc giải phóng tài nguyên / bộ nhớ VÀ nếu bạn có nhiều hơn một hàm có thể đang sử dụng đối tượng AT-DIFFERENT lần, thì hãy chọn shared_ptr.

Theo DIFFERENT-Times, hãy nghĩ đến một tình huống trong đó đối tượng-ptr được lưu trữ trong nhiều cấu trúc dữ liệu và sau đó được truy cập. Nhiều chủ đề, tất nhiên là một ví dụ khác.

unique_ptr: Nếu tất cả những gì bạn quan tâm là giải phóng bộ nhớ và quyền truy cập vào đối tượng là SEQUENTIAL, thì hãy chuyển sang unique_ptr.

Ý tôi là SEQUENTIAL, tại bất kỳ thời điểm nào đối tượng sẽ được truy cập từ một ngữ cảnh. Ví dụ: một đối tượng đã được tạo và được sử dụng ngay sau khi được tạo bởi người tạo. Sau khi tạo, đối tượng được lưu trữ trong cấu trúc dữ liệu FIRST . Sau đó, đối tượng bị hủy sau cấu trúc dữ liệu ONE hoặc được chuyển đến cấu trúc dữ liệu SECOND .

Từ dòng này, tôi sẽ coi _ptr được chia sẻ / duy nhất là con trỏ thông minh. (auto_ptr cũng là một con trỏ thông minh NHƯNG vì những sai sót trong thiết kế của nó, chúng đang không được dùng nữa và điều mà tôi nghĩ rằng tôi sẽ chỉ ra trong các dòng tiếp theo, chúng không nên được nhóm với con trỏ thông minh.)

Một lý do quan trọng nhất giải thích tại sao auto_ptr không được dùng nữa để thay thế cho con trỏ thông minh là ngữ nghĩa gán Nếu không phải vì lý do đó, họ sẽ thêm tất cả các tính năng mới của ngữ nghĩa chuyển vào auto_ptr thay vì phản đối nó. Vì ngữ nghĩa gán là tính năng không thích nhất, họ muốn tính năng đó biến mất, nhưng vì có mã được viết sử dụng ngữ nghĩa đó, (mà ủy ban tiêu chuẩn không thể thay đổi), họ phải bỏ auto_ptr, thay vì sửa đổi nó.

Từ liên kết: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Loại bài tập được unqiue_ptr hỗ trợ

  • chuyển nhiệm vụ (1)
  • gán con trỏ null (2)
  • gán kiểu-cast (3)
  • sao chép bài tập (đã xóa!) (4)

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

Loại bài tập được auto_ptr hỗ trợ

  • sao chép bài tập (4) thủ phạm

Bây giờ đi đến lý do TẠI SAO bản thân bài tập sao chép lại bị không thích, tôi có lý thuyết này:

  1. Không phải tất cả các lập trình viên đều đọc sách hoặc các tiêu chuẩn
  2. auto_ptr trên mặt của nó, hứa với bạn quyền sở hữu đối tượng
  3. mệnh đề little- * (dự định chơi chữ) của auto_ptr, mà không phải tất cả các lập trình viên đều đọc được, cho phép, gán auto_ptr này cho auto_ptr khác và chuyển quyền sở hữu.
  4. Nghiên cứu đã chỉ ra rằng hành vi này dành cho 3,1415926535% tất cả các trường hợp sử dụng và không mong muốn trong các trường hợp khác.

Hành vi ngoài ý muốn thực sự không thích và do đó không thích auto_ptr.

(Đối với 3,1415926536% lập trình viên cố tình muốn chuyển quyền sở hữu C ++ 11 đã cho họ std :: move (), điều này làm cho ý định của họ trở nên rõ ràng đối với tất cả những người thực tập sẽ đọc và duy trì mã.)


1
Vì bạn không bao giờ muốn hai auto_ptrgiá trị trỏ đến cùng một đối tượng (vì chúng không trao quyền sở hữu chung, cái đầu tiên chết sẽ để lại cho cái kia một di sản gây chết người; điều này cũng đúng với unique_ptrcách sử dụng), bạn có thể đề xuất những gì dự định trong những người còn lại 96,8584073465% của tất cả mức sử dụng?
Marc van Leeuwen

Không thể nói cho tất cả chúng, nhưng tôi đoán, họ sẽ nghĩ rằng quyền sở hữu đối tượng đang được di chuyển và KHÔNG chỉ sao chép, điều này là sai lầm.
Ajeet Ganga

@AjeetGanga Trong cụm từ 'the little- * (ý định chơi chữ) sau đây,' bạn đã đề cập là "ý định chơi chữ". Cụm từ này là mới đối với tôi và dù sao tôi cũng truy cập vào nó và biết rằng có một số trò đùa được thực hiện có chủ đích ở đây. Trò đùa đó là gì ở đây? Chỉ tò mò muốn biết điều đó.
VINOTH ENERGETIC

@AjeetGanga Bạn đã đề cập như 'the little- * (dự định chơi chữ), mệnh đề của auto_ptr, điều này không được tất cả các lập trình viên đọc, cho phép, chuyển nhượng auto_ptr này cho người khác và chuyển quyền sở hữu'. Giả sử tôi có hai ptr tự động là a và b thành số nguyên. Tôi đang làm bài tập vì *a=*b;Ở đây chỉ có giá trị của b được sao chép vào a. Tôi hy vọng Quyền sở hữu của cả a và b vẫn thuộc về cùng một người. Bạn đã đề cập như khoản nợ sẽ được chuyển. Nó sẽ như thế nào?
VINOTH ENERGETIC

@VINOTHENERGETIC Ajeet đang nói về việc gán cho chính một auto_ptrđối tượng. Việc gán cho / từ giá trị trỏ đến của nó không ảnh hưởng đến, cũng không liên quan đến quyền sở hữu. Tôi hy vọng bạn vẫn không sử dụng auto_ptr?
underscore_d

23

shared_ptrcó thể được lưu trữ bên trong các thùng chứa. auto_ptrkhông thể.

BTW unique_ptrthực sự là sự auto_ptrthay thế trực tiếp , nó kết hợp các tính năng tốt nhất của cả hai std::auto_ptrboost::scoped_ptr.


11

Tuy nhiên, một người khác tiếp tục giải thích sự khác biệt ....

Về mặt chức năng, C ++ 11 std::unique_ptrlà "cố định" std::auto_ptr: cả hai đều phù hợp khi - tại bất kỳ thời điểm nào trong quá trình thực thi - phải có một chủ sở hữu con trỏ thông minh duy nhất cho một đối tượng trỏ đến.

Sự khác biệt quan trọng là trong xây dựng bản sao hoặc chuyển nhượng từ một con trỏ thông minh chưa hết hạn khác, được hiển thị trên các =>dòng bên dưới:

   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

Ở trên, ap3lặng lẽ "đánh cắp" quyền sở hữu *ap, để apđặt thành a nullptr, và vấn đề là điều đó có thể xảy ra quá dễ dàng, mà lập trình viên không nghĩ đến sự an toàn của nó.

Ví dụ: nếu a class/ structcó một std::auto_ptrthành viên, thì việc tạo một bản sao của một thể hiện sẽ releasekhiến con trỏ từ thể hiện đó được sao chép: đó là ngữ nghĩa kỳ lạ và khó hiểu một cách nguy hiểm vì thông thường sao chép một cái gì đó sẽ không sửa đổi nó. Tác giả của class / struct dễ dàng bỏ qua việc giải phóng con trỏ khi suy luận về các bất biến và trạng thái, và do đó vô tình cố gắng bỏ qua con trỏ thông minh trong khi rỗng, hoặc không vẫn có quyền truy cập / quyền sở hữu dự kiến ​​đối với dữ liệu trỏ đến.


auto_ptr lặng lẽ "đánh cắp" quyền sở hữu +1
camino

3

auto_ptr không thể được sử dụng trong vùng chứa STL vì nó có một phương thức khởi tạo sao chép không đáp ứng yêu cầu của vùng chứa CopyConstructible . unique_ptr không triển khai một phương thức khởi tạo sao chép, vì vậy các vùng chứa sử dụng các phương thức thay thế. unique_ptr có thể được sử dụng trong vùng chứa và nhanh hơn cho các thuật toán std so với 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.