Pilihan antara vektor :: mengubah ukuran () dan vektor :: cadangan ()

151

Saya pra-mengalokasikan sebagian memori ke vectorvariabel anggota saya . Kode di bawah ini adalah bagian minimal

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Sekarang di beberapa titik waktu, jika t_Names.size()sama 1000. Saya bermaksud menambah ukuran 100. Kemudian jika sudah tercapai 1100, kembali naik 100dan seterusnya.

Pertanyaan saya adalah, apa yang harus dipilih vector::resize()dan vector::reserve(). Apakah ada pilihan yang lebih baik dalam skenario seperti ini?

Sunting : Saya memiliki semacam perkiraan yang tepat untuk t_Names. Saya memperkirakan itu berada di sekitar 700untuk 800. Namun dalam situasi (jarang) tertentu , itu dapat tumbuh lebih dari 1000.

iammilind
sumber
34
Anda menyadari bahwa melakukan ini berarti pertumbuhan vektor tidak lagi diamortisasi waktu konstan dan Anda kehilangan salah satu manfaat kinerja menggunakan std::vector.
Blastfurnace
1
Terkait, lihat Membuat C ++ Lebih Mudah: Bagaimana Vektor Tumbuh di situs Dr. Dobbs.
jww

Jawaban:

262

Kedua fungsi itu melakukan hal yang sangat berbeda!

The resize()Metode (dan melewati argumen konstruktor setara dengan itu) akan memasukkan atau menghapus jumlah yang sesuai elemen untuk vektor untuk membuat ukuran itu diberikan (memiliki argumen kedua opsional untuk menentukan nilai mereka). Ini akan mempengaruhi size(), iterasi akan membahas semua elemen itu, push_back akan menyisipkan setelah mereka dan Anda dapat langsung mengaksesnya menggunakan operator[].

The reserve()Metode hanya mengalokasikan memori, tapi daun itu uninitialized. Itu hanya mempengaruhi capacity(), tetapi size()tidak akan berubah. Tidak ada nilai untuk objek, karena tidak ada yang ditambahkan ke vektor. Jika Anda kemudian memasukkan elemen, tidak ada realokasi yang akan terjadi, karena sudah dilakukan sebelumnya, tetapi itulah satu-satunya efek.

Jadi itu tergantung apa yang Anda inginkan. Jika Anda ingin array 1000 item default, gunakan resize(). Jika Anda ingin array yang ingin Anda masukkan 1000 item dan ingin menghindari beberapa alokasi, gunakan reserve().

EDIT: Komentar Blastfurnace membuat saya membaca pertanyaan itu lagi dan menyadari, bahwa dalam kasus Anda, jawaban yang benar adalah jangan mengalokasikan secara manual. Terus masukkan elemen di akhir sesuai kebutuhan. Vektor akan secara otomatis dialokasikan kembali sesuai kebutuhan dan akan melakukannya dengan lebih efisien daripada cara manual yang disebutkan. Satu-satunya kasus yang reserve()masuk akal adalah ketika Anda memiliki perkiraan ukuran total yang cukup tepat yang Anda perlukan dengan mudah tersedia sebelumnya.

EDIT2: Edit pertanyaan iklan: Jika Anda memiliki taksiran awal, maka reserve()taksiran itu. Jika ternyata tidak cukup, biarkan vektor melakukan itu.

Jan Hudec
sumber
Saya telah mengedit pertanyaan. Saya memiliki estimasi tertentu untuk vector.
iammilind
3
@ Jan: yah, rapuh atau tidak sesuai dengan seberapa sulit Anda membuatnya sendiri untuk mempertahankan properti yang diperlukan. Sesuatu seperti x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }cukup kuat sejauh menyangkut ruang yang bersangkutan. Saya tidak tahu berapa banyak elemen yang akan ditambahkan, tetapi saya memiliki batas atas. Tentu saja ketika ragu, dengan vektor Anda hanya bisa menggunakan indeks dan bukan iterator, perbedaannya biasanya diabaikan.
Steve Jessop
4
Kata-kata Anda masuk akal bagi seseorang yang sudah mengetahui jawaban yang benar, tetapi bisa dengan mudah menyesatkan orang yang perlu mengajukan pertanyaan. "resize () ... akan menyisipkan sejumlah elemen ke vektor" - hanya benar saat pertama kali digunakan - umumnya menyisipkan perbedaan antara jumlah yang diminta dan yang sudah ada sebelumnya size(). "Metode cadangan () hanya mengalokasikan memori" - itu mungkin atau mungkin tidak mengalokasikan memori tergantung pada apakah capacity()sudah cukup, mungkin juga perlu memindahkan elemen dan membatalkan alokasi memori asli mereka. "ingin menghindari beberapa alokasi" dan salinan dll
Tony Delroy
19
Sebenarnya, memesan sebelum mendorong sangat penting dan harus digunakan. Asumsikan bahwa Anda mengkodekan semacam pemuat model 3d dan model memiliki 15000 simpul. Jika Anda mencoba untuk push_back setiap simpul saat memuat tanpa pra-alokasi mereka terlebih dahulu, itu akan memakan waktu yang serius. Saya pribadi mengalaminya, saya mencoba memuat mobil .obj model dengan 100000 simpul, butuh 30 detik. Lalu saya refactored kode menggunakan pra-alokasi dengan .reserve (), sekarang butuh 3 detik. Hanya dengan meletakkan .reserve (100000) di awal kode menghemat 27 detik.
deniz
1
@deniz Itu sepele benar pada skala 100000, tetapi sangat tidak benar pada skala 100-300, di mana pemesanan bisa sia-sia jika dilakukan secara tidak perlu.
deworde
30

resize()tidak hanya mengalokasikan memori, itu juga menciptakan instance sebanyak ukuran yang Anda inginkanresize() sebagai argumen. Tetapi reserve()hanya mengalokasikan memori, itu tidak membuat instance. Itu adalah,

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Output ( demo online ):

1
1
0
1

Jadi resize()mungkin tidak diinginkan, jika Anda tidak ingin objek yang dibuat default. Ini akan lambat juga. Selain itu, jika Anda push_back()elemen baru untuk itu, size()vektor akan semakin meningkat dengan mengalokasikan memori baru (yang juga berarti memindahkan elemen yang ada ke ruang memori yang baru dialokasikan). Jika Anda telah menggunakan reserve()di awal untuk memastikan sudah ada cukup memori yang dialokasikan, size()vektor akan meningkat ketika Anda push_back()ke sana, tetapi itu tidak akan mengalokasikan memori baru sampai kehabisan ruang yang Anda pesan untuk itu .

Nawaz
sumber
6
Setelah melakukannya reserve(N), kita bisa menggunakannya operator []tanpa bahaya. benar ?
iammilind
2
Sementara sebagian besar implementasi akan mengalokasikan jumlah persis yang Anda minta reserve, spesifikasinya hanya mengharuskannya mengalokasikan setidaknya sebanyak itu, sehingga beberapa implementasi dapat mencapai batas tertentu dan dengan demikian menunjukkan kapasitas lebih tinggi dari 1000.
Jan Hudec
16
@ iammilind: Tidak, jika indeks lebih besar atau sama dengan v.size(). Perhatikan bahwa reserve(N)tidak mengubah size()vektor.
Nawaz
5
@ iammilind: Tidak Benar. Setelah memanggil kembali, tidak ada entri yang ditambahkan, hanya cukup memori untuk menambahkannya.
Jan Hudec
2

Dari uraian Anda, sepertinya Anda ingin "mencadangkan" ruang penyimpanan yang dialokasikan dari vektor t_Names.

Perhatikan bahwa resizeinisialisasi vektor yang baru dialokasikan di mana reservehanya mengalokasikan tetapi tidak membangun. Karenanya, 'cadangan' jauh lebih cepat daripada 'mengubah ukuran'

Anda dapat merujuk ke dokumentasi tentang perbedaan ukuran dan cadangan

menukik
sumber
1
Silakan merujuk di sini sebagai gantinya: vektor dan kapasitas ( mengapa? )
sehe
1
Terima kasih untuk penambahan tautan, lihat
celup
2

cadangan ketika Anda tidak ingin objek diinisialisasi saat dipesan. juga, Anda mungkin lebih suka membedakan secara logis dan melacak penghitungannya dibandingkan penghitungan penggunaannya saat Anda mengubah ukuran. jadi ada perbedaan perilaku di antarmuka - vektor akan mewakili jumlah elemen yang sama saat dipesan, dan akan menjadi 100 elemen lebih besar ketika diubah ukurannya dalam skenario Anda.

Apakah ada pilihan yang lebih baik dalam skenario seperti ini?

itu sepenuhnya tergantung pada tujuan Anda ketika melawan perilaku default. beberapa orang akan menyukai pengalokasi yang disesuaikan - tetapi kami benar-benar membutuhkan ide yang lebih baik tentang apa yang Anda coba selesaikan dalam program Anda untuk memberi saran dengan baik.

Pertama, banyak implementasi vektor hanya akan menggandakan jumlah elemen yang dialokasikan ketika mereka harus tumbuh - apakah Anda mencoba untuk meminimalkan ukuran alokasi puncak atau Anda mencoba memesan ruang yang cukup untuk beberapa program bebas kunci atau sesuatu yang lain?

justin
sumber
" cadangan ketika Anda tidak ingin objek diinisialisasi saat dipesan. " Formulasi yang benar adalah ketika Anda tidak ingin objek ada . Ini tidak seperti array yang tidak diinisialisasi dari tipe yang sepele, di mana objek tidak dapat dibaca tetapi bisa ditugaskan; melainkan, hanya memori yang dicadangkan, tetapi tidak ada objek di dalamnya, sehingga tidak dapat diakses menggunakan operator[]atau apa pun.
underscore_d