---
title: 'Nesne Benzeri Tasarım'
source: 'https://academia.sh/tr/kurslar/c-sistem-programlama/nesne-benzeri-tasarim'
course: 'C ile Sistem Programlama'
language: tr
updated: '2026-09-26T03:02:44+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Opak bir yapı ve erişim işlevleriyle bilgi gizleyen bir nesne arayüzü kurar'
    - 'Yapı içi işlev işaretçileriyle metot çağrısı izlenimi veren bir arayüz gerçekler'
    - "İki tutamacın aynı nesneyi paylaştığını ve metot dispatch'inin bunu değiştirmediğini ölçer"
---

# Nesne Benzeri Tasarım

Opak bir yapı ve yapı içi işlev işaretçileriyle kurulan nesne benzeri arayüzlerde, iki tutamacın aynı nesneyi paylaştığının ve bir metot çağrısının da bu paylaşımı değiştirmediğinin ölçümü.

C'nin sınıfı yok, ama bir "nesne" izlenimi iki araçla kurulabiliyor: bir **opak yapı**
(iç alanları yalnız tanımlandığı `.c` dosyasında görünen bir tip) ve içine gömülü **işlev
işaretçileri** (bir "metot" çağrısı gibi okunan, aslında bir veri alanı olan çağrılabilir
alanlar). Bu ders ikisini de kuruyor ve aynı soruyu soruyor: bir "nesne"ye iki tutamaçtan
erişildiğinde, birinden yapılan bir değişiklik ötekinden görünüyor mu? Yanıt yine
**ortak** — opaklık ve metot çağrısı sözdizimi, altta yatan paylaşımı değiştirmiyor,
yalnız onu okuma biçimini değiştiriyor.

## Opak Yapı: Bilgi Gizleme ile Bir Nesne Arayüzü

`struct Nesne`'nin tanımı yalnız `nesne.c` içinde; `nesne.h`'de yalnız bir **ileri
bildirim** (`struct Nesne;`) var. Bu, başlığı dahil eden bir dosyanın `deger` alanına
doğrudan erişemeyeceği, yalnız `nesne_ayarla`/`nesne_oku` işlevleri üzerinden
konuşabileceği anlamına geliyor — nesnenin "genel arayüzü" bu iki işlev.

```c
// nesne.h — opak nesne tipi ve genel arayuzu
#ifndef NESNE_H
#define NESNE_H

struct Nesne;

struct Nesne *nesne_olustur(int baslangic);
void nesne_ayarla(struct Nesne *n, int deger);
int nesne_oku(const struct Nesne *n);
void nesne_serbest(struct Nesne *n);

#endif
```

```c
// nesne.c — opak tipin gerceklemesi
#include <stdlib.h>
#include "nesne.h"

struct Nesne {
    int deger;
};

struct Nesne *nesne_olustur(int baslangic) {
    struct Nesne *n = malloc(sizeof *n);
    if (n == NULL) {
        return NULL;
    }
    n->deger = baslangic;
    return n;
}

void nesne_ayarla(struct Nesne *n, int deger) {
    n->deger = deger;
}

int nesne_oku(const struct Nesne *n) {
    return n->deger;
}

void nesne_serbest(struct Nesne *n) {
    free(n);
}
```

`nesne_serbest`'in ne zaman çağrılacağı ayrı bir soru — önceki kursun (**C'de Bellek ve
İşaretçiler**, K02) sorduğu "bu nesneyi kim bırakacak" sorusu, burada da `nesne_olustur`'u
çağıranın üstlendiği bir sözleşme. Bu ders o soruyu değil, iki tutamacın aynı nesneyi
paylaşıp paylaşmadığını ölçüyor.

## İki Tutamacın Aynı Nesneyi Paylaşması

`n` ve `ikinci_tutamac` aynı `struct Nesne *` değerini taşıyor — ikisi de aynı nesneye
işaret ediyor. Opak tip yalnız `deger` alanına **doğrudan** erişimi engelliyor;
`nesne_ayarla`/`nesne_oku` üzerinden erişim hâlâ aynı nesneye ulaşıyor.

```c
// iki_tutamac.c — ayni nesneye iki tutamactan erisim
#include <stdio.h>
#include "nesne.h"

int main(void) {
    struct Nesne *n = nesne_olustur(1);
    struct Nesne *ikinci_tutamac = n;

    printf("%s %d\n", "olusturuldugundaki deger", nesne_oku(n));

    nesne_ayarla(ikinci_tutamac, 42);
    printf("%s %d\n", "ikinci tutamactan ayarlanan degerin n uzerinden okunmasi", nesne_oku(n));
    printf("%s %d\n", "n ile ikinci_tutamac ayni nesneyi mi gosteriyor", (int)(n == ikinci_tutamac));

    nesne_serbest(n);
    return 0;
}
```

```
olusturuldugundaki deger 1
ikinci tutamactan ayarlanan degerin n uzerinden okunmasi 42
n ile ikinci_tutamac ayni nesneyi mi gosteriyor 1
```

**VY10.** `ikinci_tutamac = n` ataması bir nesne kopyalamıyor, bir **işaretçiyi**
kopyalıyor — sonuçta ikisi de aynı `malloc` bloğunun adresini taşıyor, tıpkı bağlı
listedeki `ikinci = bas->sonraki` atamasında olduğu gibi (Bağlı Yapılar dersi).
`nesne_ayarla(ikinci_tutamac, 42)` çağrısı `deger` alanını değiştiriyor ve bu değişiklik
`nesne_oku(n)` ile okunuyor: **42**. Opak tip burada hiçbir şeyi değiştirmiyor — dışarıdan
`n->deger` yazılamaz olması, `n` ile `ikinci_tutamac`'ın aynı nesneyi paylaştığı gerçeğini
gizlemiyor, yalnız o paylaşıma nasıl erişileceğini (yalnız işlevler üzerinden) sınırlıyor.
Son satır bunu adres basmadan doğruluyor: `n == ikinci_tutamac` karşılaştırması **1**.
Sınıf **ortak**.

## İşlev İşaretçileriyle Metot Çağrısı İzlenimi

İkinci yaklaşım bilgiyi gizlemiyor, "metotları" doğrudan yapının içine gömüyor: `artir` ve
`deger_oku` alanları birer işlev işaretçisi ve `sayac_nesnesi_hazirla` onları somut
işlevlere bağlıyor. Çağıran taraf `s.artir(&s, 5)` yazdığında bu, C++'taki `s.artir(5)`
çağrısına benzeyen bir sözdizimi üretiyor — ama `artir` bir metot değil, bir veri alanı.

```c
// sayac_nesne.h — yapi ici islev gostergeleriyle metot cagrisi izlenimi
#ifndef SAYAC_NESNE_H
#define SAYAC_NESNE_H

struct SayacNesnesi {
    int adet;
    void (*artir)(struct SayacNesnesi *, int);
    int (*deger_oku)(const struct SayacNesnesi *);
};

void sayac_nesnesi_hazirla(struct SayacNesnesi *s, int baslangic);

#endif
```

```c
// sayac_nesne.c — islev gostergelerinin baglanmasi
#include "sayac_nesne.h"

static void sayac_artir(struct SayacNesnesi *s, int miktar) {
    s->adet = s->adet + miktar;
}

static int sayac_deger_oku(const struct SayacNesnesi *s) {
    return s->adet;
}

void sayac_nesnesi_hazirla(struct SayacNesnesi *s, int baslangic) {
    s->adet = baslangic;
    s->artir = sayac_artir;
    s->deger_oku = sayac_deger_oku;
}
```

```c
// yontem_cagrisi.c — iki tutamacin ayni yapi ici islevleri cagirmasi
#include <stdio.h>
#include "sayac_nesne.h"

int main(void) {
    struct SayacNesnesi s;
    sayac_nesnesi_hazirla(&s, 0);

    struct SayacNesnesi *birinci_tutamac = &s;
    struct SayacNesnesi *ikinci_tutamac = &s;

    birinci_tutamac->artir(birinci_tutamac, 5);
    printf("%s %d\n", "birinci tutamacla artirdiktan sonra ikinciden okunan",
           ikinci_tutamac->deger_oku(ikinci_tutamac));

    ikinci_tutamac->artir(ikinci_tutamac, 3);
    printf("%s %d\n", "iki tutamacla art arda artis sonrasi deger", s.deger_oku(&s));

    return 0;
}
```

```
birinci tutamacla artirdiktan sonra ikinciden okunan 5
iki tutamacla art arda artis sonrasi deger 8
```

**VY11.** `birinci_tutamac` ve `ikinci_tutamac` ikisi de `&s`'i taşıyor — aynı yerel
değişkenin adresi, tıpkı bir önceki ölçümdeki `n`/`ikinci_tutamac` çiftinde olduğu gibi.
`birinci_tutamac->artir(birinci_tutamac, 5)` çağrısı `s.adet`'i 0'dan 5'e çıkarıyor ve
`ikinci_tutamac->deger_oku(ikinci_tutamac)` bunu görüyor: **5**. `s.artir` ile
`s.deger_oku` birer işlev işaretçisi olduğu için `birinci_tutamac->artir` ile
`ikinci_tutamac->artir` de aynı işaretçiyi okuyor — çağrılan işlev ikisinde de
`sayac_artir`. Metot çağrısı gibi görünen sözdizimi, `s.adet`'e erişimi hiç
değiştirmiyor; `artir` ve `deger_oku` sadece hangi işlevin çağrılacağını **veri olarak**
taşıyor. Sınıf yine **ortak**: iki tutamaç aynı yapıyı paylaşıyor, aradaki fark yalnız
erişim sözdiziminde.

## Paylaşılan Yöntem Tablosu

`SayacNesnesi`'nin her örneği kendi `artir`/`deger_oku` alanlarını taşıyor — aynı
işlevlere işaret etseler de, her örnek bu iki işaretçinin **kendi kopyasını** saklıyor.
Alternatif bir tasarım örnekleri tek bir **paylaşılan** yöntem tablosuna bağlıyor: her
örnek yalnız tabloya bir işaretçi taşıyor, tablonun kendisi tüm örnekler arasında tek.

```c
// sayac_vtable.h — paylasilan yontem tablosuyla nesne tasarimi
#ifndef SAYAC_VTABLE_H
#define SAYAC_VTABLE_H

struct SayacYonetici;

struct SayacYontemleri {
    void (*artir)(struct SayacYonetici *, int);
    int (*deger_oku)(const struct SayacYonetici *);
};

struct SayacYonetici {
    int adet;
    const struct SayacYontemleri *yontemler;
};

extern const struct SayacYontemleri SAYAC_YONTEMLERI;

void sayac_yonetici_hazirla(struct SayacYonetici *s, int baslangic);

#endif
```

```c
// sayac_vtable.c — tek paylasilan yontem tablosunun tanimi
#include "sayac_vtable.h"

static void sayac_artir_v(struct SayacYonetici *s, int miktar) {
    s->adet = s->adet + miktar;
}

static int sayac_deger_oku_v(const struct SayacYonetici *s) {
    return s->adet;
}

const struct SayacYontemleri SAYAC_YONTEMLERI = { sayac_artir_v, sayac_deger_oku_v };

void sayac_yonetici_hazirla(struct SayacYonetici *s, int baslangic) {
    s->adet = baslangic;
    s->yontemler = &SAYAC_YONTEMLERI;
}
```

```c
// paylasilan_tablo.c — iki ornegin adeti ayri, yontem tablosu ortak mi
#include <stdio.h>
#include "sayac_vtable.h"

int main(void) {
    struct SayacYonetici birinci;
    struct SayacYonetici ikinci;
    sayac_yonetici_hazirla(&birinci, 0);
    sayac_yonetici_hazirla(&ikinci, 100);

    birinci.yontemler->artir(&birinci, 5);
    ikinci.yontemler->artir(&ikinci, 5);

    printf("%s %d\n", "birinci ornegin adet degeri", birinci.yontemler->deger_oku(&birinci));
    printf("%s %d\n", "ikinci ornegin adet degeri", ikinci.yontemler->deger_oku(&ikinci));
    printf("%s %d\n", "iki ornegin yontem tablosu ayni adresi mi gosteriyor",
           (int)(birinci.yontemler == ikinci.yontemler));

    return 0;
}
```

```
birinci ornegin adet degeri 5
ikinci ornegin adet degeri 105
iki ornegin yontem tablosu ayni adresi mi gosteriyor 1
```

**VY17.** `birinci.adet` ile `ikinci.adet` birbirinden bağımsız — her örnek kendi `int`
alanını taşıyor, biri değişince öteki etkilenmiyor. Ama `birinci.yontemler` ile
`ikinci.yontemler` **aynı** adresi taşıyor: `&SAYAC_YONTEMLERI`, dosya kapsamında tek bir
kere tanımlanmış `const` bir nesne. `SayacNesnesi` tasarımında her örnek `artir` ve
`deger_oku` alanlarının **kendi kopyasını** tutuyordu (aynı işleve işaret etseler de, iki
ayrı bellek konumunda); burada örnekler tablonun **kendisini** değil, ona giden tek bir
işaretçiyi paylaşıyor. Bir yapı içinde iki farklı sınıf aynı anda görülebiliyor: `adet`
alanı için örnekler birbirinden **kopya** gibi bağımsız, `yontemler`'in gösterdiği tablo
için ise **ortak** — tablo tek bir nesne ve tüm örnekler ona aynı adresle bağlanıyor.

## Bir Kopyalama İşleviyle Bağımsız Bir Nesne Üretmek

Şimdiye kadarki iki örnek (tutamaç kopyalama, paylaşılan yöntem tablosu) hep **ortak**
sınıfını üretti: bir işaretçi kopyalandığında ya da paylaşıldığında, altta yatan yapı
tek kaldı. Ama `nesne.h`'nin genel arayüzü zaten bağımsız bir nesne üretmek için yeterli
— `nesne_oku` ile mevcut değeri okuyup `nesne_olustur` ile o değerden yeni bir nesne
kurmak, opak tipin iç alanına hiç dokunmadan bir **derin kopya** üretiyor.

```c
// nesne_klon.h — opak nesnenin genel arayuzuyle bagimsiz kopyasini kuran islev
#ifndef NESNE_KLON_H
#define NESNE_KLON_H

#include "nesne.h"

struct Nesne *nesne_klonla(const struct Nesne *kaynak);

#endif
```

```c
// nesne_klon.c — klonlama yalniz genel arayuzu kullaniyor, ic alana dokunmuyor
#include "nesne_klon.h"

struct Nesne *nesne_klonla(const struct Nesne *kaynak) {
    return nesne_olustur(nesne_oku(kaynak));
}
```

```c
// nesne_klon_demo.c — klon n'den bagimsiz, n degisince klon etkilenmiyor
#include <stdio.h>
#include "nesne_klon.h"

int main(void) {
    struct Nesne *n = nesne_olustur(7);
    struct Nesne *klon = nesne_klonla(n);

    printf("%s %d\n", "klonun baslangictaki degeri", nesne_oku(klon));

    nesne_ayarla(n, 99);
    printf("%s %d\n", "n degistirildikten sonra klonun degeri", nesne_oku(klon));
    printf("%s %d\n", "n ile klon ayni nesneyi mi gosteriyor", (int)(n == klon));

    nesne_serbest(n);
    nesne_serbest(klon);
    return 0;
}
```

```
klonun baslangictaki degeri 7
n degistirildikten sonra klonun degeri 7
n ile klon ayni nesneyi mi gosteriyor 0
```

**VY19.** `nesne_klonla`, `n`'nin işaret ettiği yapıya hiç dokunmuyor — yalnız
`nesne_oku(kaynak)` ile mevcut değeri okuyup `nesne_olustur` ile **yeni bir `malloc`
bloğu** kuruyor. Sonuçta `klon`, `n`'den ayrı bir adres taşıyor: son satır bunu
`n == klon` karşılaştırmasının **0** dönmesiyle doğruluyor. `nesne_ayarla(n, 99)`
çağrısı yalnız `n`'nin işaret ettiği bloğu değiştiriyor; `klon`'un işaret ettiği blok
etkilenmiyor, `nesne_oku(klon)` hâlâ **7** dönüyor. Bu, dersin ilk iki örneğinin tam
tersi: `ikinci_tutamac = n` bir işaretçiyi kopyalayıp aynı yapıyı paylaştırıyordu
(**ortak**), `nesne_klonla` ise genel arayüzü kullanarak **yeni bir yapı** kuruyor ve
sonucu **kopya** sınıfına taşıyor — iki nesne artık birbirinden bağımsız, biri
değişince öteki değişmiyor. Fark kodun biçiminde değil: ikisi de işaretçi atıyor,
ikisi de opak tipin arkasında çalışıyor; fark, sağ taraftaki ifadenin **var olan bir
adresi mi taşıdığı** yoksa **yeni bir ayırmadan mı geldiği**.

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

Bir "nesne" arayüzünün biçimi — opak bir tip mi, yapı içi işlev işaretçileri mi, sıradan
işlevler mi — altta yatan paylaşımı belirlemiyor. `nesne_ayarla(ikinci_tutamac, 42)` ile
`birinci_tutamac->artir(birinci_tutamac, 5)` farklı görünüyor (biri sıradan bir işlev
çağrısı, öteki yapı alanı üzerinden bir çağrı gibi okunuyor), ama ikisi de aynı ilkeye
dayanıyor: bir işaretçiyle erişilen bir yapıyı değiştirmek, o yapıya erişen her işaretçiden
görünür. Bir arayüzün "nesne benzeri" görünmesi — bilgi gizleme, metot çağrısı sözdizimi —
paylaşımı gizlemiyor ya da ortadan kaldırmıyor, yalnız paylaşılan yapıya nasıl
erişileceğini biçimlendiriyor. Paylaşımın kendisi hâlâ yalnız işaretçilerin eşitliğiyle
(`n == ikinci_tutamac`, `birinci_tutamac == ikinci_tutamac`) ölçülebiliyor.

## Özet

- Bir opak tip (`struct Nesne;` ileri bildirimi) iç alanlara doğrudan erişimi
  engelliyor, ama nesneyi işaret eden iki tutamaç arasındaki paylaşımı değiştirmiyor;
  yapı içine gömülü işlev işaretçileri de (`artir`, `deger_oku`) bir metot çağrısı
  sözdizimi veriyor ama birer veri alanı olarak kalıyor. Paylaşım yalnız işaretçi
  eşitliğiyle ölçülebiliyor.
- İki tutamaç aynı nesneyi/yapıyı gösterdiğinde, birinden yapılan bir değişiklik
  ötekinden (hangi arayüz biçimi kullanılırsa kullanılsın) görünüyor — sınıf **ortak**.
- Bir nesneyi **kim bırakacağı** (K02'nin sorusu) ile bir değişikliğin **kim
  tarafından görüleceği** (bu dersin sorusu) ayrı eksenler.
- Örneklerin kendi işlev işaretçilerini taşıması ile tek bir paylaşılan tabloya
  işaretçiyle bağlanması farklı sonuçlar veriyor: birincisinde her örnek kendi kopyasını
  tutuyor, ikincisinde tablo tek ve tüm örnekler ona aynı adresle erişiyor.
- Genel arayüzü (yalnız `nesne_olustur`/`nesne_oku`) kullanarak yeni bir nesne kurmak
  işaretçi kopyalamaktan farklı: sonuç **kopya** sınıfına giriyor, iki nesne birbirinden
  bağımsız kalıyor.

## Sonraki Adım

Bu dersin iki örneğinde de kaynak yönetimi basitti: tek bir `nesne_serbest` çağrısı ya da
hiç ayırma. Bir sonraki ders, Kaynak Temizleme Deyimleri, birden fazla kaynağın (birden
fazla ayırma, birden fazla açılan tutamaç) bir işlev içinde sırayla açılıp hata durumunda
doğru sırayla kapatılması gereken durumları ölçüyor — orada sınıf **gözlenmeyen**: bir
kaynak, hiçbir okuyucuya ulaşmadan, hata yolunda kapatılıp kayboluyor.
