shared_ptr ke sebuah array: haruskah itu digunakan?

172

Hanya pertanyaan kecil tentang shared_ptr.

Apakah ini praktik yang baik untuk menggunakan shared_ptrmenunjuk ke array? Sebagai contoh,

shared_ptr<int> sp(new int[10]);

Jika tidak, mengapa tidak? Salah satu alasan saya sudah sadar adalah seseorang tidak dapat menambah / mengurangi shared_ptr. Oleh karena itu tidak dapat digunakan seperti pointer normal ke array.

tshah06
sumber
2
FWIT, Anda juga dapat mempertimbangkan hanya menggunakan std::vector. Anda harus berhati-hati untuk menyebarkan array menggunakan referensi sehingga Anda tidak membuat salinannya. Sintaks untuk mengakses data lebih bersih daripada shared_ptr, dan mengubah ukurannya sangat sangat mudah. Dan Anda mendapatkan semua kebaikan STL jika Anda menginginkannya.
Nicu Stiurca
6
Jika ukuran array ditentukan pada waktu kompilasi, Anda mungkin juga mempertimbangkan untuk menggunakannya std::array. Ini hampir sama dengan array mentah, tetapi dengan semantik yang tepat untuk digunakan di sebagian besar komponen perpustakaan. Terutama benda-benda jenis itu dihancurkan delete, bukan delete[]. Dan tidak seperti vectoritu, ia menyimpan data secara langsung di objek, sehingga Anda tidak mendapatkan alokasi tambahan.
celtschk

Jawaban:

268

Dengan C ++ 17 , shared_ptrdapat digunakan untuk mengelola array yang dialokasikan secara dinamis. The shared_ptrTemplate argumen dalam kasus ini harus T[N]atau T[]. Jadi, Anda bisa menulis

shared_ptr<int[]> sp(new int[10]);

Dari n4659, [util.smartptr.shared.const]

  template<class Y> explicit shared_ptr(Y* p);

Membutuhkan: Y akan menjadi tipe yang lengkap. Ekspresi delete[] p, kapan Tadalah tipe array, atau delete p, ketika Tbukan tipe array, harus memiliki perilaku yang jelas, dan tidak akan membuang pengecualian.
...
Keterangan: Ketika Tmerupakan tipe array, konstruktor ini tidak akan berpartisipasi dalam resolusi yang berlebihan kecuali ekspresi delete[] pwell-formed dan baik Tadalah U[N]dan Y(*)[N]bisa dikonversi ke T*, atau Tadalah U[]dan Y(*)[]bisa dikonversi ke T*. ...

Untuk mendukung ini, tipe anggota element_typesekarang didefinisikan sebagai

using element_type = remove_extent_t<T>;

Elemen array dapat diakses menggunakan operator[]

  element_type& operator[](ptrdiff_t i) const;

Membutuhkan: get() != 0 && i >= 0 . Jika Tadalah U[N], i < N. ...
Keterangan: Ketika Tbukan tipe array, itu tidak ditentukan apakah fungsi anggota ini dideklarasikan. Jika dideklarasikan, tidak ditentukan jenis pengembaliannya, kecuali bahwa deklarasi (walaupun tidak harus definisi) dari fungsi harus dibentuk dengan baik.


Sebelum C ++ 17 , shared_ptrbisa tidak digunakan untuk mengelola array dialokasikan secara dinamis. Secara default, shared_ptrakan memanggil deleteobjek yang dikelola ketika tidak ada lagi referensi yang tersisa. Namun, ketika Anda mengalokasikan menggunakan new[]Anda perlu menelepon delete[], dan tidak delete, untuk membebaskan sumber daya.

Untuk menggunakan shared_ptrarray dengan benar, Anda harus menyediakan deleter khusus.

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

Buat shared_ptr sebagai berikut:

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

Sekarang shared_ptrakan memanggil dengan benar delete[]ketika menghancurkan objek yang dikelola.

Deleter khusus di atas dapat diganti dengan

  • yang std::default_deletespesialisasi parsial untuk jenis berbagai

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
  • ekspresi lambda

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });

Selain itu, kecuali jika Anda benar-benar perlu berbagi keanggotaan objek yang dikelola, a unique_ptrlebih cocok untuk tugas ini, karena ia memiliki spesialisasi parsial untuk jenis array.

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]

Perubahan yang diperkenalkan oleh C ++ Extensions for Fundamentals Library

Alternatif pra-C ++ 17 lain dari yang tercantum di atas disediakan oleh Spesifikasi Teknis Dasar-Dasar Perpustakaan , yang ditambahkan shared_ptruntuk memungkinkannya bekerja di luar kotak untuk kasus-kasus ketika ia memiliki berbagai objek. Draf shared_ptrperubahan yang dijadwalkan untuk TS ini saat ini dapat ditemukan di N4082 . Perubahan ini akan dapat diakses melalui std::experimentalnamespace, dan termasuk dalam <experimental/memory>header. Beberapa perubahan yang relevan untuk mendukung shared_ptrarray adalah:

- Definisi element_typeperubahan tipe anggota

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

- Anggota operator[]sedang ditambahkan

 element_type& operator[](ptrdiff_t i) const noexcept;

- Berbeda dengan unique_ptrspesialisasi parsial untuk array, keduanya shared_ptr<T[]>dan shared_ptr<T[N]>akan valid dan keduanya akan menghasilkan delete[]dipanggil pada array objek yang dikelola.

 template<class Y> explicit shared_ptr(Y* p);

Membutuhkan : Yakan menjadi tipe yang lengkap. Ekspresi delete[] p, ketika Tadalah tipe array, atau delete p, ketika Tbukan tipe array, harus dibentuk dengan baik, harus memiliki perilaku yang jelas, dan tidak akan membuang pengecualian. Ketika Tadalah U[N], Y(*)[N]akan dikonversi ke T*; saat Tini U[], Y(*)[]akan dikonversi ke T*; jika tidak, Y*akan dapat dikonversi menjadi T*.

Praetorian
sumber
9
+1, komentar: Ada juga Boost's shared-array.
jogojapan
5
@ tshah06 shared_ptr::getmengembalikan pointer ke objek yang dikelola. Jadi Anda dapat menggunakannya sebagaisp.get()[0] = 1; ... sp.get()[9] = 10;
Praetorian
55
ALT: std::shared_ptr<int> sp( new int[10], std::default_delete<int[]>() );lihat juga en.cppreference.com/w/cpp/memory/default_delete
yohjp
2
@ Jeremy Jika ukurannya diketahui pada waktu kompilasi tidak perlu menulis kelas untuk itu, std::shared_ptr<std::array<int,N>>harus cukup.
Praetorian
13
Mengapa unique_ptrmendapatkan spesialisasi sebagian tetapi shared_ptrtidak?
Adam
28

Alternatif yang mungkin lebih mudah yang mungkin bisa Anda gunakan adalah shared_ptr<vector<int>>.

Timmmm
sumber
5
Ya itu. Atau vektor adalah superset dari array - ia memiliki representasi dalam memori yang sama (ditambah metadata) tetapi dapat diubah ukurannya. Sebenarnya tidak ada situasi di mana Anda ingin array tetapi tidak dapat menggunakan vektor.
Timmmm
2
Perbedaannya, di sini, adalah bahwa ukuran vektor lagi statis, dan akses ke data akan dilakukan dengan tipuan ganda. Jika kinerja bukan masalah kritis, ini berfungsi, jika berbagi array mungkin memiliki alasan sendiri.
Emilio Garavaglia
4
Maka Anda mungkin bisa menggunakan shared_ptr<array<int, 6>>.
Timmmm
10
Perbedaan lainnya adalah bahwa itu sedikit lebih besar dan lebih lambat daripada array mentah. Secara umum tidak benar-benar masalah tetapi jangan berpura-pura bahwa 1 == 1.1.
Andrew
2
Ada situasi di mana sumber data dalam array berarti bahwa hal itu sulit atau tidak perlu dikonversi ke vektor; seperti saat mendapatkan bingkai dari kamera. (Atau, begitulah pemahaman saya)
Narfanator