C ++ STL'de const_iterator ve const olmayan yineleyici arasındaki fark nedir?


139

A const_iteratorve a arasındaki fark nedir ve iteratorbirini diğerinin üzerinde nerede kullanırsınız?


1
Peki, const_iterator adı yineleyici gibi görünüyor, bu yineleyicinin işaret ettiği şey gerçek const.
talekeDskobeDa

Yanıtlar:


124

const_iteratorişaret ettikleri değerleri değiştirmenize izin vermiyor, normal iterators.

C ++ 'daki her şeyde olduğu gibi, constdüzenli yineleyiciler kullanmak için iyi bir neden olmadığı sürece her zaman tercih edin (yani const, işaret edilen değeri değiştirmek zorunda olmadıklarını kullanmak istiyorsanız ).


8
Mükemmel bir dünyada böyle olur. Ancak C ++ const ile sadece kodu yazan kişi kadar iyidir :(
JaredPar

mutable çok iyi bir nedenden dolayı var. Nadiren kullanılır ve Google Kod Arama'ya bakıldığında, geçerli kullanımların adil bir yüzdesi olduğu görülmektedir. Anahtar kelime çok güçlü bir optimizasyon aracıdır ve kaldırmanın doğruluğunu artıracağı gibi değil ( coughpointerscoughandcoughreferencescough )
coppro

2
Daha çok güçlü bir hack gibi. 'Mutable' anahtar kelimesinin kullanımıyla ilgili gördüğüm tüm örneklerden biri hariç tümü, kodun kötü yazılmış olduğunu ve hataların giderilmesi için bir saldırı olarak mutable'a ihtiyaç duyulduğunu gösteren doğru bir göstergedir.
John Dibling

7
Bir const sınıfı içinde uzun bir hesaplamanın sonuçlarını önbelleğe almak gibi meşru kullanımları vardır. Öte yandan, neredeyse yirmi yıllık C ++ gelişiminde değişebilen bir zaman kullandım.
Kafa Geek


40

Hemen hemen açıklayıcı olmalıdırlar. Yineleyici T türündeki bir öğeyi gösteriyorsa, const_iterator 'const T' türündeki bir öğeyi gösterir.

Temel olarak işaretçi türlerine eşdeğerdir:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

Bir sabit yineleyici her zaman aynı öğeyi işaret eder, bu nedenle yineleyicinin kendisi sabittir. Ancak, işaret ettiği öğenin const olması gerekmez, bu nedenle işaret ettiği öğe değiştirilebilir. Bir const_iterator, bir const öğesine işaret eden bir yineleyicidir, bu nedenle yineleyicinin kendisi güncellenebilirken (örneğin artırılabilir veya azaltılabilir), işaret ettiği öğe değiştirilemez.


1
"Sabit yineleyici her zaman aynı öğeye işaret eder," Bu yanlış.
John Dibling

2
Nasıl yani? Eksik alt çizgiyi not edin. Ben bir tür değişkeni stt :: vector <T> :: iterator std :: vector <T> :: const_iterator ile tezat. Önceki durumda, yineleyicinin kendisi sabittir, bu nedenle değiştirilemez, ancak referans aldığı öğe serbestçe değiştirilebilir.
jalf

4
Ah, anlıyorum. Evet eksik alt çizgiyi kaçırdım.
John Dibling

3
@JohnDibling const iteraterve arasındaki inceliği açıkladığı için seçildi const_iterator.
legends2k

8

Ne yazık ki, STL kapları için birçok yöntem const_iterators yerine yineleyicileri parametre olarak alır . Bir const_iterator'ınız varsa, "bu yineleyicinin işaret ettiği öğeden önce bir öğe ekleyin" diyemezsiniz (bence böyle bir şey kavramsal olarak bir const ihlali değildir). Yine de bunu yapmak istiyorsanız, std :: advance () veya boost :: next () kullanarak sabit olmayan bir yineleyiciye dönüştürmeniz gerekir . Örneğin. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Eğer kap bir olan std :: liste , daha sonra bu çağrı için çalışma süresi olacak O (n) .

Bu nedenle, "mantıklı" olan her yerde const eklemek için evrensel kural, STL kapları söz konusu olduğunda daha az evrenseldir.

Bununla birlikte, destek kapları const_iterators alır (örn. Boost :: unordered_map :: erase ()). Yani takviye kapları kullandığınızda "const agresif" olabilirsiniz. Bu arada, STL kaplarının sabitlenip sabitlenmeyeceğini veya ne zaman sabitleneceğini bilen var mı?


1
Bu bir görüş meselesi olabilir. Durumunda vectorve dequebir eleman geçersiz çok değil tüm mevcut yineleyicinızı, ekleme, const. Ama ne demek istediğini anlıyorum. Bu tür işlemler constyineleyiciler tarafından değil, konteyner tarafından korunur . Ve neden standart konteyner arayüzünde bir const-non-const yineleyici dönüşüm fonksiyonu olmadığını merak ediyorum.
Potatoswatter

Ben kategorik duyuyorum, doğru Potatoswatter vardır, bu rasgele erişim konteynerler için görüş ve meselesidir container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) olduğunu O (1) neyse. Ayrıca rastgele olmayan erişim kapları için neden bir dönüştürme işlevi olmadığını merak ediyorum, ama belki de iyi bir neden var mı? Rastgele olmayan erişim kaplarının işlevlerinin const_iterators almamasının bir nedeni olup olmadığını biliyor musunuz ?
Magnus Andermo

"böyle bir şey söylemek kavramsal olarak bir const ihlali, bence" - bu çok ilginç bir yorum, konu hakkında aşağıdaki düşünceler var. Basit işaretçilerle, söylenebilir int const * foo; int * const foo;ve int const * const foo;her üçü de her biri kendi yollarıyla geçerli ve yararlıdır. std::vector<int> const barikincisi ile aynı olmalıdır, ancak maalesef genellikle üçüncü gibi tedavi edilir. Sorunun temel nedeni, bir vektördeki ile std::vector<int const> bar;aynı etkiyi elde etmenin bir yolu olmadığı anlamına int const *foo;gelemediğimizdir.
dgnuff

5

Mümkün olduğunca const_iterator kullanın , başka seçeneğiniz yoksa yineleyici kullanın .


4

Minimal çalıştırılabilir örnekler

Sabit olmayan yineleyiciler işaret ettiklerini değiştirmenize olanak tanır:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Const yineleyicileri şunları yapmaz:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

Yukarıda gösterildiği gibi, v.begin()bir constaşırı ve döner ya iteratorya da const_iteratorkap değişkeninin const-lik bağlı olarak:

const_iteratorAçılan yaygın bir durum this, bir constyöntemin içinde kullanıldığı zamandır :

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

constthisconst yapar , bu da this->vconst yapar .

Genellikle bunu unutabilirsiniz auto, ancak bu yineleyicileri geçmeye başlarsanız, yöntem imzaları için bunları düşünmeniz gerekir.

Const ve const olmayanlara benzer şekilde, const olmayanlardan const'a kolayca dönüştürebilirsiniz, ancak bunun tersi olmaz:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

Hangisinin kullanmak için: analog için const intvs int: const yineleyicinızı tercih bunları kullanabilirsiniz zaman (onlarla kabı değiştirmeye gerek yoktur olduğunda), daha iyi belgeye değiştirmeden okuma niyetinizi.


0

(diğerlerinin söylediği gibi) const_iterator, işaret ettiği öğeleri değiştirmenize izin vermez, bu const sınıfı yöntemlerinin içinde yararlıdır. Ayrıca niyetinizi ifade etmenizi sağlar.


0

Tamam Sabit yineleyici kullanmadan önce çok basit bir örnekle açıklayayım. Rasgele tamsayı koleksiyonu "randomData"

    for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;

Koleksiyon içinde veri yazmak / düzenlemek için görüldüğü gibi normal yineleyici kullanılır, ancak okuma amacıyla sabit yineleyici kullanılmıştır. Döngü için önce sabit yineleyici kullanmayı denerseniz hata alırsınız. Temel kural olarak, toplama içindeki verileri okumak için sürekli yineleyici kullanın.

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.