vektör :: at ve vektör :: operatörü []


98

Bunun , / [] operatör hızında C ++ Vector veya :: std :: vector :: at () ile operatör [] << arasındaki benzer sorularda tartışılan sınır kontrolünden at()daha yavaş olduğunu biliyorum << şaşırtıcı sonuçlar !! 5 ila 10 kat daha yavaş / daha hızlı! . Sadece yöntemin ne işe yaradığını anlamıyorum .[]at()

Bunun gibi basit bir vektöre sahipsem: std::vector<int> v(10);ve bir indeksim olduğunda durum at()yerine kullanarak öğelerine erişmeye karar []verirsem ive vektörlerin sınırları içinde olup olmadığından emin değilim, beni onu try-catch ile sarmaya zorluyor blok :

try
{
    v.at(i) = 2;
}
catch (std::out_of_range& oor)
{
    ...
}

size()Dizini kendi başıma kullanarak ve kontrol ederek aynı davranışı elde edebilmeme rağmen , bu benim için daha kolay ve daha uygun görünüyor:

if (i < v.size())
    v[i] = 2;

Öyleyse sorum şu: vector :: at over vector :: operator []
kullanmanın avantajları nelerdir ? Vector :: size + vector :: operator [] yerine
ne zaman vector :: at kullanmalıyım ?


11
+1 çok güzel soru !! ama () 'nin yaygın olarak kullanıldığını sanmıyorum.
Rohit Vipin Mathews

10
Örnek kodunuzda, herhangi bir öğeye if (i < v.size()) v[i] = 2;atanmayan olası bir kod yolu olduğunu unutmayın . Doğru davranış buysa, harika. Ancak çoğu zaman bu işlevin ne zaman yapabileceği mantıklı değildir . Bu yüzden neden özel bir nedeni var olmamalıdır beklenmedik bir durum belirtmek için bir istisna kullanın. Pek çok işlev , boyut ve menzil içinde olması gereken belgeyi kontrol etmeden kullanır ve ortaya çıkan UB'yi arayanı suçlar. 2vi >= v.size()operator[]i
Steve Jessop

Yanıtlar:


75

vector::at()Fırlatılan istisnaların , hemen çevreleyen kod tarafından yakalanması amaçlanmadığını söyleyebilirim . Genellikle kodunuzdaki hataları yakalamak için kullanışlıdırlar. Çalışma zamanında sınır kontrolü yapmanız gerekiyorsa, örneğin indeks kullanıcı girdisinden geliyorsa, gerçekten en iyisi bir ififadedir. Özetle, kodunuzu vector::at()asla bir istisna atmayacak şekilde tasarlayın , öyle ki öyle olursa ve programınız iptal edilirse, bu bir hatanın işaretidir. (tıpkı bir gibi assert())


1
+1 Yanlış kullanıcının girdisinin işlenmesinin nasıl ayrılacağına dair açıklamayı beğendim (girdi doğrulama; geçersiz girdi beklenebilir, bu nedenle istisnai bir şey olarak kabul edilmez) ... ve koddaki hatalar (aralık dışı olan yineleyiciyi yeniden referanslama istisnai şey)
Bojan Komazec

Yani , endeks kullanıcı girdisine bağlı olduğunda size()+ kullanmam gerektiğini söylüyorsunuz , ileride ve diğer tüm durumlarda kolay hata düzeltmesi için endeksin asla sınırların dışında olmaması gereken durumlarda ve diğer tüm durumlarda (her ihtimale karşı, yanlış bir şey olabilir ... .)[]assert.at()
LihO

8
@LihO: Eğer uygulamanız bir hata ayıklama uygulaması sunuyorsa, vectoro zaman bunu at()her yerde değil " her durumda" seçeneği olarak kullanmak muhtemelen daha iyidir . Bu şekilde, ihtiyacınız olursa diye yayın modunda biraz daha fazla performans elde etmeyi umabilirsiniz.
Steve Jessop

3
Evet, bugünlerde çoğu STL uygulaması operator[], hatta sınır kontrolleri yapan bir hata ayıklama modunu destekliyor , örneğin gcc.gnu.org/onlinedocs/libstdc++/manual/… bu nedenle, platformunuz bunu destekliyorsa, muhtemelen en iyisi onunla devam edebilirsiniz!
pmdj

1
@pmdj hakkında bilmediğim harika nokta ... ama öksüz bağlantı. : P mevcut biridir: gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
underscore_d

16

beni try-catch bloğu ile sarmalamaya zorluyor

Hayır değil (dene / yakala bloğu yukarı yönde olabilir). Tanımlanmamış davranış alanına girmek için programınız yerine bir istisna atılmasını istediğinizde kullanışlıdır.

Vektörlere sınır dışı erişimlerin çoğunun bir programcının hatası olduğunu kabul ediyorum (bu durumda assertbu hataları daha kolay bulmak için kullanmanız gerekir; standart kitaplıkların çoğu hata ayıklama sürümü bunu sizin için otomatik olarak yapar). Programcı hatalarını bildirmek için yukarı akışta yutulabilecek istisnaları kullanmak istemezsiniz: hatayı düzeltebilmek istersiniz .

Bir vektöre sınır dışı erişimin normal program akışının bir parçası olması olası olmadığından (bu durumda haklısınız: sizeistisna kabarcıklarının artmasına izin vermek yerine önceden kontrol edin ), teşhisinize katılıyorum: ataslında işe yaramaz.


out_of_rangeİstisna yakalayamazsam abort()çağrılır.
LihO

@LihO: Mutlaka değil ... try..catchbu yöntemi çağıran yöntemde mevcut olabilir.
Naveen

12
Başka hiçbir şey yoksa, atkendinizi böyle bir şey yazarken bulacağınız ölçüde faydalıdır if (i < v.size()) { v[i] = 2; } else { throw what_are_you_doing_you_muppet(); }. İnsanlar genellikle istisna atma işlevlerini "lanetler, istisnayı halletmem gerekiyor" şeklinde düşünür, ancak her bir işlevinizin neler atabileceğini dikkatlice belgelediğiniz sürece, bunlar "harika, yapmıyorum bir koşulu kontrol etmeli ve bir istisna atmalı ".
Steve Jessop

@SteveJessop: Program hataları için istisnalar atmayı sevmiyorum, çünkü diğer programcılar tarafından yukarı akışa yakalanabilirler. İddialar burada çok daha kullanışlıdır.
Alexandre C.

6
@AlexandreC. Pekala, buna resmi yanıt şudur ki, bu out_of_rangekaynaktır logic_errorve diğer programcılar logic_erroryukarı akışları yakalamaktan ve onları görmezden gelmekten daha iyi "bilmelidir" . assertEğer meslektaşlarınız hatalarını bilmemeye hevesliyse de göz ardı edilebilir, bu daha zordur çünkü kodunuzu NDEBUG;-) ile derlemek zorundadırlar Her mekanizmanın avantajları ve kusurları vardır.
Steve Jessop

11

Vector :: at over vector :: operator [] kullanmanın avantajları nelerdir? Vector :: size + vector :: operator [] yerine ne zaman vector :: at kullanmalıyım?

Buradaki önemli nokta, istisnaların normal kod akışının hata işleme mantığından ayrılmasına izin vermesidir ve tek bir yakalama bloğu, işlev çağrılarının derinliklerine dağılmış olsa bile sayısız fırlatma sitelerinden herhangi birinden üretilen sorunları idare edebilir. Bu nedenle, at()tek bir kullanım için bu çok daha kolay değildir , ancak doğrulamak için çok fazla indekslemeniz olduğunda bazen daha kolay hale gelir ve normal durum mantığını daha az karıştırır.

Bazı kod türlerinde, bir dizinin karmaşık şekillerde artırılması ve sürekli olarak bir dizi aramak için kullanılması da dikkate değerdir. Bu gibi durumlarda, kullanarak doğru kontrolleri sağlamak çok daha kolaydır at().

Gerçek dünya örneği olarak, C ++ 'yı sözcüksel öğelere, ardından bir dizini simge vektörü üzerinde hareket ettiren başka bir koda dönüştüren bir koda sahibim. Karşılaşılan şeye bağlı olarak, aşağıdaki gibi bir sonraki öğeyi artırmak ve kontrol etmek isteyebilirim:

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

Bu tür bir durumda, girdinin sonuna uygunsuz bir şekilde ulaşıp ulaşmadığınızı kontrol etmek çok zordur çünkü bu karşılaşılan tam belirteçlere çok bağlıdır. Her kullanım noktasında açık kontrol acı vericidir ve ön / son artışlar, kullanım noktasındaki sapmalar, daha önceki bazı testlerin devam eden geçerliliği hakkında hatalı muhakeme vb. Başladığı için programcı hatası için çok daha fazla yer vardır.


9

at vektöre bir işaretçiniz varsa daha net olabilir:

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

Performans bir yana, bunlardan ilki daha basit ve daha net koddur.


... özellikle bir işaretçi gerektiğinde n bir vektörün-inci elemanı.
yunus

IMHO, tercih etmek için yeterince iyi bir neden değil at(). Sadece yazın: auto& vector = *pVector;ve şimdi yapabilirsiniz return vector[n]. Ayrıca, özellikle karmaşık sınıflar için, işaretçilerle (referansların aksine) doğrudan çalışmaktan gerçekten kaçınmalısınız.
einpoklum

5

Hata ayıklama yapılarında, at()bundan daha yavaş olacağı garanti edilmez operator[]; Yaklaşık aynı hızda olmalarını beklerdim. Aradaki fark, at()bir sınır hatası (bir istisna) olduğunda tam olarak ne olacağını belirtmesidir; burada olduğu gibi operator[], bu tanımsız bir davranıştır - kullandığım tüm sistemlerde (g ++ ve VC ++) bir çökme, en azından ne zaman normal hata ayıklama bayrakları kullanılır. (Diğer bir fark, kodumdan emin olduğumda operator[], hata ayıklamayı kapatarak önemli bir hız artışı elde edebilirim . Performans gerektiriyorsa - gerekmedikçe yapmam.)

Uygulamada at()nadiren uygundur. Bağlam, dizinin geçersiz olabileceğini bildiğiniz şekildeyse, muhtemelen açık testi istersiniz (örneğin, bir varsayılan değer veya başka bir şey döndürmek için) ve geçersiz olamayacağını biliyorsanız, iptal etmek istersiniz (ve eğer geçersiz olup olmayacağını bilmiyorsanız, fonksiyonunuzun arayüzünü daha kesin olarak belirlemenizi öneririm). Bununla birlikte, geçersiz dizinin kullanıcı verilerinin ayrıştırılmasından kaynaklanabileceği ve hatanın tüm isteğin iptal edilmesine (ancak sunucuyu kapatmamasına) neden olması gereken birkaç istisna vardır; bu gibi durumlarda, bir istisna uygundur ve at()bunu sizin için yapar.


@phresnel'in operator[]sınır kontrolü yapması gerekmez, ancak tüm iyi uygulamalar bunu yapar. En azından hata ayıklama modunda. Tek fark, indeks sınırların dışındaysa ne yaptıklarıdır: operator[]bir hata mesajı ile iptal edilir, at()bir istisna atar.
James Kanze

1
@phresnel Gönderdiğim kodun çoğu "hata ayıklama" modunda. Yalnızca performans sorunları gerçekten gerektirdiğinde denetimi kapatırsınız. (Microsoft 2010 öncesi burada biraz problemdi, çünkü std::stringkontrol seçenekleri çalışma zamanındakilerle uyuşmuyorsa her zaman işe yaramıyordu: -MDve kontrolü kapatsanız iyi olur -MDdve yapsanız iyi olur )
James Kanze

3
Ben daha çok "standart olarak onaylanmış (garantili) kod" diyen kamptanım; Tabii ki, hata ayıklama modunda teslim etmekte özgürsünüz, ancak platformlar arası geliştirme yaparken (sadece aynı işletim sistemi durumu değil, farklı derleyici sürümleri dahil), standartlara güvenmek sürümler ve hata ayıklama modu için en iyi seçenektir programcının o şeyi çoğunlukla doğru ve sağlam hale getirmesi için bir araç olarak kabul edilir :)
Sebastian Mach

@phresnel Açıkçası, sadece standarda güveniyorsunuz. Ancak bazı platformlar tanımlanmamış davranışlar için bir çökme garantisi veriyorsa, bundan yararlanmamak aptalca olacaktır (profilci size yapamayacağınızı söylemediği sürece). Kodunuzda hiçbir hata olmadığından% 100 emin olamazsınız ve en azından bazı özel durumlarda, bazı belirli platformlarda tüm müşteri verilerini yok etmek yerine çökeceğinizi bilmek güven vericidir.
James Kanze

1
Öte yandan, uygulamanızın kritik bölümleri izole edilmiş ve örneğin istisna güvenliği (RAII ftw) ile korunuyorsa, o zaman her erişim operator[]sakatlanmalı mı? Örneğin std::vector<color> surface(witdh*height); ...; for (int y=0; y!=height; ++y)...,. Bence, teslim edilen ikili dosyalar üzerinde sınır kontrolünü zorunlu kılmak, erken bir kötümseme sürecine giriyor. Imho, yalnızca iyi tasarlanmış olmayan kodlar için yara bandı olmalıdır.
Sebastian Mach

1

İstisnaları kullanmanın tüm amacı, hata işleme kodunuzun daha uzakta olabileceğidir.

Bu özel durumda, kullanıcı girdisi gerçekten iyi bir örnektir. Dahili olarak depoladığınız bir tür kaynağa başvurmak için indeksleri kullanan bir XML veri yapısını anlamsal olarak analiz etmek istediğinizi hayal edin std::vector. Şimdi XML ağacı bir ağaçtır, bu yüzden muhtemelen onu analiz etmek için özyinelemeyi kullanmak istersiniz. Özyinelemenin derinliklerinde, XML dosyasının yazarı tarafından bir erişim ihlali olabilir. Bu durumda, genellikle tüm özyineleme düzeylerinden çıkıp tüm dosyayı (veya herhangi bir "daha kaba" yapıyı) reddetmek istersiniz. İşte burada işe yarıyor. Analiz kodunu, dosya geçerliymiş gibi yazabilirsiniz. Kütüphane kodu, hata tespitini halleder ve hatayı sadece kaba seviyede yakalayabilirsiniz.

Ayrıca, gibi diğer kapsayıcılar std::mapda : at std::map::atöğesinden biraz farklı anlamlara sahip olanlara std::map::operator[]sahipken sabit bir haritada kullanılabilirken kullanılamaz operator[]. Şimdi ya da ele alabileceği bir şey gibi, yazma konteyner agnostik kodu istiyorsa const std::vector<T>&ya const std::map<std::size_t, T>&, ContainerType::atseçtiğiniz silah olacaktır.

Bununla birlikte, tüm bu durumlar genellikle bir tür doğrulanmamış veri girişi işlenirken ortaya çıkar. Eğer geçerli aralık yaklaşık eminseniz genellikle olması gerektiği gibi, genellikle kullanabilirsiniz operator[], ancak daha iyisi, ile yineleyiciler begin()ve end().


1

Bu makaleye göre , performans bir yana, kullanımda herhangi bir fark yaratmaz atveya operator[]sadece erişimin vektör boyutu içinde olması garanti edilirse. Aksi takdirde, erişim sadece vektörün kapasitesine bağlıysa, kullanımı daha güvenlidir at.


2
dışarıda ejderhalar var. o bağlantıya tıklarsak ne olur? (ipucu: Ben zaten biliyorum, ama StackOverflow'daki biz bağlantı çürümesine olmaz yorum, yani demek istediğim hakkında kısa bir özetini sunar tercih)
Sebastian Mach

Bahşiş için teşekkürler. Şimdi düzeltildi.
ahj

0

Not: Görünüşe göre bazı yeni insanlar neyin yanlış olduğunu söyleme nezaketine sahip olmadan bu yanıta olumsuz oy veriyor. Cevap Aşağıda doğrudur ve doğrulanabilir burada .

Gerçekten tek bir fark var: atsınırlar kontrol edilirken kontrol operator[]edilmiyor. Bu, hata ayıklama yapılarının yanı sıra sürüm yapıları için de geçerlidir ve bu, standartlar tarafından çok iyi belirtilmiştir. Bu kadar basit.

Bu, atdaha yavaş bir yöntemdir, ancak aynı zamanda kullanmamak için gerçekten kötü bir tavsiye at. Göreli sayılara değil, mutlak sayılara bakmalısınız. Kodunuzun çoğunun daha pahalı işlemler yaptığına güvenle bahse girebilirim at. Şahsen kullanmaya çalışıyorum atçünkü kötü bir hatanın tanımsız davranışlar yaratmasını ve üretime gizlice girmesini istemiyorum.


1
C ++ 'daki istisnalar, hata ayıklama aracı değil, hata işleme mekanizmasıdır. Atma neden Herb Sutter açıklıyor std::out_of_rangeveya herhangi bir biçimde std::logic_errorkendi başına, aslında, bir mantık hatasıdır burada .
Big Temp

@BigTemp - Yorumunuzun bu soru ve cevapla nasıl bir ilgisi olduğundan emin değilim. Evet, istisnalar çok konu tartışılıyor ama burada soru arasındaki farktır atve []ve cevabım sadece farkı belirtir. Ben şahsen performansın sorun olmadığı durumlarda "güvenli" yöntemi kullanıyorum. Knuth'un dediği gibi erken optimizasyon yapmayın. Ayrıca, felsefi farklılıklardan bağımsız olarak böcekleri üretimde olduğundan daha erken yakalamak iyidir.
Shital Shah
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.