Linux Yazılım Notları

D-Göstericisi

Oldukça basit olan bu yöntemde, temel olarak, sınıfın içsel değişkenleri başka bir alanda tutulmakta ve sınıf içerisinde bu alana bir gösterici tutulmaktadır. Kendisi de dışarıdan erişime kapalı olan bu gösterici genellikle d-pointer olarak isimlendirilmektedir.

Not: d-pointer ismi, bu yöntemi Qt kütüphanelerinde ilk olarak kullanan, Arnt Gulbrandsen'den gelmektedir.

Bu kullanım, ayrıca Pimpl (Pointer to implementation) idiom, opaque pointer, compiler firewall ve Cheshire Cat gibi isimlerle de anılmaktadır.

d-göstericisine, yalnız kütüphane içerisinden erişilebilmekte ve sınıfın içsel alanının boyutları değişse bile uygulama bu değişiklikten etkilenmemektedir. Bir göstericinin, gösterdiği alandan bağımsız olarak daima sabit genişlikte (mimariye göre 4 veya 8 byte uzunluğunda) olduğunu hatırlayınız. İçsel alan üzerindeki tüm işlemler bu gösterici üzerinden yapılmaktadır.

Daha önce hata aldığımız (Segmentation fault) örnek için bu kez bu yöntemi kullanalım. İlk durumda kütüphane dosyalarımız aşağıdaki gibi olacaktır.

test.h:

class ClassPrivate;

class Class {
public:
    Class();
    ~Class();
    int foo();
private:
    ClassPrivate *d;
};

test.cpp:

#include "test.h"

class ClassPrivate {
public:
    int _foo;
    void init();
};

void ClassPrivate::init() {
    _foo = 111;
}

Class::Class() {
    d = new ClassPrivate;
    d->init();
}

Class::~Class() {
    delete d;
}

int Class::foo() {
    return d->_foo;
}

Örneğimizde, Class sınıfının içsel alanını ClassPrivate isimli başka bir sınıf üzerinden yöneteceğiz. Bu sayede, kütüphane kullanıcısının dışarıdan erişemeyeceği init fonksiyonunu da bu içsel sınıfa taşıyabiliriz. Elbette, sınıfın sonlandırıcı fonksiyonunda (destructor), içsel sınıf için edindiğimiz alanı geri vermeliyiz.

Bu sınıfın bildiriminin başlık dosyasında değil kaynak dosyada yapıldığına dikkat ediniz. Bu yüzden ClassPrivate türüne yalnız Class sınıfından erişilebilmektedir. Bir diğer yaklaşım ise ClassPrivate sınıfını ayrı bir başlık dosyasında yazmak olabilirdi. Kütüphane kullanıcısının görmediği bu tür private başlık dosyaları genel olarak _p.h ile bitecek şekilde isimlendirilmektedir. Örneğimiz için bu dosya test_p.h şeklinde olacaktır. Başka bir yaklaşım ise ClassPrivate türünü Class sınıfının içsel bir türü olarak yazmak olabilirdi fakat bu kullanım biçimi pek yaygın değildir.

Başlık dosyasında, derleyiciyi bilgilendirme amaçlı olarak, ClassPrivate türüyle ilgili forward declaration yapıldığına dikkat ediniz. C ve C++ dillerinde, henüz tanımı bilinmeyen türlere ait göstericiler tutulabildiğini hatırlayınız. Elbette bu aşamada, göstericinin gösterdiği adres üzerinde işlem yapılmaya çalışılması derleme hatasına yol açacaktır.

Bu aşamada, daha önce yaptığımız gibi, kütüphanenin ilk versiyonunu çıkaralım ve uygulamamızı çalıştıralım. Bu işlemleri daha önce de yaptığımız için burada tekrarlamıyoruz. Uygulamayı çalıştırdığımızda beklediğimiz sonucu aldığımızı görmekteyiz.

$ LD_LIBRARY_PATH=. ./app
111

Şimdi daha önce yaptığımız gibi kütüphaneye 100 elamanlı bir int dizi ekleyelim. Bu durumda kütüphanenin başlık dosyası aynı kalacaktır.

test.cpp:

#include "test.h"

class ClassPrivate {
public:
    int _foo;
    int _bar[100];
    void init();
};

void ClassPrivate::init() {
    _foo = 111;
    for (int i = 0; i < 100; ++i) {
        _bar[i] = 0;
    }
}

Class::Class() {
    d = new ClassPrivate;
    d->init();
}

Class::~Class() {
    delete d;
}

int Class::foo() {
    return d->_foo;
}

Kütüphaneyi yeniden derleyip çalıştırarak bir hata almadığınızı ve aynı sonuca ulaştığınızı doğrulayınız.