Mengapa auto_ptr tidak digunakan lagi?

Jawaban:

93

Pengganti langsung untuk auto_ptr(atau yang paling dekat dengan salah satunya) adalah unique_ptr. Sejauh "masalah" berjalan, itu cukup sederhana: auto_ptrmentransfer kepemilikan ketika sudah ditetapkan. unique_ptrjuga mentransfer kepemilikan, tetapi berkat kodifikasi semantik pemindahan dan keajaiban referensi nilai r, hal itu dapat dilakukan secara lebih alami. Ini juga "cocok" dengan pustaka standar lainnya secara jauh lebih baik (meskipun, dalam keadilan, beberapa di antaranya berkat sisa pustaka yang berubah untuk mengakomodasi semantik bergerak alih-alih selalu membutuhkan penyalinan).

Perubahan nama juga (IMO) merupakan hal yang disambut baik - auto_ptrtidak benar-benar memberi tahu Anda banyak tentang apa yang coba diotomatiskan, sedangkan unique_ptrdeskripsi yang cukup masuk akal (jika singkat) tentang apa yang disediakan.

Jerry Coffin
sumber
25
Hanya catatan pada auto_ptrnama: auto menyarankan otomatis seperti dalam variabel otomatis, dan ini mengacu pada satu hal yang auto_ptrdilakukan: menghancurkan sumber daya yang dikelola di destruktornya (ketika keluar dari ruang lingkup).
Vincenzo Pii
14
Informasi lebih lanjut: Berikut adalah alasan resmi untuk mencela auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005/…
Howard Hinnant
@HowardHinnant menarik doc! aneh jika std :: sort () memiliki spesialisasi untuk std :: unique_ptr untuk menggunakan semantik pemindahan sesuai kebutuhan. Saya bertanya-tanya mengapa std :: sort () tidak dapat dikhususkan untuk std :: auto_ptr untuk memperbaiki masalah penyalinan yang disebutkan di dokumen. Terima kasih sebelumnya.
Hei
2
@Hei: std::sorttidak memiliki spesialisasi untuk unique_ptr. Sebaliknya itu ditentukan ulang untuk tidak pernah menyalin. Jadi auto_ptrsebenarnya tidak bekerja dengan modern sort. Namun C ++ 98/03 sortadalah hanya sebuah contoh algoritma di sini: Setiap algoritma generik (std-disediakan atau pengguna tertulis) yang mengasumsikan bahwa sintaks copy memiliki copy semantik kemungkinan akan memiliki kesalahan run-time jika digunakan dengan auto_ptr, karena auto_ptrdiam-diam bergerak dengan sintaks salin . Masalahnya jauh lebih besar dari sekadar sort.
Howard Hinnant
36

Saya menemukan jawaban yang ada bagus, tetapi dari PoV petunjuknya. IMO, jawaban yang ideal harus memiliki jawaban perspektif pengguna / programmer.

Hal pertama yang pertama (seperti yang ditunjukkan oleh Jerry Coffin dalam jawabannya)

  • auto_ptr bisa diganti dengan shared_ptr atau unique_ptr tergantung situasi

shared_ptr: Jika Anda khawatir tentang membebaskan sumber daya / memori DAN jika Anda memiliki lebih dari satu fungsi yang dapat menggunakan objek SETIAP kali, maka gunakan shared_ptr.

Menurut DIFFERENT-Times, pikirkan situasi di mana object-ptr disimpan dalam beberapa struktur data dan kemudian diakses. Beberapa utas, tentu saja adalah contoh lain.

unique_ptr: Jika semua yang Anda khawatirkan adalah mengosongkan memori, dan akses ke objek adalah SEQUENTIAL, maka gunakan unique_ptr.

Dengan SEQUENTIAL, maksud saya, pada titik mana pun objek akan diakses dari satu konteks. Misalnya benda yang dibuat, dan digunakan segera setelah dibuat oleh pencipta. Setelah pembuatan, objek disimpan dalam struktur data PERTAMA . Kemudian salah satu objek dihancurkan setelah SATU struktur data atau dipindahkan ke struktur data KEDUA .

Dari baris ini, saya akan merujuk shared / unique _ptr sebagai smart-pointers. (auto_ptr juga merupakan smart-pointer TETAPI karena kekurangan dalam desainnya, yang membuatnya tidak digunakan lagi, dan yang menurut saya akan saya tunjukkan di baris berikutnya, mereka tidak boleh dikelompokkan dengan smart-pointer.)

Satu-satunya alasan terpenting mengapa auto_ptr tidak digunakan lagi untuk mendukung smart-pointer adalah assignment-semantics Jika bukan karena alasan itu, mereka akan menambahkan semua barang baru dari memindahkan semantik ke auto_ptr alih-alih menghentikannya. Karena tugas-semantik adalah fitur yang paling tidak disukai, mereka ingin fitur itu ditiadakan, tetapi karena ada kode tertulis yang menggunakan semantik itu, (yang tidak dapat diubah oleh komite standar), mereka harus melepaskan auto_ptr, bukan memodifikasinya.

Dari tautan: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/

Jenis tugas yang didukung oleh unqiue_ptr

  • pindah tugas (1)
  • tetapkan pointer nol (2)
  • tugas tipe-cor (3)
  • menyalin tugas (dihapus!) (4)

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

Jenis tugas yang didukung oleh auto_ptr

  • menyalin tugas (4) pelakunya

Sekarang datang ke alasan MENGAPA tugas menyalin itu sendiri sangat tidak disukai, saya memiliki teori ini:

  1. Tidak semua programmer membaca buku atau standar
  2. auto_ptr di depannya, menjanjikan Anda kepemilikan objek
  3. the little- * (pun intended), klausa auto_ptr, yang tidak dibaca oleh semua pemrogram, memungkinkan, penugasan satu auto_ptr ke yang lain, dan mentransfer kepemilikan.
  4. Penelitian telah menunjukkan perilaku ini ditujukan untuk 3,1415926535% dari semua penggunaan, dan tidak disengaja dalam kasus lain.

Perilaku yang tidak diinginkan benar-benar tidak disukai dan karenanya tidak menyukai auto_ptr.

(Untuk 3.1415926536% programmer yang dengan sengaja ingin mentransfer kepemilikan C ++ 11 memberi mereka std :: move (), yang membuat niat mereka sangat jelas bagi semua magang yang akan membaca dan memelihara kode.)

Ajeet Ganga
sumber
1
Karena Anda tidak pernah ingin dua auto_ptrnilai menunjuk ke objek yang sama (karena mereka tidak memberikan kepemilikan bersama, yang pertama mati akan meninggalkan yang lain dengan warisan yang mematikan; ini juga berlaku untuk unique_ptrpenggunaan), dapatkah Anda menyarankan apa yang dimaksudkan di yang tersisa 96.8584073465% dari semua penggunaan?
Marc van Leeuwen
Tidak dapat berbicara untuk semuanya, tetapi saya akan menebak, mereka akan berpikir kepemilikan objek sedang dipindahkan dan BUKAN hanya digandakan, yang salah.
Ajeet Ganga
@AjeetGanga Dalam frase berikut 'the little- * (pun intended),' Anda sebutkan sebagai "pun intended". Kalimat ini baru bagi saya dan bagaimanapun saya mencari di Google dan mengetahui bahwa ada beberapa lelucon yang sengaja dilakukan di sini. Apa lelucon itu di sini? Hanya penasaran ingin tahu itu.
VINOTH ENERGETIC
@AjeetGanga Anda menyebutkan seperti 'the little- * (pun intended), klausa auto_ptr, yang tidak dibaca oleh semua programmer, memungkinkan, menugaskan satu auto_ptr ke yang lain, dan mentransfer kepemilikan'. Katakanlah saya memiliki dua ptr otomatis sebagai a dan b ke integer. Saya melakukan tugas karena *a=*b;Di sini hanya nilai b yang disalin ke a. Semoga Kepemilikan a dan b tetap dimiliki oleh orang yang sama. Anda menyebutkan seperti kepemilikan akan ditransfer. Bagaimana jadinya?
VINOTH ENERGETIC
@VINOTHENERGETIC Ajeet berbicara tentang menugaskan ke auto_ptrobjek itu sendiri. Menugaskan ke / dari nilai menunjuk ke tidak akan berpengaruh pada, atau relevansi dengan, kepemilikan. Saya harap Anda tidak masih menggunakan auto_ptr?
underscore_d
23

shared_ptrdapat disimpan di dalam wadah. auto_ptrtidak bisa.

BTW unique_ptrbenar-benar auto_ptrpengganti langsung , ini menggabungkan fitur terbaik dari keduanya std::auto_ptrdan boost::scoped_ptr.

Ben Voigt
sumber
11

Namun yang lain menjelaskan perbedaannya ....

Secara fungsional, C ++ 11 std::unique_ptr adalah "tetap" std::auto_ptr: keduanya cocok ketika - pada titik waktu mana pun selama eksekusi - harus ada satu pemilik smart-pointer untuk objek yang diarahkan ke.

Perbedaan krusial adalah dalam konstruksi salinan atau penugasan dari penunjuk cerdas lain yang tidak kedaluwarsa, yang ditunjukkan pada =>baris di bawah ini:

   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

Di atas, ap3diam-diam "mencuri" kepemilikan *ap, meninggalkan apset ke anullptr , dan masalahnya adalah itu bisa terjadi terlalu mudah, tanpa programmer memikirkan keamanannya.

Misalnya, jika a class/ structmemiliki std::auto_ptranggota, maka membuat salinan sebuah instance akan releasemembuat pointer dari instance tersebut akan disalin: itu semantik yang aneh dan sangat membingungkan karena biasanya menyalin sesuatu tidak akan mengubahnya. Sangat mudah bagi penulis class / struct untuk mengabaikan rilis pointer ketika bernalar tentang invariants dan state, dan akibatnya secara tidak sengaja mencoba untuk dereferensi smart-pointer sementara null, atau hanya tidak memiliki akses / kepemilikan yang diharapkan dari data yang diarahkan ke.

Tony Delroy
sumber
auto_ptr diam-diam "mencuri" kepemilikan +1
camino
3

auto_ptr tidak dapat digunakan dalam penampung STL karena memiliki konstruktor salinan yang tidak memenuhi persyaratan penampung CopyConstructible . unique_ptr tidak mengimplementasikan konstruktor salinan, jadi container menggunakan metode alternatif. unique_ptr dapat digunakan dalam container dan lebih cepat untuk algoritme std daripada 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
edW
sumber