İçeriğe geç
academia.sh

Ders 01 / 14

C ve C++ Farkları

C'de değerle aktarılan bir yapının C++'ta hangi nesneyle çalıştığını ölçen bir çekirdek koşumuyla kursun dört ölçüm sınıfını tanıtır.

Yayın Güncelleme

Bu derste

  • Değerle aktarılan bir yapının çağıranın nesnesiyle mi yoksa bir kopyayla mı çalıştığını ölçer
  • Bir çağrının hangi nesneyle çalıştığını yalnız işlevin bildirdiği parametre tipinden çıkarır
  • C++'ın C'ye eklediği temel dil öğelerini sayar
İçindekiler

M11/K03’te “Nesne Benzeri Tasarım” bir yapıyı ve ona bağlı işlev işaretçilerini elle bir araya getirerek nesne davranışını C’de taklit etti; hangi işlevin çalışacağı elle kurulan bir tabloda saklanıyordu. Bu kurs aynı soruyu C++’ın kendi dil öğeleriyle sorar: bir ifade, tam olarak hangi nesneyle çalışır — adı verilen nesnenin kendisiyle mi, ondan kopyalanmış yeni bir nesneyle mi, içindeki bir parçayla mı, yoksa adsız ve kısa ömürlü bir nesneyle mi?

Ölçü Ekseni

Kurs boyunca her ders bir ifadeyi dört sınıftan birine yerleştirir:

  • kendisi — ifade, adı verilen nesnenin tamamıyla çalışır.
  • kopya — ifade, adı verilen nesneden kopyalanarak kurulmuş yeni bir nesneyle çalışır.
  • parça — ifade, adı verilen nesnenin içindeki bir alt nesneyle çalışır.
  • geçici — ifade, adsız ve tam ifadenin sonunda yok olan yeni bir nesneyle çalışır.

Bu dört sınıf tahmin değil, koşum sonucudur: aşağıdaki çekirdek her ifadeyi adres, kopyalayan yapıcı kaynağı ve nesne ömrü üzerinden izler ve sınıfı kendisi belirler.

Çekirdeğin Tam Koşumu

Çekirdek üç dosyadır. iz.hpp ve iz.cpp izleme altyapısını taşır: her nesne kuruluşta bir sıra numarası alır, kopyalanırsa kaynağının numarasını saklar; dokun ifadenin gerçekten çalıştığı nesneyi kaydeder, bitir beyan edilen sınıfla ölçüleni karşılaştırıp satırı basar. hangisi.cpp bu kursun on dört dersinin tümüne ait on dört ölçümü tek main’de koşturur.

// iz.hpp — bir ifadenin hangi nesneyle calistigini olcen iz
#ifndef IZ_HPP
#define IZ_HPP

#include <cstddef>
#include <typeinfo>

enum class Sinif { kendisi, kopya, parca, gecici };

// Izlenen her nesne bu tabandan turer: kurulusta bir sira numarasi alir,
// kopyalanirsa kaynaginin numarasini saklar.
class Iz {
public:
    Iz();
    Iz(const Iz& kaynak);
    Iz& operator=(const Iz& kaynak);
    ~Iz();
    int no() const { return no_; }
    int kaynak() const { return kaynak_; }

private:
    int no_;
    int kaynak_;
};

void baslat_kayit(const void* adres, std::size_t boy, const std::type_info& tip, int no);
void dokun_kayit(const void* adres, std::size_t boy, const std::type_info& tip,
                 const Iz* iz, int kaynak);

// Adi verilen nesneyi kaydeder: olcum bu nesneye gore yapilir.
template <class T>
void baslat(const T& butun) {
    baslat_kayit(&butun, sizeof butun, typeid(T), butun.no());
}

// Ifadenin gercekten calistigi nesneyi kaydeder ve siniflandirir.
template <class T>
void dokun(const T& nesne) {
    dokun_kayit(&nesne, sizeof nesne, typeid(T), &static_cast<const Iz&>(nesne),
                nesne.kaynak());
}

// Olcumu kapatir, beyan edilen sinifla olculeni karsilastirip satiri basar.
void bitir(const char* ifade, Sinif beyan);

// Sinif basina sayilari ve var olan / yeni nesne toplamini basar.
void ozet();

#endif
// iz.cpp — sayilar ve olcum durumu yalniz bu birimde tutulur
#include "iz.hpp"

#include <cstdio>
#include <functional>

namespace {

int olusan = 0;
const char* const adlar[] = { "kendisi", "kopya", "parca", "gecici" };
int sayilar[4] = {};
int var_olan = 0;
int yeni = 0;

const char* butun_adres = nullptr;
std::size_t butun_boy = 0;
const std::type_info* butun_tip = nullptr;
int butun_no = 0;

bool dokunuldu = false;
bool ayni = false;
bool icinde = false;
bool kopyasi = false;
bool bitti = false;
const Iz* izlenen = nullptr;

}  // namespace

Iz::Iz() : no_(++olusan), kaynak_(0) {}

Iz::Iz(const Iz& kaynak) : no_(++olusan), kaynak_(kaynak.no_) {}

Iz& Iz::operator=(const Iz&) {
    return *this;  // atama nesneyi degistirir ama yeni nesne kurmaz
}

Iz::~Iz() {
    if (this == izlenen) {
        bitti = true;
        izlenen = nullptr;
    }
}

void baslat_kayit(const void* adres, std::size_t boy, const std::type_info& tip, int no) {
    butun_adres = static_cast<const char*>(adres);
    butun_boy = boy;
    butun_tip = &tip;
    butun_no = no;
    dokunuldu = ayni = icinde = kopyasi = bitti = false;
    izlenen = nullptr;
}

void dokun_kayit(const void* adres, std::size_t boy, const std::type_info& tip,
                 const Iz* iz, int kaynak) {
    const char* bas = static_cast<const char*>(adres);
    std::less<const char*> once;
    dokunuldu = true;
    ayni = bas == butun_adres && boy == butun_boy && tip == *butun_tip;
    icinde = !once(bas, butun_adres) && !once(butun_adres + butun_boy, bas + boy);
    kopyasi = kaynak == butun_no;
    bitti = false;
    izlenen = iz;
}

void bitir(const char* ifade, Sinif beyan) {
    int olculen = -1;
    if (dokunuldu) {
        if (ayni) {
            olculen = static_cast<int>(Sinif::kendisi);
        } else if (icinde) {
            olculen = static_cast<int>(Sinif::parca);
        } else if (kopyasi) {
            olculen = static_cast<int>(Sinif::kopya);
        } else if (bitti) {
            olculen = static_cast<int>(Sinif::gecici);
        }
    }
    int b = static_cast<int>(beyan);
    bool var = beyan == Sinif::kendisi || beyan == Sinif::parca;
    sayilar[b]++;
    if (var) {
        var_olan++;
    } else {
        yeni++;
    }
    std::printf("%-36s %-5s %s%s\n", ifade, var ? "var" : "yeni", adlar[b],
                olculen == b ? "" : "  TUTARSIZ");
    izlenen = nullptr;
}

void ozet() {
    std::printf("siniflar: kendisi %d, kopya %d, parca %d, gecici %d\n",
                sayilar[0], sayilar[1], sayilar[2], sayilar[3]);
    std::printf("var olan %d, yeni %d\n", var_olan, yeni);
}
// hangisi.cpp — on dort ifade, her biri icin tek soru: hangi nesne
#include <algorithm>

#include "iz.hpp"

/* 1. C ve C++ farklari: degerle aktarilan yapi */
struct Nokta : Iz {
    int x = 0;
    int y = 0;
};
static void kaydir(Nokta p) {
    p.x += 1;
    dokun(p);
}
static void c_farki() {
    Nokta n;
    baslat(n);
    kaydir(n);
    bitir("degerle aktarilan yapi", Sinif::kopya);
}

/* 2. Erisim denetimi: uye islevin calistigi nesne */
class Hesap : public Iz {
public:
    void yatir(int miktar) {
        bakiye_ += miktar;
        dokun(*this);
    }
    int bakiye() const { return bakiye_; }

private:
    int bakiye_ = 0;
};
static void erisim() {
    Hesap h;
    baslat(h);
    h.yatir(50);
    bitir("uye islevin this'i", Sinif::kendisi);
}

/* 3. Yapicilar: donusturen yapicinin kurdugu nesne */
class Tutar : public Iz {
public:
    Tutar(int deger) : deger_(deger) {}  // explicit degil: int'ten donusturur
    int deger() const { return deger_; }

private:
    int deger_;
};
static int goster(const Tutar& t) {
    dokun(t);
    return t.deger();
}
static void yapici() {
    Tutar t(3);
    baslat(t);
    goster(5);
    bitir("int'ten kurulan arguman", Sinif::gecici);
}

/* 4. RAII: koruyucunun biraktigi uye */
class Kaynak : public Iz {
public:
    void edin() { tutuluyor_ = true; }
    void birak() {
        tutuluyor_ = false;
        dokun(*this);
    }

private:
    bool tutuluyor_ = false;
};
class Koruyucu {
public:
    explicit Koruyucu(Kaynak& k) : k_(k) { k_.edin(); }
    ~Koruyucu() { k_.birak(); }
    Koruyucu(const Koruyucu&) = delete;
    Koruyucu& operator=(const Koruyucu&) = delete;

private:
    Kaynak& k_;
};
class Sayac : public Iz {
public:
    void artir() {
        Koruyucu g(kaynak_);
        deger_++;
    }

private:
    Kaynak kaynak_;
    int deger_ = 0;
};
static void raii() {
    Sayac s;
    baslat(s);
    s.artir();
    bitir("koruyucunun biraktigi uye", Sinif::parca);
}

/* 5. Kopyalama: derin kopyalanan tampon */
class Tampon : public Iz {
public:
    explicit Tampon(int boy) : boy_(boy), veri_(new int[boy]()) {}
    Tampon(const Tampon& k) : Iz(k), boy_(k.boy_), veri_(new int[k.boy_]) {
        std::copy(k.veri_, k.veri_ + k.boy_, veri_);
    }
    Tampon& operator=(const Tampon& k) {
        if (this != &k) {
            int* yeni = new int[k.boy_];
            std::copy(k.veri_, k.veri_ + k.boy_, yeni);
            delete[] veri_;
            veri_ = yeni;
            boy_ = k.boy_;
        }
        return *this;
    }
    ~Tampon() { delete[] veri_; }
    void yaz(int i, int deger) {
        veri_[i] = deger;
        dokun(*this);
    }
    int oku(int i) const { return veri_[i]; }

private:
    int boy_;
    int* veri_;
};
static void kopyalama() {
    Tampon a(3);
    baslat(a);
    Tampon b(a);
    b.yaz(0, 9);
    bitir("derin kopyalanan tampon", Sinif::kopya);
}

/* 6. Referanslar: referansla aktarilan nesne */
static void tasi(Nokta& p) {
    p.y += 1;
    dokun(p);
}
static void referans() {
    Nokta n;
    baslat(n);
    tasi(n);
    bitir("referansla aktarilan yapi", Sinif::kendisi);
}

/* 7. Islec asiri yukleme: a + b'nin sonucu */
class Para : public Iz {
public:
    explicit Para(int kurus) : kurus_(kurus) {}
    Para operator+(const Para& b) const { return Para(kurus_ + b.kurus_); }
    Para& operator+=(const Para& b) {
        kurus_ += b.kurus_;
        return *this;
    }
    int kurus() const { return kurus_; }

private:
    int kurus_;
};
static int yaz_para(const Para& p) {
    dokun(p);
    return p.kurus();
}
static void islec() {
    Para a(150);
    Para b(250);
    baslat(a);
    yaz_para(a + b);
    bitir("a + b sonucu", Sinif::gecici);
}

/* 8. Kalitim: taban sinifin islevi */
class Arac : public Iz {
public:
    void hizlan() {
        hiz_++;
        dokun(*this);
    }

private:
    int hiz_ = 0;
};
class Kamyon : public Arac {
public:
    int yuk() const { return yuk_; }

private:
    int yuk_ = 0;
};
static void kalitim() {
    Kamyon k;
    baslat(k);
    k.hizlan();
    bitir("taban sinifin islevi", Sinif::parca);
}

/* 9. Sanal fonksiyonlar: taban referansindan sanal cagri */
class Sekil : public Iz {
public:
    virtual ~Sekil() = default;
    virtual void ciz() const { dokun(*this); }
};
class Daire : public Sekil {
public:
    void ciz() const override { dokun(*this); }

private:
    int yaricap_ = 1;
};
static void sanal() {
    Daire d;
    baslat(d);
    const Sekil& s = d;
    s.ciz();
    bitir("taban referansindan sanal cagri", Sinif::kendisi);
}

/* 10. Soyut siniflar: arayuz uzerinden cagri */
class Kaydedici : public Iz {
public:
    virtual ~Kaydedici() = default;
    virtual void kaydet() = 0;
};
class SatirKaydedici final : public Kaydedici {
public:
    void kaydet() override {
        satir_++;
        dokun(*this);
    }

private:
    int satir_ = 0;
};
static void kullan(Kaydedici& k) {
    k.kaydet();
}
static void soyut() {
    SatirKaydedici k;
    baslat(k);
    kullan(k);
    bitir("arayuz uzerinden cagri", Sinif::kendisi);
}

/* 11. Coklu kalitim: sanal tabanin tek alt nesnesi */
class Cihaz : public Iz {
public:
    void ac() {
        acik_ = true;
        dokun(*this);
    }

private:
    bool acik_ = false;
};
class Yazici : public virtual Cihaz {
    int sayfa_ = 0;
};
class Tarayici : public virtual Cihaz {
    int cozunurluk_ = 0;
};
class Cokislevli : public Yazici, public Tarayici {};
static void elmas() {
    Cokislevli c;
    baslat(c);
    c.ac();
    bitir("sanal tabanin alt nesnesi", Sinif::parca);
}

/* 12. Istisnalar: degerle yakalanan istisna */
class Hata : public Iz {
public:
    explicit Hata(int kod) : kod_(kod) {}
    int kod() const { return kod_; }

private:
    int kod_;
};
static void istisna() {
    const Hata* firlatilan = nullptr;
    try {
        try {
            throw Hata(7);
        } catch (const Hata& ic) {
            firlatilan = &ic;
            throw;
        }
    } catch (Hata e) {
        baslat(*firlatilan);
        dokun(e);
        bitir("degerle yakalanan istisna", Sinif::kopya);
    }
}

/* 13. Ad alanlari: using bildirimiyle anilan nesne */
namespace muhasebe {
class Defter : public Iz {
public:
    void isle() {
        kayit_++;
        dokun(*this);
    }

private:
    int kayit_ = 0;
};
Defter ana_defter;
}  // namespace muhasebe

static void ad_alani() {
    using muhasebe::ana_defter;
    baslat(muhasebe::ana_defter);
    ana_defter.isle();
    bitir("using bildirimiyle anilan nesne", Sinif::kendisi);
}

/* 14. Tip donusumleri: tabana donusturulen referans */
class Olcer : public Iz {
public:
    void oku() const { dokun(*this); }
};
class Termometre : public Olcer {
public:
    int derece() const { return derece_; }

private:
    int derece_ = 20;
};
static void donusum() {
    Termometre t;
    baslat(t);
    static_cast<const Olcer&>(t).oku();
    bitir("tabana donusturulen referans", Sinif::parca);
}

int main() {
    c_farki();
    erisim();
    yapici();
    raii();
    kopyalama();
    referans();
    islec();
    kalitim();
    sanal();
    soyut();
    elmas();
    istisna();
    ad_alani();
    donusum();
    ozet();
    return 0;
}
degerle aktarilan yapi               yeni  kopya
uye islevin this'i                   var   kendisi
int'ten kurulan arguman              yeni  gecici
koruyucunun biraktigi uye            var   parca
derin kopyalanan tampon              yeni  kopya
referansla aktarilan yapi            var   kendisi
a + b sonucu                         yeni  gecici
taban sinifin islevi                 var   parca
taban referansindan sanal cagri      var   kendisi
arayuz uzerinden cagri               var   kendisi
sanal tabanin alt nesnesi            var   parca
degerle yakalanan istisna            yeni  kopya
using bildirimiyle anilan nesne      var   kendisi
tabana donusturulen referans         var   parca
siniflar: kendisi 5, kopya 3, parca 4, gecici 2
var olan 9, yeni 5

SK1. On dört ifadenin beşi kendisiyle, üçü bir kopyayla, dördü bir parçayla, ikisi bir geçiciyle çalıştı; kursun dört sınıfının adı ve dağılımı buradan doğar. İlk satır bu dersin konusudur: kaydir(Nokta p) değerle aldığı p üzerinde çalışır, p n’den kopyalanarak kurulmuştur — kaydir(n) içindeki p.x += 1 çağıranın n‘sine ulaşmaz. C’de bir struct’ı değerle almak da aynı sonucu verir; C++ bu davranışı değiştirmedi, üzerine yeni bir seçenek ekledi.

Değerle Aktarılan Yapının Derinleştirilmesi

C++‘ın C’ye eklediği referans parametresi aynı çağrıyı farklı bir sınıfa taşır. Aşağıdaki ölçüm kaydir ile tasi’yi kendi main’inde yan yana koşturur; ikisi de n üzerinden çağrılır, ikisi de kaynakta (n) yazımıyla görünür, ama biri değerle biri referansla alır.

// deger-referans.cpp — ayni cagri yazimi, farkli parametre bildirimi
#include <cstdio>

struct Nokta {
    int x = 0;
};

static void kaydir(Nokta p, const Nokta* disaridaki) {
    p.x += 1;
    std::printf("kaydir: p disaridaki nesneyle ayni mi %s\n",
                &p == disaridaki ? "evet" : "hayir");
}

static void tasi(Nokta& p, const Nokta* disaridaki) {
    p.x += 1;
    std::printf("tasi: p disaridaki nesneyle ayni mi %s\n",
                &p == disaridaki ? "evet" : "hayir");
}

int main() {
    Nokta n;
    kaydir(n, &n);
    tasi(n, &n);
    return 0;
}
kaydir: p disaridaki nesneyle ayni mi hayir
tasi: p disaridaki nesneyle ayni mi evet

SK2. kaydir(n) içindeki p, çağırandaki n’den ayrı bir nesnedir — kopya sınıfı. tasi(n) içindeki p, çağırandaki n’nin kendisidir — kendisi sınıfı. Çağrı yazımı ikisinde de (n)’dir; farkı yaratan tek şey işlevin bildirimidir: parametre tipi Nokta mı Nokta& mı.

C++‘ın C’den Farkları

Bu ders yalnız değerle aktarımı ölçtü; C++‘ın C’ye eklediği öğelerin geri kalanı kursun sonraki derslerinde tek tek ölçülür. Kısaca sayılırsa: class ve erişim denetimi, referans tipi, işleç aşırı yükleme, new/delete, ad alanları, ayrı bir bool tipi ve C’den daha sıkı bir tip denetimi — örneğin void*‘tan başka bir işaretçi tipine örtük dönüşüm C’de serbesttir, C++’ta derleme hatasıdır.

Sınırlayıcı Ölçüm

Bu dersin ölçtüğü ifade kopya sınıfındadır, ama ölçümün kendisi bir sınırı da gösterir: çağrı noktası hangi sınıfla çalışılacağını söylemez. kaydir(n) ile tasi(n) kaynakta birebir aynı görünür — ikisi de bir tanımlayıcıyı parantez içinde n ile çağırır. Hangi nesneyle çalışıldığını yalnız işlevin bildirimindeki parametre tipi belirler; okuyan biri bu kararı çağrı satırından değil, imzadan çıkarmak zorundadır.

Özet

  • Kursun ölçü ekseni: bir ifade kendisi, kopya, parça ya da geçici sınıflarından biriyle çalışır; on dört ölçüm bu dağılımı 5/3/4/2 olarak sabitler.
  • Bu dersin ölçtüğü ifade kopya sınıfındadır: değerle aktarılan Nokta, çağıranın nesnesinden ayrı bir kopyayla çalışır.
  • Aynı (n) çağrı yazımı, parametre referans olduğunda kendisi sınıfına geçer; sınıfı belirleyen çağrı değil bildirimdir.
  • C++‘ın C’ye eklediği öğeler (sınıf, referans, aşırı yükleme, new/delete, ad alanı, bool, daha sıkı tip denetimi) kısaca sayıldı; her biri ilgili derste derinleşir.
  • Derleme uyarısızdır ve çıktı üç ayrı koşumda birebir aynıdır; ölçüm tahmine değil koşuma dayanır.

Sonraki Adım

Bu ders bir işlevin parametre tipine göre hangi nesneyle çalıştığını gösterdi; bir sonraki ders aynı soruyu bir üye işlev için sorar — h.yatir(50) çağrıldığında this’in hangi nesneyi gösterdiğini ve private erişiminin nesne değil sınıf düzeyinde çalıştığını ölçer.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat