Std :: vector öğelerinin bitişik olması garantili mi?


111

Sorum basit: std :: vektör öğelerinin bitişik olması garantili mi? Kelime sırasına göre, göstericiyi bir std :: vektörün ilk elemanına C-dizisi olarak kullanabilir miyim?

Hafızam bana iyi hizmet ediyorsa, C ++ standardı böyle bir garanti vermedi. Bununla birlikte, std :: vektör gereksinimleri öyledir ki, öğeler bitişik değilse bunları karşılamak neredeyse imkansızdır.

Biri bunu açıklayabilir mi?

Misal:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

valuesO ifbloğun içinde mutasyona uğrarsan başının belada olduğunu biliyorum . Yine de sorunuzun cevabını bilmiyorum, bu yüzden sadece bir yorum bırakıyorum. :)
Greg D

@Greg: Ne sorun - biraz ayrıntı verebilir misin?
Reunanen

Sanırım, yeni değerlerin itilmesinin dizinin geçersiz olmasına neden olacak bir "yeniden tahsisi" tetikleyebileceğini kastetti.
Martin Cote

Mutasyona uğrayan çağrılar values, özellikle boyutunu değiştiren (örneğin push_back()), kopyalanan işaretçiyi geçersiz kılan temel vektörün yeniden tahsis edilmesini isteyebilir array. Vektöre bir işaretçi yerine bir vector :: yineleyici kullanmanın da aynı prensibi vardır. :)
Greg D

1
Evet, sınıfın kendisinden bahsettiğimi açıklığa kavuşturmak için değerlerin etrafına yerleştirdim, içinde yer alan değerlerden değil. :) Talihsiz adlandırma ve tüm bunlar. Yine de bu sorunun alakalı olduğu genel durumda bunun gerçekten bir sorun olduğunu sanmıyorum - neden birisi belleğe bir işaretçi tutup sonra işaretçiyi kullanmak yerine vektörle uğraşmaya başlasın? Sersemlik.
Greg D

Yanıtlar:


118

Bu, C ++ 98 standardından tam olarak kaçırıldı, ancak daha sonra bir TR'nin parçası olarak eklendi. Önümüzdeki C ++ 0x standardı elbette bunu bir gereklilik olarak içerecektir.

N2798'den (C ++ 0x taslağı):

23.2.6 Sınıf şablon vektörü [vektör]

1 Bir vektör, rastgele erişim yineleyicileri destekleyen bir sıra kapsayıcısıdır. Ek olarak, sonunda sabit zamanlı ekleme ve silme işlemlerini (amorti edilmiş) destekler; Ortada eklemek ve silmek doğrusal zaman alır. Depolama yönetimi otomatik olarak yapılır, ancak verimliliği artırmak için ipuçları verilebilir. Bir vektörün elemanları bitişik olarak depolanır, yani v, T'nin bool dışında bir tür olduğu bir vektörse, o zaman tüm 0 <= n <v için & v [n] == & v [0] + n kimliğine itaat eder. .boyut().


3
Bu aynı zamanda ISO 14882, 2. Baskı: Bölüm 23.2.4 [lib.vector] 'da belirtilmiştir: "Bir vektörün elemanları bitişik olarak depolanır, yani v bir vektörse <T, Ayırıcı> burada T bool, sonra tüm 0 <= n <v.size () için & v [n] == & v [0] + n kimliğine itaat eder. "
Mike Caron

4
so s, TR, TC, :) Aslında C ++ 03, okuduğum şeyden C ++ 98-TC1 (teknik düzeltme) olarak da adlandırılıyor
Johannes Schaub - litb

2
Vektörlerin vektörleri ne olacak? İç vektörler, son grubun iç vektörlerinden hemen sonra mı?
huseyin tugrul buyukisik

1
@huseyin tugrul buyukisik bunun cevabını öğrendiniz mi? Bunun nasıl çalıştığını da merak ediyorum
David Doria

1
@huseyin tugrul buyukisik Tabii ki doğru, ama std::vectorbitişik olan sonraki örneklerdir . Örneğin: std::vector<std::vector<int>> vöğelerde v[0], v[1]... sonradan bellekte saklanır, ancak öğede saklanır v[0].back()ve v[1].front()olacağı garanti edilmez.
jarzec

27

Diğer yanıtların da işaret ettiği gibi, bir vektörün içeriğinin sürekli olması garantilidir (bool'un tuhaflığı hariç).

Eklemek istediğim yorum şudur: Vektör üzerinde, vektörün belleğini yeniden tahsis etmesine neden olabilecek bir ekleme veya silme işlemi yaparsanız, kaydedilen tüm işaretçilerinizin ve yineleyicilerinizin geçersiz olmasına neden olursunuz.


1
Öğeler yine de bitişik bir bellek bloğunda saklanacaktı, sadece farklı bir yerde olacaktı. Soru özellikle yakınlık hakkındaydı.
Dima

2
Ancak mevcut işaretçiler ve yineleyiciler geçersiz kılınacaktır.
Bill Lynch

İyi bir nokta. Ne demek istediğini netleştirmek için bunu cevabına eklemelisin.
Dima

bana en yararlı cevap
CoffeDeveloper

Şimdi, dün programımın neden segfaulting yaptığını biliyorum, bazı öğeleri kaldırarak çift döngü içinde döngüye girdiğimde :) Teşekkürler!
user2891462

9

Standart aslında a'nın vectorbellekte sürekli olduğunu ve bunun bir dizi bekleyen bir işleve &a[0]geçirilebileceğini garanti etmez C.

Bu kuralın istisnası, vector<bool>her biri için yalnızca bir bit kullanmasıdır , bu boolnedenle sürekli belleğe sahip olmasına rağmen, bir olarak kullanılamaz bool*(bu genellikle yanlış bir optimizasyon ve bir hata olarak kabul edilir).

BTW, neden yineleyiciler kullanmıyorsun? Onlar bunun için.


1
> BTW, neden yineleyiciler kullanmıyorsunuz? Onlar bunun için. Belki Alexanrescu'nun konuyla ilgili yeni makalesini okudu
Nemanja Trifunovic

Bağlantı için teşekkürler, okuma listeme geleceğim (Alexandresu'nun makalelerini kaçırmamaya çalışıyorum)
Motti

Mwahaha, bugünlerde herkes bu sunum hakkında konuşuyor gibi görünüyor. Bakın, bu konudaki
Johannes Schaub - litb

Dikkatlice okursanız, Alexandrescu'nun makalesi gerçekten "C ++ 'da yineleyiciler kullanmayın" demiyor, "D'ye bakın" diyor. Bu makalede anlattığı yaklaşım, işlevsel mirası (List, Scheme, Haskell) özümseyen mevcut dillere ve çerçevelere çarpıcı bir şekilde benziyor ve başka bir C tabanlı sözdiziminin daha iyi bir başlangıç ​​noktası olup olmadığından ciddi şekilde şüpheliyim liste işleme. Geçen yıl bir ara onu kısaca, hatırı sayılır yeteneklerini C # gibi önceden kurulmuş bir dili geliştirmeye yönlendirmeye ikna etmeye çalıştım, ancak başarıdan korkmuyorum! :)
Daniel Earwicker

6

Diğerlerinin daha önce de söylediği gibi, vectordahili olarak bitişik bir nesne dizisi kullanır. Sabit olmayan herhangi bir üye işlev IIRC olarak adlandırıldığında bu diziye işaretçiler geçersiz olarak değerlendirilmelidir.

Ancak, bir istisna var !!

vector<bool>yerden tasarruf etmek için tasarlanmış özel bir uygulamaya sahiptir, böylece her bool yalnızca bir bit kullanır. Temeldeki dizi bitişik bir bool dizisi vector<bool>değildir ve dizi aritmetiği açık olduğu gibi çalışmaz vector<T>.

(Her zaman yeni bir tane uygulayabildiğimiz için, bunun herhangi bir vektör uzmanlaşması için de geçerli olabileceğini düşünüyorum. Bununla birlikte, std::vector<bool>basit işaretçi aritmetiğinin çalışmayacağı tek, hatalı, standart uzmanlıktır.)


Kullanıcının uzmanlaşmasına izin verilmez std::vectorve diğer tüm vektörlerin bitişik depolamayı kullanması gerekir. Bu nedenle, std::vector<bool>(neyse ki) tuhaf olan tek standart vektördür. (Bu uzmanlığın kullanımdan kaldırılması ve örneğin std::dynamic_bitsetaynı işlevselliğe sahip bir ile değiştirilmesi gerektiğine kuvvetle inanıyorum . Kötü bir veri yapısı değil, sadece bir vektör değil.)
Arne Vogel

3

Bu iş parçacığını buldum çünkü bitişik bellek kullanan vektörlerin bir avantaj olduğu bir kullanım durumum var.

OpenGL'de köşe tampon nesnelerini nasıl kullanacağımı öğreniyorum. Tampon mantığını içerecek bir sarmalayıcı sınıfı yarattım, bu yüzden tek yapmam gereken tampon oluşturmak için bir dizi float ve birkaç yapılandırma değeri geçirmek. Kullanıcı girdisine dayalı bir işlevden bir arabellek oluşturabilmek istiyorum, bu nedenle uzunluk derleme zamanında bilinmiyor. Bunun gibi bir şey yapmak en kolay çözüm olacaktır:

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

Şimdi vektörün yüzer sayılarını bir dizi olarak OpenGL'nin tamponla ilgili işlevlerine geçirebilirim. Bu aynı zamanda dizinin uzunluğunu belirlemek için sizeof gereksinimini de ortadan kaldırır.

Bu, kayan sayıları depolamak için büyük bir dizi ayırmaktan ve onu yeterince büyük yaptığımı ummaktan veya bitişik depolama ile kendi dinamik dizimi yapmaktan çok daha iyidir.


2
bu işlev bana hiç mantıklı gelmiyor. Kendisinden vziyade bir referans veya bir gösterici vmi geçirmeyi kastediyorsunuz ? çünkü vtek başına geçmek , işlevin içinde bir kopya yapılmasına neden olur ve bu, işlev sona erdikten sonra yok olur. Bu nedenle, vektörün üzerine yalnızca fonksiyon sona erdiğinde vektörü silmek için bir şey itiyorsunuz.
johnbakers

1

cplusplus.com:

Vektör kapları dinamik diziler olarak uygulanır; Tıpkı normal dizilerde olduğu gibi, vektör konteynerlerinin de öğeleri bitişik depolama konumlarında depolanır; bu, öğelerine yalnızca yineleyiciler kullanılarak değil, aynı zamanda öğelere yönelik normal işaretçilerdeki ofsetler kullanılarak da erişilebileceği anlamına gelir.


1

Evet, std :: vector öğelerinin bitişik olması garantilidir.


Sağ. Sanırım bunlardan çok kullanıyorum :)
Benoît
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.