Nesne Tabanlı C

Açık Kaynak, Programlama, tutorial, Uzun Kod İçerikli, ytulinux

Eveeet kim demiş cde nesne tabanlı programlama yapılamaz diye?
Öncelikle nelere ihtiyacımız var onları görelim…

– Struct yapılarını kullanabilme yeteneği
– Bellek allocate etme becerisi (İşaretçiler hakkındaki bilgi de buna dahil sanırım)
– Nesne tabanlı programlama hakkında biraz fikir
– Beş yıllık excel 2025 tecrübesi 😛

Hedef: C programlama diline nesne tabanlı programlama yapma becerisi kazandırmak

Öncelikle yapacağımız programı açıklayalım. Şu bizim genel, öğrenci diye nesne yarat, sonra bunu sınıf diye bir nesneye ekle geyiğini yapacağız. Bunu yaparken nesne kullanacağız. Peki c de nesne nasıl yaratılır?

Öncelikle, nesnelerin aslında bizim c programlama dilimizdeki structların fonksiyon eklenmiş hali olduğunu (kabaca) düşünürsek,

1- Structlarda tuttuğumuz verileri değiştirmeyle ilgili fonksiyonlara ihtiyaç duyacağımızı,
2- Structları yaratmak için constructor işlevini gören fonksiyonlar yazmamız gerektiğini
3- Structları yoketmek için yine birer fonksiyon yazmamız gerektiğini
4- Hiyerarşide üstte yer alan fonksiyonlarda kullandığımız nesnelerin alt nesnelerine direk değil, aradaki nesne vasıtasıyla iş yaptırmamız gerektiğini

Düşünerek işe başlayabiliriz.

Elimizdeki örnekte iki adet nesnemiz olacak. “ogrenci” ve “sinif” nesneleri…
ogrenci nesnesine ait bir bilgiVer fonksiyonumuz olacak. Bu fonksiyon ekrana öğrenciye ait bilgileri yazdıracak. (aslında bu verileri ana fonksiyona döndürmek daha güzel olurdu, ama olayı fazla karmaşıklaştırmak istemiyorum, örnekte anlaşılabilirliğin yüksek tutulması amacıyla herşey olabildiğince basit tutulmuştur)
sinif nesnemizde ise, ogrEkle ve ogrAdaGoreBul fonksiyonlarımız olacak. Bu fonksiyonlardan ilki sinif nesnemize öğrenci eklerken, ikincisi ada göre öğrencinin listede kaçıncı sırada bulunduğunu bulacak. Aslında numaraya göre bulmak daha mantıklı olurdu gerçi, ama numarada da tekil olma şartıyla ilgili bir kontrol yapmadığımız için pek de bir fark olmayacak.
Bu şekilde ilk nesnemizi, yani öğrenci nesnesini yaratıyoruz.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ADN 16
#define SOYADN 16
#define NUMARAN 10
typedef struct ogrenci_t
{
char ad[ADN];
char soyad[SOYADN];
char numara[NUMARAN];
void (*yoket)(struct ogrenci_t *);
void (*bilgiVer)(struct ogrenci_t *);
} ogrenci;

void ogrYoket(ogrenci * ogr)
{
free(ogr);
}
void bilgiVer(ogrenci * ogr)
{
printf(“Adi:%s\nSoyadi:%s\nNumarasi:%s\n”,ogr->ad,ogr->soyad,ogr->numara);
}

ogrenci * ogrOlustur(char * ad, char * soyad, char * numara)
{
ogrenci * ogr = (ogrenci *) malloc (sizeof(ogrenci));
strncpy(ogr->ad,ad,ADN-1);
strncpy(ogr->soyad,soyad,SOYADN-1);
strncpy(ogr->numara,numara,NUMARAN-1);
ogr->yoket = ogrYoket;
ogr->bilgiVer = bilgiVer;
return ogr;
}

ogrenci.c dosyamız bundan ibaret. Görüldüğü üzere, structtan çağırılan fonksiyon süsü verebilmek için structa bir takım fonksiyon işaretçileri atamış durumdayız. Constructor (ya aslında bu kelimeleri kullanmayı sevmiyorum ama nesne tabanlı programlama ile ilgili türkçe-ingilizce kaynaklarda bu şekilde geçiyor) yani yapılandırıcı fonksiyonumuzda nesneyi yaratmamızı sağlayan bir takım kodlar var. İlk olarak structımız yani yapımıza malloc fonksiyonu ile yer tahsis ediliyor. Sonra, yapıya gerekli parametrelerden gelen veriler yerleştirilip, fonksiyon işaretçilerine fonksiyonların adresleri atanıyor ki sonradan sınıf işaretçisi ile bunlara erişelim. Aslında buna gerek yok, direk fonksiyon da kullanılabilir. Ama daha fazla nesne tabanlı süsü verebilmek için bunu kullanmayı uygun buldum :) Farkedileceği üzere, bütün fonksiyonların ilk parametresi yapının adresi. Bunun nedeni direk fonksiyonu yazıp, hangi yapıya ait olduğunu içinde bulamayacak olmamız, fonksiyonun hangi yapıdan çağırıldığını bulabilecek bir yöntem olduğunu da sanmıyorum açıkcası :)

Yoket fonksiyonumuz, malloc ile almış olduğumuz alanı, nesneyle işimiz kalmadığımız zaman onu kullanılmış bir mendil gibi umarsızca kenara atıp, onu kaderiyle başbaşa bırakmak yerine hannibal filmindeki gibi domuzlara verip tamamen ondan kurtulmamıza yarıyor (evet sanırım dikkatleri tekrar televizyondan yazıya çevirmeyi başardım)

uhhhh… Neyse ne diyordum? Yapılandırıcı fonksiyonumuzun geri dönüş değeri farkettiyseniz yapımızın kendi türünde. Yani malloc ile aldığımız alanın adresini geri döndürüyoruz. Bu, nesneyi yaratırken işimize yarayacak.

bilgiVer fonksiyonuna bakalım. Oluşturduğumuz yapıyı kullanma şansını ilk burada buluyoruz. Fazla açıklanacak birşey yok, pozisyonu dondurmadan anlatmaya devam ediyorum.

İkinci bir nesne daha yaratıyoruz ki bu nesnenin adı sinif.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include “ogrenci.c”
typedef struct sinif_t
{
ogrenci ** liste;
int ogrSay;
void (*yoket)(struct sinif_t *);
void (*ogrEkle)(struct sinif_t *, ogrenci *);
int (*adaGoreBul)(struct sinif_t *, char *);
} sinif;

void sinifYoket(sinif * snf)
{
free(snf->liste);
free(snf);
}

void sinifOgrEkle(sinif * snf, ogrenci * ogr)
{
snf->liste[snf->ogrSay] = ogr;
snf->ogrSay++;
snf->liste = (ogrenci **) realloc (snf->liste,sizeof(ogrenci *)*(snf->ogrSay+1));
}

int sinifAdaGoreBul(sinif * snf, char * ad)
{
int i;
for (i=0;i<snf->ogrSay && strncmp(ad,snf->liste[i]->ad,ADN);i++)
{
}
if (i==snf->ogrSay)
{return 0;}
return i;
}

sinif * sinifOlustur()
{
sinif * snf = (sinif *) malloc (sizeof(sinif));
snf->liste = (ogrenci **) malloc (sizeof(ogrenci *));
snf->yoket = sinifYoket;
snf->ogrEkle = sinifOgrEkle;
snf->adaGoreBul = sinifAdaGoreBul;
snf->ogrSay = 0;
return snf;
}

Burada da hemen hemen aynı şeyleri yapıyoruz (sinif.c). Aradaki fark, daha önce tanımladığımız ogrenci nesnesini bu nesnenin altında kullanmış olmamız. Göreceğiniz gibi öğrenci ekliyoruz, her ekleyişte sayıyı arttırıyoruz, işaretçi dizisini reallocate ediyoruz.

Veeee gelelim main fonksiyonumuza…

#include <stdio.h>
#include “sinif.c”

int main ()
{
int i;
sinif * snf = sinifOlustur();
snf->ogrEkle(snf,ogrOlustur(“Huseyin Oguz”,”ALBAYRAK”,”04011056″));
snf->ogrEkle(snf,ogrOlustur(“Hede”,”hodo”,”04011000″));
i = snf->adaGoreBul(snf,”Hede”);
snf->liste[i]->bilgiVer(snf->liste[i]);
snf->yoket(snf);
return 0;
}

Ne? main çok mu kısa oldu? Evet zaten amacımız da buydu sanırım :)
Açıkcası bu yöntemi görüntü işleme dersinde bir ödevde kullandım ve pek de hoşuma gitti. Kod gayet temiz görünüyor. Tabi ciddi projede kullanılacaksa bence fonksiyon işaretçileriyle uğraşılmamalı, boşuna kalabalık yapıyorlar çünkü. Fonksiyonların başında nesneye ait bir kısaltmanın bulunması daha mantıklı sanki.

Neyse, buradan küçüklerime sevgilerimi, büyüklerime saygılarımı sunuyor ve bu yazımı sonlandırıyorum. Hmm aslında sonlandırmadan bir eklemede bulunayım, biraz önce gdb de bir işaretçinin free fonksiyonundan sonra tuttuğu alanın gerçekten boşalıp boşalmadığını görmek için x struct_adi kullanılabileceğini gördüm. Belki işinize yarar :)

Bu yazı toplamda 2509, bugün ise 2 kez görüntülenmiş

3 Comments
  • mustafa karaca

    “Tabi ciddi projede kullanılacaksa bence fonksiyon işaretçileriyle uğraşılmamalı, boşuna kalabalık yapıyorlar çünkü. Fonksiyonların başında nesneye ait bir kısaltmanın bulunması daha mantıklı sanki.”
    demişsin. Ben tam tersi olduğunu düşünüyorum çünkü function pointer lar aracılığı ile “function overloading” olayi yapilabiliyor. Örneğin ogrenci sinifindan miras yoluyla “sinif” bilgisini de içeren aşağidaki sinifi oluşturalim:

    typedef struct {
    ogrenci super;
    char sinif[10];
    } ogrenci2;

    void bilgiVer2(ogrenci2 * ogr)
    {
    printf(”Adi:%s\nSoyadi:%s\nNumarasi:%s Sinif:%s\n”,ogr->ad,ogr->soyad,ogr->numara, ogr->sinif);
    }

    ogrenci2 * ogrOlustur2(char * ad, char * soyad, char * numara, char *sinif)
    {
    ogrenci2 * ogr = (ogrenci2 *) malloc (sizeof(ogrenci2));
    strncpy(ogr->ad,ad,ADN-1);
    strncpy(ogr->soyad,soyad,SOYADN-1);
    strncpy(ogr->numara,numara,NUMARAN-1);
    strncpy(ogr->sinif, sinif,9);
    ogr->yoket = ogrYoket;
    ogr->bilgiVer = bilgiVer2;
    return ogr;
    }

    Bu yeni “ogrenci2” sinifini kullanirken yine eski “ogrenci” sinifi gibi kullanabiliriz. Örn:

    #include
    #include “sinif.c”

    int main ()
    {
    int i;
    sinif * snf = sinifOlustur();
    snf->ogrEkle(snf,ogrOlustur(”Huseyin Oguz”,”ALBAYRAK”,”04011056″));
    snf->ogrEkle(snf,(ogrenci *)ogrOlustur2(”Hede”,”hodo”,”04011000″, “5-F”));
    i = snf->adaGoreBul(snf,”Hede”);
    snf->liste[i]->bilgiVer(snf->liste[i]);
    snf->yoket(snf);
    return 0;
    }

    Görüldüğü gibi ogrenci2 sinifini oluştururken farkli oluşturduk, ama kullanirken ayni şekilde kullandik.

  • mustafa karaca

    Az önceki “ogrOlustur2” ve “bilgiVer2” fonksiyonlarini düzeltiyorum:

    void bilgiVer2(ogrenci2 * ogr)
    {
    printf(”Adi:%s\nSoyadi:%s\nNumarasi:%s Sinif:%s\n”,ogr->super.ad,ogr->super.soyad,ogr->super.numara, ogr->sinif);
    }

    ogrenci2 * ogrOlustur2(char * ad, char * soyad, char * numara, char *sinif)
    {
    ogrenci2 * ogr = (ogrenci2 *) malloc (sizeof(ogrenci2));
    strncpy(ogr->super.ad,ad,ADN-1);
    strncpy(ogr->super.soyad,soyad,SOYADN-1);
    strncpy(ogr->super.numara,numara,NUMARAN-1);
    strncpy(ogr->sinif, sinif,9);
    ogr->super.yoket = ogrYoket;
    ogr->super.bilgiVer = bilgiVer2;
    return ogr;
    }

  • huseyinalb

    hmm haklısın :)
    neyse zaten bu olayı bana sen öğretmiştin ehuehue