İçeriğe geç

Menü

Berk Akademi
Yorumlar Google yorumları Özel Ders Başvuru Ücretsiz ön görüşme Ana Sayfa

C++ vector Nasıl Kullanılır? Eleman Ekleme, Silme ve Dolaşma

cpp-vector-kullanimi-eleman-ekleme-silme-dolasma
Bu yazıda neler var?
  1. std::vector nasıl tanımlanır ve başlangıç değeri verilir?
  2. vector elemanları nasıl eklenir ve güvenli biçimde silinir?
  3. size(), [] ve at() arasındaki fark nedir?
  4. vector elemanları hangi yöntemlerle dolaşılır?
  5. vector fonksiyona referansla nasıl gönderilir?
  6. Notları filtreleyen tam C++17 örneği nasıl yazılır?
  7. Sık Sorulan Sorular

C++ vector kullanımı, eleman sayısı çalışma sırasında değişebilen koleksiyonlarla çalışmayı kolaylaştırır. std::vector ile eleman ekleyebilir, sondan veya belirli bir konumdan silebilir ve elemanlar arasında indeks, aralık tabanlı döngü ya da iterator kullanarak dolaşabilirsin. Bu yazıdaki örnekler C++17 standardına göre hazırlanmıştır; yayımlanmamış standart özellikleri kapsam dışında bırakılmıştır.

Özellikle not listeleri, ölçüm değerleri ve kullanıcıdan alınan değişken sayıdaki veriler için vector, sabit uzunluklu dizilere göre daha esnek bir yapı sunar. Eleman silme sırasında iterator geçerliliğine dikkat etmek, çalışma zamanında oluşabilecek hataları önlemek açısından önemlidir.

std::vector nasıl tanımlanır ve başlangıç değeri verilir?

std::vector, çalışma sırasında eleman eklenip çıkarılabildiği için boyutu büyüyüp küçülebilen bir dizi yapısıdır. Kullanabilmek için önce <vector> başlık dosyasını eklemen, ardından eleman türünü köşeli parantez benzeri açı parantezleri içinde belirtmen gerekir.

#include <vector>

int main() {
    std::vector<int> notlar;
    return 0;
}

Bu program, henüz eleman içermeyen bir std::vector<int> oluşturur. notlar değişkeni yalnızca tam sayı değerleri saklayabilir ve daha sonra push_back ile eleman alabilir.

Başlangıç değerlerini doğrudan vermek için başlatıcı liste kullanabilirsin:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notlar{75, 82, 90};

    for (int notDegeri : notlar) {
        std::cout << notDegeri << ' ';
    }

    return 0;
}

Bu örnekte vector üç elemanla oluşturulur. Döngü çalıştığında ekrana 75 82 90 yazdırılır. Süslü parantez içindeki değerlerin türü, vector içinde belirtilen türle uyumlu olmalıdır.

Belirli sayıda başlangıç elemanı oluşturmak istediğinde boyutu ve başlangıç değerini birlikte verebilirsin:

#include <iostream>
#include <vector>

int main() {
    std::vector<int> puanlar(4, 0);

    for (int puan : puanlar) {
        std::cout << puan << ' ';
    }

    return 0;
}

Burada dört elemanlı bir vector oluşturulur ve tüm elemanlara 0 değeri verilir. Programın çıktısı 0 0 0 0 olur. std::vector<int> puanlar(4) biçimi de dört tam sayı oluşturur; tam sayı türünde bu elemanlar sıfırla başlatılır.

vector elemanları nasıl eklenir ve güvenli biçimde silinir?

vector elemanları nasıl eklenir ve güvenli biçimde silinir?

Eleman eklemek için push_back, sondaki elemanı kaldırmak için pop_back ve belirli bir konumdaki elemanı kaldırmak için erase kullanılır. pop_back çağırmadan önce vector'ün boş olup olmadığını kontrol etmelisin.

#include <iostream>
#include <vector>

int main() {
    std::vector<int> notlar{70, 85};

    notlar.push_back(95);

    if (!notlar.empty()) {
        notlar.pop_back();
    }

    notlar.erase(notlar.begin() + 1);

    for (int notDegeri : notlar) {
        std::cout << notDegeri << ' ';
    }

    return 0;
}

Program önce sona 95 ekler, ardından vector boş olmadığı için bu elemanı kaldırır. erase(notlar.begin() + 1) ifadesi, ikinci konumdaki 85 değerini siler. Çıktı 70 olur.

Iterator, vector içindeki elemanlar arasında ilerlemeyi sağlayan bir konum göstericisidir. begin() ilk elemanı, end() ise son elemandan sonraki konumu gösterir. Bu nedenle aralık silerken başlangıç konumu dahil, bitiş konumu hariç tutulur.

std::vector<int> sayilar{10, 20, 30, 40, 50};

sayilar.erase(sayilar.begin() + 1, sayilar.begin() + 3);

Bu ifade, ikinci ve üçüncü elemanları, yani 20 ve 30 değerlerini siler. Vector içinde 10 40 50 kalır.

Döngü sırasında silme yaparken silinen konumu artırmamalısın. erase sonrasında aynı iterator nesnesini kullanmaya devam etmek hatalıdır; çünkü silinen elemanı gösteren iterator geçersiz hâle gelir. Geçersiz bir iterator üzerinde işlem yapmak, standardın programın sonucunu belirlemediği tanımsız davranışa yol açabilir.

std::vector<int> notlar{45, 70, 50, 90};

for (auto it = notlar.begin(); it != notlar.end();) {
    if (*it < 60) {
        it = notlar.erase(it);
    } else {
        ++it;
    }
}

Doğru yaklaşımda erase tarafından döndürülen yeni iterator tekrar it değişkenine atanır. Eleman silinmediğinde ise iterator bir sonraki konuma ilerletilir. Bu işlemin sonunda 60 değerinden küçük notlar silinir ve vector içinde 70 90 kalır.

size(), [] ve at() arasındaki fark nedir?

size() vector içindeki eleman sayısını verir; [] ve at() ise belirli bir indeksteki elemana erişir. Ancak iki erişim yöntemi, indeks sınırlarını aynı şekilde denetlemez.

Bir vector üç eleman içeriyorsa size() değeri 3 olur ve geçerli indeksler 0, 1 ve 2 ile sınırlıdır. [] kullanırken bu aralığın dışındaki bir indekse erişmek tanımsız davranışa yol açabilir. Tanımsız davranış, programın sonucunun C++ standardı tarafından garanti edilmemesi anlamına gelir. at() ise sınır kontrolü yapar ve geçersiz erişimde istisna oluşturabilir.

std::vector<int> puanlar{70, 85, 90};

std::cout << puanlar[1] << 'n';
std::cout << puanlar.at(2) << 'n';

Bu kodda puanlar[1] ikinci elemana, puanlar.at(2) ise üçüncü elemana erişir. Çıktı sırasıyla 85 ve 90 olur. İki erişim de geçerli indeks kullandığı için aynı vector üzerinde güvenli biçimde çalışır.

İndeksin geçerli olup olmadığını önceden kontrol etmek, [] kullanımında güvenli bir yaklaşım sağlar:

std::size_t indeks = 3;

if (indeks < puanlar.size()) {
    std::cout << puanlar[indeks] << 'n';
} else {
    std::cout << "Geçersiz indeksn";
}

Vector üç eleman içerdiği için 3 numaralı indeks geçerli değildir. Koşul başarısız olur ve program Geçersiz indeks çıktısını üretir. Özellikle indeks hesaplamayla elde ediliyorsa size() ile sınır kontrolü yapmak erişim hatalarını önlemeye yardımcı olur.

vector elemanları hangi yöntemlerle dolaşılır?

vector elemanları hangi yöntemlerle dolaşılır?

Vector elemanları klasik for döngüsü, aralık tabanlı for döngüsü veya iterator kullanımıyla dolaşılabilir. Klasik yöntem, indeks gerektiğinde kullanışlıdır:

std::vector<int> sayilar{10, 20, 30};

for (std::size_t i = 0; i < sayilar.size(); ++i) {
    std::cout << sayilar[i] << ' ';
}

Döngü indeksi 0'dan başlar ve size() değerine ulaşmadan önce durur. Çıktı 10 20 30 olur. Sınır koşulunda i < sayilar.size() yazılması, son geçerli indeksten sonra erişim yapılmasını engeller.

Aralık tabanlı for, indeks gerekmiyorsa daha okunabilir bir seçimdir:

for (const auto& sayi : sayilar) {
    std::cout << sayi << ' ';
}

const auto& kullanımı elemanları kopyalamadan okumaya yarar. Bu parça da 10 20 30 çıktısını üretir. Bu dolaşma sırasında vectorün eleman sayısını değiştirmekten kaçınmak gerekir.

Iterator, bir container içindeki elemanlara sırayla ulaşmayı sağlayan araçtır. begin() ilk elemanı, end() ise son elemandan sonraki konumu gösterir:

for (auto it = sayilar.begin(); it != sayilar.end(); ++it) {
    std::cout << *it << ' ';
}

*it ifadesi iteratorün gösterdiği elemana erişir. Iterator döngüsünün çıktısı yine 10 20 30 olur. end() konumunun kendisine erişilmez; iterator yalnızca bu konuma ulaşınca döngü sonlandırılır.

vector fonksiyona referansla nasıl gönderilir?

Fonksiyon vector üzerinde değişiklik yapacaksa referans, yalnızca okuyacaksa const referans kullanılmalıdır. Referans, mevcut vector nesnesine başka bir ad üzerinden erişmeyi sağlayan parametre biçimidir. Bu nedenle referans alan fonksiyon, çağrıldığı yerdeki nesnenin içeriğini değiştirebilir.

#include <iostream>
#include <vector>

void notEkle(std::vector<int>& notlar, int notu) {
    notlar.push_back(notu);
}

void yazdir(const std::vector<int>& notlar) {
    for (int notu : notlar) {
        std::cout << notu << ' ';
    }

    std::cout << 'n';
}

int main() {
    std::vector<int> notlar = {70, 80};

    std::cout << "Fonksiyon çağrısından önce: ";
    yazdir(notlar);

    notEkle(notlar, 90);

    std::cout << "Fonksiyon çağrısından sonra: ";
    yazdir(notlar);
}

Program önce 70 80 değerlerini yazdırır. notEkle fonksiyonu referans aldığı için 90 değerini doğrudan notlar nesnesine ekler ve ikinci çıktıda 70 80 90 görülür. yazdir ise const referans kullandığından elemanları okuyabilir, fakat vector içeriğini değiştiremez.

vector değere göre parametre olarak gönderilirse fonksiyon için ayrı bir kopya oluşturulabilir. Kopyaya ihtiyaç olmayan ve yalnızca okuma yapılacak durumlarda const referans kullanmak, mevcut nesne üzerinden çalışmayı sağlar.

Notları filtreleyen tam C++17 örneği nasıl yazılır?

Program kullanıcıdan notları alıp vector içinde tutacak, ortalamayı hesaplayacak ve 50'nin altındaki notları güvenli iterator güncellemesiyle silecektir. Iterator, vector içindeki belirli bir konumu gösteren ve bu konumlar arasında ilerlemeyi sağlayan nesnedir. erase silme işleminden sonra sıradaki geçerli konumu döndürdüğü için bu değer tekrar iterator'e atanır.

#include <iomanip>
#include <iostream>
#include <vector>

int main() {
    int adet;
    std::cin >> adet;

    if (adet <= 0) {
        return 0;
    }

    std::vector<int> notlar;

    for (int i = 0; i < adet; ++i) {
        int notu;
        std::cin >> notu;
        notlar.push_back(notu);
    }

    int toplam = 0;

    for (int notu : notlar) {
        toplam += notu;
    }

    double ortalama =
        static_cast<double>(toplam) / notlar.size();

    for (auto it = notlar.begin(); it != notlar.end();) {
        if (*it < 50) {
            it = notlar.erase(it);
        } else {
            ++it;
        }
    }

    std::cout << "Ortalama: "
              << std::fixed
              << std::setprecision(2)
              << ortalama
              << 'n';

    std::cout << "Filtrelenen notlar: ";

    for (int notu : notlar) {
        std::cout << notu << ' ';
    }

    std::cout << 'n';
}

Örneğin program şu girdiyi alabilir:

4
40 70 55 30

Toplam not 195 olduğu için ortalama 48.75 olarak hesaplanır. 40 ve 30 değerleri 50'nin altında kaldığından silinir. Programın çıktısı şu olur:

Ortalama: 48.75
Filtrelenen notlar: 70 55

Döngü içinde silme işleminden sonra eski iterator üzerinde işlem yapmak, tanımsız davranışa yol açabilir. Tanımsız davranış, C++ standardının program için belirli bir sonuç tanımlamadığı durumdur. Bu nedenle silme gerçekleştiğinde it = notlar.erase(it) yazılır, silme gerçekleşmediğinde ise ++it ile bir sonraki elemana geçilir.

  • erase sonrasında iterator'ü, fonksiyonun döndürdüğü geçerli değerle güncelle.
  • [] kullanırken indeksin size() sınırları içinde kaldığını kontrol et.
  • Büyük vector'leri kopyalamamak için kopyaya ihtiyaç olmayan fonksiyonlarda const referans kullan.

Sık Sorulan Sorular

std::vector ile klasik C++ dizisi arasındaki temel fark nedir?

std::vector çalışma sırasında eleman ekleyip çıkarabileceğin, boyutu değişebilen bir kapsayıcıdır. Klasik C++ dizisinin eleman sayısı ise tanımlandığı yapı içinde sabittir. Bu nedenle eleman sayısının program çalışırken değişmesi bekleniyorsa vector daha uygun bir seçim olabilir.

erase döngü içinde kullanılırken iterator neden güncellenmelidir?

erase silinen elemandan sonraki geçerli konumu gösteren bir iterator döndürür. Bu dönüş değeri kullanılmaz ve eski iterator ile devam edilirse geçersizleşmiş bir iterator'e erişilebilir. Döngü içinde güvenli kalıp it = vector.erase(it) biçimidir.

at() ile [] arasında hangi durumda seçim yapılır?

İndeksin geçerli olduğundan emin olduğun ve kısa erişim sözdizimi istediğin durumlarda [] kullanılabilir. İndeks dışarıdan geliyor veya sınır kontrolü önemliyse at() tercih edilebilir; geçersiz indeks durumunda sınır hatasını bildiren bir istisna üretir.

Bir vector fonksiyona ne zaman const referansla gönderilir?

Fonksiyon vector içeriğini yalnızca okuyacak ve değiştirmeyecekse const std::vector<T>& biçimi kullanılabilir. Böylece fonksiyonun çağırandaki vector'ü değiştirmesi engellenir ve kopyaya ihtiyaç duyulmaz.

std::vector kullanırken referans türünü, iterator güncellemesini ve indeks sınırlarını birlikte düşünmek, kodun daha güvenli ve anlaşılır olmasına yardımcı olur.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin, yazılım geliştirici ve eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; İzmir Ekonomi Üniversitesi'ni bölüm birincisi ve yüksek şeref öğrencisi olarak tamamladı. Bugün yalnızca eğitim vermekle kalmıyor, sektörde aktif olarak yazılım projeleri geliştiriyor ve gerçek dünya deneyimini birebir derslerine taşıyor. Ezberden uzak, mühendislik zihniyetini merkeze alan sürdürülebilir öğrenme sistemleri tasarlayarak sorgulayan, üreten ve problem çözebilen yeni nesil yazılımcılar yetiştiriyor.

Sektörel Deneyim & Projeler

  • Ticarify Entegrasyon Yazılım logosu CEO Ticarify Entegrasyon YazılımPazaryerleri ve e-ticaret sitelerine otomatik e-fatura kesimi, sipariş ve kargo takibi hizmetleri sunan e-Dönüşüm platformunun API mimarisini ve yazılım ekibini yönetmektedir.
  • Benim Düğünüm logosu CEO Benim DüğünümDijital etkinlik ve anı paylaşım platformu.
  • Siberdizayn logosu Yazılım Ekibi Lideri SiberdizaynYüksek anlık oyuncu trafiğine sahip oyun kontrol panelleri ve sunucu altyapıları geliştiren yazılım ekibine liderlik etmektedir.
  • MEDYOGRAFYA 360° Dijital Çözümler logosu Dijital Strateji Lideri MEDYOGRAFYA 360° Dijital ÇözümlerŞirketlerin dijital çözümlerde uzun vadede nasıl ilerlemesi gerektiği ve dijital dönüşüm süreçlerinin yönetilmesine destek olmaktadır.
  • İzmir Ekonomi Üniversitesi logosu Danışma Kurulu Üyesi İzmir Ekonomi ÜniversitesiMezun olduğu üniversitesinde, Bilgisayar Programcılığı bölümünün akademik müfredatını güncel sektör ihtiyaçlarına göre şekillendirmek adına Danışma Kurulu'nda görev almaktadır.
WhatsApp Hemen Ara