Yusuf Bülbül

An Engineer

C Dilini Nesne Yönelimli Kullanmak

for english click

Mühendisliği bir çok farklı şekilde tanımlayabilirsiniz. Fakat ben tanımlayacak olsaydım; yaptığınız işi şematiğe dökebiliyor ve üzerinde parametrik olarak verimlilik ya da tasarım anlamında ayarlamalar yapabiliyorsanız, mühendislik yapıyorsunuzdur diyebilirdim.  Bir konuda usta bile olsanız mühendisliğini yapmak bu yüzden farklı bir durumdur.

Yazılım dünyasının bu günlere gelmesinin en önemli sebebi tabi ki nesne yönelimli yazılımın geliştirilmiş olmasıdır. Çünkü nesne yönelimli bir tasarım, kod üzerinde mühendislik yapabilmenizi kolaylaştırıyor. Bunun farkına vardığınızda uzman yazılımcı rütbesine yükseliyorsunuz.  Neden diye soracak olursanız, bu ayrımı yapabilmek için biri iyi diğeri kötü kod tasarımına sahip iki büyük projede yer almış olmanız gerekiyor. Basit işler yapan maksimum beş-on bin satır koddan bahsetmiyorum. Burada belki yüz binlerce satır koddan bahsediyorum.  Böyle büyüklükte bir projede, kendinizi kod yığını içerisinde boğulurken bulduğunuzda nesne yönelimli mantığın ve yazılım tasarım desenlerinin kıymetini anlıyorsunuz.

Bu gün gömülü sistemler ve çekirdek programlar halen öncelikli olarak C dilini kullanıyor.  Ayriyeten, unix sistemler C dilinde yazılmış olduğundan ve popüler bir çok dilin tabanını oluşturduğundan, C dilinin unutulması ve ya daha geri planda kalması gibi bir durum söz konusu değil şimdilik. Fakat C dili yapısal olarak nesne yönelimli olmadığından özellikle büyük projelerde, kod tasarımı açısından dezavantajlara sahip bir dil.  Bu yüzden C++ kullanabiliyorsak, C yerine C++ kullanmak oldukça mantıklı. Fakat çoğu gömülü platformun halen C++ desteği yok. Bu durumda  C dili nesne yönelimli olmasa da bu dili nesne yönelimli dillere benzeterek kulllanabiliriz. Bu da sizin kodu daha anlamlı bir şekilde şematize edebilmenize ve tasarlaya bilmeye yarıyor tabi ki. Bu yazımda C dilini de C++ gibi nesne yönelimli bir şekilde kullanabilmeyi ele alacağım.

Öncelikle C de basit bir nesne tanımlama ile başlayalım.  Örneğin C++ da şöyle bir nesne olsun;

person.h dosyası;

class Person 
{

Public:

    Person();
    ~Person();
    
    std::string name;
    virtual void talk(int Opinion);
    

Private:
    
    const int own_opinion = 1;
    void think(int opinion);

};

person.cpp dosyası;

#include "person.h"

//private function
void Person::think(int Opinion)
{
  if(own_opinion != Opinion)
    std::cout << "person " << name << "disagree" << std::endl;
  else
    std::cout << "person " << name << "agree" << std::endl;

}
    

//public function
void Person::talk(int Opinion)
{
   think(Opinion);
   std::cout << "person " << name << "talking" << endl;
}


Nesenin kullanımı;

#include "person.h"

int main()
{   
    Person person1;
    Person person2;    

    person1.name = "Alice";
    person2.name = "Bob";

    person1.talk(1);
    person2.talk(2);

    return 0;

}

Şimdi C++ ve nesne yönelimli programlama bildiğinizi düşünerek bu nesneyi bir de C dilinde oluşturmaya çalışalım.

person.h dosyası şu şekilde olacak;

#define __person_H
#ifdef __person_H

struct Person
{
    const char *name;   //public variable
    void (*talk)(void *Person, int Opinion); //public function
};

static void think(struct Person *Person, int Opinion); //private
static void talk(void *Person, int Opinion);  //public

// constructer and deconstructer
void initPerson(struct Person *Person);
void deinitPerson(struct Person *Person);

#endif

person.c dosyası şu şekilde olacak;

#include "person.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

//private variable defination
static const int own_opinion = 1;



//private function
static void think(struct Person *Person, int Opinion)
{
    if(own_opinion == Opinion)
       printf("Person %s agree\n", Person->name);
    else
       printf("Person %s disagree\n", Person->name);
}



//public function defination
static void talk(void *Person, int Opinion)
{
    struct Person *temp = (struct Person *) Person;
    think(Person, Opinion);
    printf("Person %s talking...\n", temp->name);
}



// constructer and deconstructer
void initPerson(struct Person *Person)
{
    Person->talk = talk;          //define where is the public function in object
}



void deinitPerson(struct Person *Person)  //Deconstructer
{
}

 

C dilinde yukarıdaki nesnenin kullanımı şu şekilde;

#include <stdio.h>
#include "person.h"

int main()
{
    struct Person person1;
    struct Person person2;

    initPerson(&person1);
    initPerson(&person2);

    person1.name = "Alice";
    person2.name = "Bob";

    person1.talk(&person1, 1);
    person2.talk(&person2, 2);


    deinitPerson(&person1);
    deinitPerson(&person2);

    return 0;
}

Çıktı;

Person Alice agree
Person Alice talking...
Person Bob disagree
Person Bob talking...

“Person” isimli nesnenin genel(public) fonksiyonlarını, yapının(struct) içerisinde  fonksiyon göstericisi(function pointer) olarak tanımlıyoruz. Bu fonksiyonlar, aynı zamanda “.h” ve “.c” uzantılı dosyalarda, statik olarak tanımlanması gerekiyor. Çünkü C dilinde statik olarak tanımlanan fonksiyon ve değişkenlere dosya dışından erişilemiyor. Genel fonksiyonlara tek erişimin nesne işaretleticisi(Object Pointer) üzerinden olması gerekiyor.  Bu yüzden bunları yapının(struct) içerisine koyuyoruz. Fakat özel(private) fonksiyon ve değişkenlere nesne üzerinden de ulaşılamaması gerekiyor.  Bunlar sadece o nesnenin bulunduğu dosyadan ya da nesne içinden erişile bilmeli. Bu yüzden bu fonksiyon ve değişkenler yapının(struct) içerisine konmayıp sadece “.c” uzantılı dosyada statik olarak tanımlanıyor.  Statik fonksiyonları özellikle C de oldukça fazla görürüz.  Çünkü, özel(private) fonksiyon ve değişkenler için “.c” dosyasındaki statik tanımlaması biçilmiş kaftandır.  Bu yüzden nesne içerisinde tanımladığımız her şeyi, statik olarak tanımlıyoruz. Bu tanımlamanın dışında kalan tek fonksiyonlar nesnenin yapıcı ve yıkıcı fonksiyonları olarak kalıyor.

Burada iki seçenek mevcut.  Nesne yapıcı fonksiyona geçirilmeden önce, heap de ve ya stack de tanımlanabilir. Ben Stack’ de tanımladım. Daha sonra, nesnenin yapıcı fonksiyonunda, genel(public) fonksiyonların nerede olduğunu belirtmek gerekiyor. Aynı zamanda bütün üye fonksiyonların(member function) ait olduğu nesnenin işaretleyicisi(pointer) parametre olarak alması gerekiyor. Çünkü bu fonksiyon içerisinden nesnenin diğer üye fonksiyon ve değişkenlerine ulaşılabilmesi gerekiyor.

Nesneyi bu şekilde tanımladık. Peki bu yeterli mi? Tabi ki yeterli olabilmesi için kalıtım işlemi denilen ebeveyn ve çocuk nesneleri de tanımlayabilmek gerekiyor.  Bunu da C de kendimiz yapabiliriz. Örneğin yukarıdaki “person” fonksiyonunu ebeveyn nesne olarak alan bir “student” isimli nesne oluşturalım.

Bu işlem C++ kullanırken oldukça kolay.  C++’da şu şekilde tanımlanabilir;

class Student : public Person
{

private:
    void cheat(int Lesson)
    {
        std::cout << "Student "<< name << "cheating on lesson" << Lesson << std::endl;
    }
    std::string favorite_lesson;

public:
    Student();
    ~Student();

    std::string degree;
    void study(int Lesson)
    {
        if(Lesson != favorite_lesson)
            cheat(Lesson);
        else
            std::cout << "Student "<< name << "studying on lesson" << Lesson << std::endl;

    }

    void talk(int Opinion)
    {
        std::cout << name << "talking at " << degree << std::endl;

    }

};

 

Şimdi bu programı C de yazalım.

student.h dosyası şu şekilde olmalı;

#define __student_H
#ifdef __student_H

#include "person.h"

struct Student
{
    struct Person person;

    const char *degree;
    void (*study)(struct Student *Student, int Lesson);
};


static void talk(void *Student, int Opinion);
static void study(struct Student *Student, int Lesson);
static void cheat(struct Student *Student, int Lesson);

void initStudent(struct Student *Student);
void deinitStudent(struct Student *Student);



#endif

student.c dosyası şöyle olmalı;

#include "student.h"
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

const int fovarite_lesson = 1;

static void talk(void *Student, int Opinion)
{
    struct Student *student = (struct Student *)Student;
    printf("%s talking at %s", student->person.name, student->degree);
}



static void cheat(struct Student *Student, int Lesson)
{
    printf("Student %s cheating on lesson %d...\n", Student->person.name, Lesson);
}



static void study(struct Student *Student, int Lesson)
{
    if(Lesson != fovarite_lesson)
        cheat(Student, Lesson);
    else
        printf("Student %s studying on lesson %d...\n", Student->person.name, Lesson);


}



void initStudent(struct Student *Student)
{
    initPerson(&Student->person);
    Student->study = study;
    Student->person.talk = talk;
}


void deinitStudent(struct Student *Student)
{
    deinitPerson(&Student->person);
}

çıktı;

Student Alice cheating on lesson 3...
Alice talking at High School

Böylelikle  C dilinde de yazılım tasarım desenleri uygulanabiliyor.  Bu da kaliteli kod ve dolayısıyla kaliteli mühendislik getiriyor. Yukarıdaki kodlar ve tasarımlar kendimin yazmış olduğu ve bir çok yerden farklı tanımlamalar görüp bunları harmanlayarak oluşturduğum C de nesne tasarımları. Bu kodlar “gcc (Debian 7.2.0-19) 7.2.0” kullanılarak derlenip “Kali GNU/Linux Rolling 64-bit” sistemde test edilmiştir.

 

 

 

 

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Copyright © Tüm Hakları Saklıdır.