Saya tahu itu at()
lebih lambat daripada []
karena pemeriksaan batasnya, yang juga dibahas dalam pertanyaan serupa seperti C ++ Vector pada / [] kecepatan operator atau :: std :: vector :: at () vs operator [] << hasil yang mengejutkan !! 5 hingga 10 kali lebih lambat / lebih cepat! . Saya hanya tidak mengerti untuk apa at()
metode itu baik.
Jika saya memiliki vektor sederhana seperti ini: std::vector<int> v(10);
dan saya memutuskan untuk mengakses elemennya dengan menggunakan at()
alih-alih []
dalam situasi ketika saya memiliki indeks i
dan saya tidak yakin apakah itu dalam batas vektor, itu memaksa saya untuk membungkusnya dengan try-catch blok :
try
{
v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
...
}
meskipun saya dapat melakukan perilaku yang sama dengan menggunakan size()
dan memeriksa indeks saya sendiri, yang tampaknya lebih mudah dan nyaman bagi saya:
if (i < v.size())
v[i] = 2;
Jadi pertanyaan saya adalah:
Apa keuntungan menggunakan vector :: at over vector :: operator [] ?
Kapan saya harus menggunakan vector :: at daripada vector :: size + vector :: operator [] ?
if (i < v.size()) v[i] = 2;
, ada kemungkinan jalur kode yang tidak ditetapkan2
ke elemen apa punv
sama sekali. Jika itu perilaku yang benar, bagus. Tetapi seringkali tidak ada yang masuk akal bahwa fungsi ini dapat dilakukan kapani >= v.size()
. Jadi tidak ada alasan khusus mengapa tidak menggunakan pengecualian untuk menunjukkan situasi yang tidak terduga. Banyak fungsi yang hanya digunakanoperator[]
tanpa mencentang ukuran, dokumen yangi
harus berada dalam jangkauan, dan menyalahkan UB yang dihasilkan pada pemanggil.Jawaban:
Saya akan mengatakan pengecualian yang
vector::at()
melempar tidak benar-benar dimaksudkan untuk ditangkap oleh kode sekitarnya. Mereka terutama berguna untuk menangkap bug dalam kode Anda. Jika Anda perlu memeriksa batas saat runtime karena misalnya indeks berasal dari input pengguna, Anda memang paling baik denganif
pernyataan. Singkatnya, rancang kode Anda dengan tujuan yangvector::at()
tidak akan pernah mengeluarkan pengecualian, sehingga jika ya, dan program Anda dibatalkan, itu adalah tanda bug. (sepertiassert()
)sumber
size()
+[]
ketika indeks bergantung pada input pengguna, gunakanassert
dalam situasi di mana indeks tidak boleh keluar dari batas untuk perbaikan bug yang mudah di masa mendatang dan.at()
dalam semua situasi lainnya (untuk berjaga-jaga, menyebabkan sesuatu yang salah mungkin terjadi .. .)vector
maka mungkin lebih baik menggunakannya sebagai opsi "berjaga-jaga" daripada diat()
mana - mana. Dengan begitu, Anda dapat mengharapkan kinerja yang lebih baik dalam mode rilis, jika Anda membutuhkannya.operator[]
, misalnya gcc.gnu.org/onlinedocs/libstdc++/manual/… jadi jika platform Anda mendukung ini, Anda mungkin lebih baik melakukannya!Tidak, tidak (blok coba / tangkap bisa hulu). Ini berguna ketika Anda ingin pengecualian dilemparkan daripada program Anda memasuki dunia perilaku tidak terdefinisi.
Saya setuju bahwa sebagian besar akses di luar batas ke vektor adalah kesalahan programmer (dalam hal ini Anda harus menggunakan
assert
untuk menemukan kesalahan tersebut dengan lebih mudah; sebagian besar versi debug dari pustaka standar melakukan ini secara otomatis untuk Anda). Anda tidak ingin menggunakan pengecualian yang dapat ditelan untuk melaporkan kesalahan pemrogram: Anda ingin dapat memperbaiki bug .Karena tidak mungkin akses di luar batas ke vektor merupakan bagian dari alur program normal (dalam kasus ini, Anda benar: periksa terlebih dahulu
size
daripada membiarkan pengecualian menggelembung), saya setuju dengan diagnostik Anda:at
pada dasarnya tidak berguna.sumber
out_of_range
pengecualian, makaabort()
dipanggil.try..catch
ada dalam metode yang memanggil metode ini.at
berguna sejauh Anda menemukan diri Anda menulis sesuatu seperti ituif (i < v.size()) { v[i] = 2; } else { throw what_are_you_doing_you_muppet(); }
. Orang sering memikirkan fungsi pelempar pengecualian dalam istilah "kutukan, saya harus menangani pengecualian", tetapi selama Anda mendokumentasikan dengan cermat apa yang dapat dilemparkan oleh masing-masing fungsi, fungsi tersebut juga dapat digunakan sebagai "hebat, saya tidak harus memeriksa kondisi dan memberikan pengecualian ".out_of_range
berasal darilogic_error
, dan programmer lain "harus" tahu lebih baik daripada untuk menangkaplogic_error
dan mengabaikan mereka.assert
dapat diabaikan juga jika kolega Anda tidak ingin tahu tentang kesalahan mereka, itu hanya lebih sulit karena mereka harus mengkompilasi kode Anda denganNDEBUG
;-) Setiap mekanisme memiliki kelebihan dan kekurangan.Poin penting di sini adalah bahwa pengecualian memungkinkan pemisahan aliran kode normal dari logika penanganan kesalahan, dan satu blok tangkap dapat menangani masalah yang dihasilkan dari berbagai situs lemparan, bahkan jika tersebar jauh di dalam pemanggilan fungsi. Jadi, ini tidak
at()
selalu lebih mudah untuk satu penggunaan, tetapi terkadang itu menjadi lebih mudah - dan tidak terlalu mengaburkan logika kasus normal - ketika Anda memiliki banyak pengindeksan untuk divalidasi.Patut diperhatikan juga bahwa dalam beberapa jenis kode, indeks bertambah dengan cara yang rumit, dan terus digunakan untuk mencari array. Dalam kasus seperti itu, jauh lebih mudah untuk memastikan penggunaan pemeriksaan yang benar
at()
.Sebagai contoh dunia nyata, saya memiliki kode yang mengubah C ++ menjadi elemen leksikal, lalu kode lain yang memindahkan indeks ke vektor token. Bergantung pada apa yang ditemui, saya mungkin ingin menambah dan memeriksa elemen berikutnya, seperti di:
if (token.at(i) == Token::Keyword_Enum) { ASSERT_EQ(tokens.at(++i), Token::Idn); if (tokens.at(++i) == Left_Brace) ... or whatever
Dalam situasi seperti ini, sangat sulit untuk memeriksa apakah Anda telah secara tidak mencapai akhir dari input karena itu sangat tergantung pada token tepat ditemui. Pemeriksaan eksplisit pada setiap titik penggunaan itu menyakitkan, dan ada lebih banyak ruang untuk kesalahan programmer karena peningkatan pra / posting, offset pada titik penggunaan, penalaran yang salah tentang validitas lanjutan dari beberapa pengujian sebelumnya, dll.
sumber
at
bisa lebih jelas jika Anda memiliki penunjuk ke vektor:return pVector->at(n); return (*pVector)[n]; return pVector->operator[](n);
Selain performa, yang pertama adalah kode yang lebih sederhana dan lebih jelas.
sumber
Pertama, apakah lebih lambat
at()
atauoperator[]
tidak ditentukan. Ketika tidak ada kesalahan batas, saya berharap mereka memiliki kecepatan yang sama, setidaknya dalam proses debugging. Perbedaannya adalah bahwaat()
menentukan dengan tepat apa yang akan terjadi jika ada kesalahan batas (pengecualian), sedangkan dalam kasusoperator[]
, ini adalah perilaku yang tidak ditentukan — kerusakan di semua sistem yang saya gunakan (g ++ dan VC ++), setidaknya ketika flag debugging normal digunakan. (Perbedaan lainnya adalah bahwa setelah saya yakin dengan kode saya, saya bisa mendapatkan peningkatan kecepatan yang substansialoperator[]
dengan mematikan debugging. Jika kinerja memerlukannya — saya tidak akan melakukannya kecuali diperlukan.)Dalam praktiknya,
at()
jarang sekali sesuai. Jika konteksnya sedemikian sehingga Anda tahu bahwa indeks mungkin tidak valid, Anda mungkin menginginkan pengujian eksplisit (misalnya mengembalikan nilai default atau sesuatu), dan jika Anda tahu bahwa itu tidak mungkin tidak valid, Anda ingin membatalkannya (dan jika Anda tidak tahu apakah itu tidak valid atau tidak, saya sarankan Anda menentukan antarmuka fungsi Anda dengan lebih tepat). Namun, ada beberapa pengecualian, di mana indeks yang tidak valid dapat dihasilkan dari penguraian data pengguna, dan kesalahan tersebut seharusnya menyebabkan pembatalan seluruh permintaan (tetapi tidak membuat server turun); dalam kasus seperti itu, pengecualian sesuai, danat()
akan melakukannya untuk Anda.sumber
operator[]
tidak dipaksa untuk memeriksa batas, padahalat()
sebenarnya? Apakah Anda menyiratkan masalah penyangga cache, spekulasi, dan bercabang?at()
.std::string
tidak selalu berfungsi jika opsi pemeriksaan tidak sesuai dengan yang ada pada runtime:,-MD
dan Anda sebaiknya mematikan pemeriksaan-MDd
, dan Anda sebaiknya memiliki itu menyala.)operator[]
dilumpuhkan? Misalnyastd::vector<color> surface(witdh*height); ...; for (int y=0; y!=height; ++y)...
,. Saya pikir menegakkan pemeriksaan batas pada biner yang dikirim berada di bawah pesimisasi dini. Imho, itu seharusnya hanya menjadi bantuan pita untuk kode yang tidak direkayasa dengan baik.Inti dari menggunakan pengecualian adalah kode penanganan kesalahan Anda bisa lebih jauh.
Dalam kasus khusus ini, masukan pengguna memang merupakan contoh yang baik. Bayangkan Anda ingin menganalisis secara semantik struktur data XML yang menggunakan indeks untuk merujuk ke beberapa jenis sumber daya yang Anda simpan secara internal di file
std::vector
. Sekarang pohon XML adalah pohon, jadi Anda mungkin ingin menggunakan rekursi untuk menganalisisnya. Jauh di lubuk hati, dalam rekursi, mungkin ada pelanggaran akses oleh penulis file XML. Dalam hal ini, Anda biasanya ingin keluar dari semua tingkat rekursi dan hanya menolak seluruh file (atau jenis struktur "yang lebih kasar"). Di sinilah di berguna. Anda tinggal menulis kode analisis seolah-olah file tersebut valid. Kode perpustakaan akan menangani deteksi kesalahan dan Anda dapat menangkap kesalahan pada tingkat kasar.Juga, kontainer lain, seperti
std::map
, juga memilikistd::map::at
yang memiliki semantik yang sedikit berbeda daristd::map::operator[]
: at dapat digunakan pada peta const, sementaraoperator[]
tidak bisa. Sekarang jika Anda ingin menulis kode agnostik kontainer, seperti sesuatu yang dapat menangani salah satuconst std::vector<T>&
atauconst std::map<std::size_t, T>&
,ContainerType::at
akan menjadi senjata pilihan Anda.Namun, semua kasus ini biasanya muncul saat menangani beberapa jenis input data yang tidak divalidasi. Jika Anda yakin tentang rentang valid Anda, seperti biasanya, Anda biasanya dapat menggunakan
operator[]
, tetapi lebih baik lagi, iterator denganbegin()
danend()
.sumber
Menurut artikel ini , selain performa, tidak ada bedanya untuk digunakan
at
atauoperator[]
, hanya jika akses dijamin berada dalam ukuran vektor. Sebaliknya, jika akses hanya berdasarkan pada kapasitas vektor maka lebih aman digunakanat
.sumber
Catatan: Tampaknya beberapa orang baru meremehkan jawaban ini tanpa memberi tahu apa yang salah. Jawaban di bawah ini benar dan dapat diverifikasi di sini .
Hanya ada satu perbedaan:
at
apakah memeriksa batas sedangkanoperator[]
tidak. Ini berlaku untuk build debug serta build rilis dan ini ditentukan dengan sangat baik oleh standar. Sesederhana itu.Ini membuat
at
metode menjadi lebih lambat tetapi juga merupakan saran yang sangat buruk untuk tidak digunakanat
. Anda harus melihat angka absolut, bukan angka relatif. Saya dapat dengan aman bertaruh bahwa sebagian besar kode Anda melakukan operasi yang lebih mahal daripadaat
. Secara pribadi, saya mencoba menggunakanat
karena saya tidak ingin bug jahat membuat perilaku tidak terdefinisi dan menyelinap ke produksi.sumber
std::out_of_range
atau bentuk apa punstd::logic_error
sebenarnya adalah kesalahan logika dalam dan dari dirinya sendiri di sini .at
dan[]
dan jawaban saya hanya menyatakan perbedaannya. Saya pribadi menggunakan metode "aman" ketika kinerja tidak menjadi masalah. Seperti yang dikatakan Knuth, jangan lakukan pengoptimalan prematur. Selain itu, ada baiknya untuk menangkap bug lebih awal daripada dalam produksi terlepas dari perbedaan filosofis.