📌 Hızlı Özet & Temel Çıkarım:
Modern C++ (C++20 ve C++23), C dilinin sunduğu çıplak donanım kontrolü ve mikrosaniye seviyesindeki işlem hızını; sıfır ek maliyetli soyutlamalar (zero-cost abstractions), Akıllı İşaretçiler (Smart Pointers), RAII prensipleri ve derleme zamanı tip kısıtları (Concepts) ile birleştiren en güçlü sistem programlama dilidir. Doğru bellek yerleşimi (cache locality) ve taşıma semantiği (move semantics) ile çöp toplayıcı (Garbage Collector) gecikmelerini sıfıra indirir.


1. Eski C++’tan Modern Standartlara: Manuel new/delete Döneminin Sonu

Geleneksel C++’ta yazılımcıların en büyük kabusu bellek sızıntıları (memory leaks), sarkan işaretçiler (dangling pointers) ve çift serbest bırakma (double free) hatalarıydı.

Modern C++ bu riskleri Kaynak Edinimi İlklendirmedir (RAII - Resource Acquisition Is Initialization) prensibiyle tamamen ortadan kaldırmıştır:

  • Bir kaynak (RAM, dosya tanıtıcısı, TCP soketi) bir nesnenin kurucusunda (constructor) tahsis edilir.
  • Nesne kapsam dışına (scope) çıktığı anda yıkıcısında (destructor) otomatik ve istisnasız serbest bırakılır.

Bu sayede modern projelerde new ve delete anahtar kelimelerine neredeyse hiç rastlanmaz; yerini std::unique_ptr ve std::make_shared almıştır.


2. Taşıma Semantiği (Move Semantics) ve Derleme Zamanı Şablonları

Gereksiz bellek kopyalamaları yüksek frekanslı robotik veya ağ uygulamalarında ciddi darboğazlar üretir. C++11 ile gelen std::move ve rvalue referansları, büyük veri bloklarını (örneğin 10 MB’lık bir nokta bulutunu) kopyalamak yerine işaretçi sahipliğini aktararak işlemi 0 milisaniyede tamamlar.

C++20 ile gelen Concepts ise şablonların (templates) anlaşılmaz derleyici hata çıktılarını sonlandırır:

#include <iostream>
#include <concepts>
#include <vector>
#include <memory>

// Yalnizca aritmetik tipleri kabul eden modern C++20 konsepti:
template<typename T>
concept Numeric = std::is_arithmetic_v<T>;

template<Numeric T>
class RingBuffer {
public:
    explicit RingBuffer(size_t capacity) : capacity_(capacity) {
        data_.reserve(capacity);
    }

    void push(T value) noexcept {
        if (data_.size() < capacity_) {
            data_.push_back(value);
        } else {
            data_[head_] = value;
            head_ = (head_ + 1) % capacity_;
        }
    }

    [[nodiscard]] size_t size() const noexcept { return data_.size(); }

private:
    std::vector<T> data_;
    size_t capacity_;
    size_t head_{0};
};

int main() {
    auto buffer = std::make_unique<RingBuffer<double>>(1000);
    buffer->push(42.5);
    buffer->push(99.1);
    std::cout << "Tampon Boyutu: " << buffer->size() << std::endl;
    return 0;
}

Bu kodda RingBuffer<std::string> derlenmeye çalışılırsa, derleyici anında “Numeric konsepti sağlanmadı” şeklinde açık ve net bir hata verir.


3. Donanım Önbelleği (CPU Cache Locality) Optimizasyonu

Modern işlemcilerde RAM erişimi (yaklaşık 200 döngü), L1 önbellek erişimine (yaklaşık 4 döngü) kıyasla çok yavaştır.

Eğer verilerinizi hafızaya dağınık işaretçiler (linked list, ağaç yapıları) halinde yerleştirirseniz işlemci sürekli Cache Miss yaşar. Bunun yerine verileri bitişik dizi (std::vector) formatında saklamak, işlemcinin prefetcher mekanizmasını tetikler ve döngü performansını 10 kata kadar artırır.


4. İlgili Konular ve İç Bağlantılar


5. Sıkça Sorulan Sorular

C++ yerine Rust kullanmak günümüzde daha mantıklı değil midir?

Rust, derleme zamanındaki mülkiyet (ownership) modeliyle bellek güvenliğini zorunlu kılar ve harika bir dildir. Ancak özellikle robotik (ROS 2), bilgisayarlı görü (OpenCV, CUDA) ve simülasyon (Gazebo) ekosistemlerinde C++ kütüphane desteği ve donanım sürücüsü entegrasyonu hala açık ara liderliğini korumaktadır.

std::shared_ptr performans kaybı yaratır mı?

Evet. std::shared_ptr referans sayacını atomik (atomic) olarak artırıp azaltır. Çok iş parçacıklı (multi-threaded) ortamlarda atomik operasyonlar CPU çekirdekleri arasında önbellek geçersiz kılma (cache invalidation) maliyeti doğurur. Mümkün olan her yerde tekil sahiplik sunan std::unique_ptr tercih edilmelidir.

6. C++20 Coroutines ile Asenkron ve Olay Tabanlı I/O

Geleneksel multithreading modellerinde her iş parçacığı için işletim sisteminden 2 ila 8 MB stack belleği ayrılır. Binlerce eşzamanlı sensör veya ağ bağlantısı yönetirken bu durum gigabaytlarca belleğin heba olmasına yol açar.

C++20 ile dile eklenen Coroutines (Eşyordamlar), fonksiyonların çalışma anında duraklatılıp (co_await, co_yield) daha sonra kaldığı yerden devam edebilmesini sağlar. Yığınsız (stackless) çalıştıkları için bir coroutine nesnesinin bellek maliyeti yalnızca birkaç yüz bayttır.

Aşağıdaki örnek, sensör akışlarını asenkron olarak tüketen modern bir C++ jeneratör kalıbını temsil eder:

#include <coroutine>
#include <iostream>

template<typename T>
struct Generator {
    struct promise_type {
        T current_value;
        auto get_return_object() { return Generator{handle_type::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() noexcept { return std::suspend_always{}; }
        void unhandled_exception() { std::terminate(); }
        auto yield_value(T value) {
            current_value = value;
            return std::suspend_always{};
        }
        void return_void() {}
    };
    using handle_type = std::coroutine_handle<promise_type>;
    handle_type handle;

    ~Generator() { if (handle) handle.destroy(); }
    bool next() { handle.resume(); return !handle.done(); }
    T value() const { return handle.promise().current_value; }
};

Bu mekanizma, özellikle robotik araçlarda telemetri paketlerini donanım bekleme döngülerine girmeden minimal CPU tüketimiyle işlemeyi mümkün kılar.

7. Statik Polimorfizm: CRTP (Curiously Recurring Template Pattern) ile Sanal Fonksiyon Tablosu (vtable) Maliyetini Sıfırlama

Standart nesne yönelimli C++’ta virtual anahtar kelimesi kullanıldığında derleyici bir sanal metot tablosu (vtable) oluşturur. Çalışma anında her fonksiyon çağrısında bu tablodan işaretçi çözülür (indirect function call), bu da CPU işlemci dal tahmincisini (branch predictor) yanıltabilir.

Ultra yüksek hızlı algoritmik sistemlerde bunun yerine CRTP (Statik Polimorfizm) tercih edilir:

template <typename Derived>
class SensorBase {
public:
    void read_data() {
        // Calisma zamaninda vtable olmadan dogrudan alt sinifin metodu inline edilir:
        static_cast<Derived*>(this)->read_implementation();
    }
};

class LidarSensor : public SensorBase<LidarSensor> {
public:
    void read_implementation() {
        // Donanimdan laser taramasi oku...
    }
};

Bu yöntemle derleyici fonksiyonu doğrudan çağrı noktasına gömer (inlining); sanal fonksiyon ek yükü (overhead) tam olarak 0 nanosaniyeye iner.