C++ Şablonlar (Templates)

Şablonlar, generic(genel) türde çalışan özel yapılardır. Bu fonksiyonlar türden bağımsız olarak çalışabilmektedir (örnek: std::string, int, float vs.). Template kullanımı sayesinde kod içerisinde tekrar tekrar fonksiyonlar yazmamıza gerek kalmaz. Fonksiyonun aşırı yüklenmesi bu duruma örnek olarak verilebilir. C# ve Java dillerinde template yerine generic adında buna benzer bir kullanım mevcuttur.

Şablonlar, Fonksiyon Şablonları ve Sınıf Şablonları olmak üzere ikiye ayrılır.

Fonksiyon Şablonları

Fonksiyon şablonlarının kullanım şekli aşağıdaki gibidir. İlk olarak T isminde bir tanımlayıcı barındıran template tanımladık. T, fonksiyon içerisinde kullanılacak bilinmeyen veri türünü temsil etmektedir. Tanımlarken typename veya class olarak belirtebiliriz. Bunlar arasında bazı farklar bulunmakta ancak aşağıdaki örnekte her ikisi de kullanılabilir.
template <typename T>
T sum(T m, T n){
    return m + n;
}
Veya aşağıdaki gibi typename yerine class koyulabilir.
template <class T>
T sum(T m, T n){
    return m + n;
}
main fonksiyonu içerisinde sum fonksiyonu aşağıdaki gibi çalıştırılır.
int main(){
    std::cout << sum(1, 2) << std::endl;
    std::cout << sum(3.6f, 4.1f) << std::endl;
    return 0;
}
İlk sum fonksiyonu parametrelerini integer tipinde değer aldı. İkinci sum fonksiyonu ise parametre olarak float tipinde değer aldı. Çalıştırıldığında elde edilen sonuç sırasıyla 3 ve 7.7 değerleridir. Aynı fonksiyonu farklı veri tipine sahip değerlerde kullanılabilir olduğunu gördük. Bu fonksiyonları kullanılan parametrelerin veri türlerini belirterek kullanabiliriz. Ancak derleyici zaten parametredeki değerlerin veri türlerini otomatik olarak algıladığı için aşağıdaki gibi bir kullanım şart değil.
int main(){
    std::cout << sum<int>(1, 2) << std::endl;
    std::cout << sum<float>(3.6f, 4.1f) << std::endl;
    return 0;
}
Bazı durumlarda şablon fonksiyonu içerisinde farklı veri türünde değerler ile birlikte işlem yapmak gerekebilir. Örneğin, bir geometrik dairenin alanın hesaplanması için parametre olarak integer türünde yarıçap değeri ve float tipinde pi sayısı almak isteyebiliriz. Böyle bir durumda oluşturulması gereken şablon yapısı aşağıdaki gibidir:
#include <iostream>

template <typename T, typename S>
T area(T rad, S pi){
    return pi*rad*rad;
}

int main(){
    std::cout << area(2, 3.14f) << std::endl;
    return 0;
}
Şablon içerisinde iki tane, veri türlerini temsil edecek tanımlayıcı ekledik. Bunlar kullanıma göre aynı veri türüne de temsil edebilir. main fonksiyonu içerisinde area fonksiyonu çağırıldığında, birinci parametre için T integer türünü, ikinci parametre için S float türünü temsil etmektedir. Sonuç olarak ekrana yazdırılan ise 12 değeridir. Bunun sebebi fonksiyon türünün T temsilcisine bağlı olarak döndürülmesidir. Eğer S olarak değiştirilirse değer float türünde dönecektir. Bu durumda ekrana yansıyacak sonuç 12.56 değeridir. Bu örnek aynı zamanda non-type olarak bilinen şablonda kullanılan tür dışı parametrelere örnektir.

Sınıf Şablonları

Vector gibi yapılar generic programlama altında yer almaktadır. Sınıflar bilindiği üzere aslında kullanıcının kendi veri türlerini tanımladığı yapılardır. Örneğin bir dizi(array) türü tanımlamak istiyoruz. Ancak bu dizi yapısında tüm veri türlerinin depolanabilir olması gerek. Bir dizi string türünde değerler içerirken, diğeri de integer türünde değerler içerebilmelidir. Bunu gerçekleştirebilmek içinse Sınıf Şablonu kullanılmalıdır. En başta verdiğimiz Vector örneği ise bu şekilde gerçekleştirilmiştir. Aşağıdaki gibi template ile beraber Array adında bir sınıf tanımlandı.
template <typename T, int n>
class Array{
    private:
        T array[n];
    public:
        int sizeOfArray() const {return n;}
};
Template parametrelerinde belirtilen n parametresi dizi boyutunu belirleyecektir. T ise hangi veri türünü temsil ediyorsa o türde bir dizi oluşturacaktır. sizeOfArray() fonksiyonu ise oluşturulan dizinin boyutunu döndürmektedir. main fonksiyonu içerisinde Array yapısının kullanımına göz atalım:
int main(){
    Array<int, 10> array1;
    Array<std::string, 10> array2;
    Array<float, 5> array3;
    return 0;
}
Array türünde diziler oluşturduk. Oluşturulan diziler verilen parametreler sayesinde farklı veri türlerine ve dizi boyutlarına sahip oldu. Eğer Array oluştururken sadece boyut parametresi verirsek derleyici hatası alınacaktır. Ayrıca sınıf şablonu iki parametre yerine tek parametre olarak ekleyip, dizi boyutunu ise constructor(kurucu) fonksiyon üzerinden elde edebilirdik. Böyle bir durumda kod aşağıdaki gibi olmalıdır:
template <typename T>
class Array{
    private:
        T *array;
        int n;
    public:
        Array(int size){
            n = size;
            array = new T[n];
        }
        int sizeOfArray() const {return n;}
};

int main(){
    Array <int>array1(7);
    Array <std::string>array2(3);
    Array <float>array3(4);
    return 0;
}
Ayrıca sınıfta bildirilen fonksiyonu sınıf dışında tanımlamak için fonksiyon adından önce className<..>:: bildirimi yapılmalıdır. Örnekte olduğu gibi:
template <typename T>
class Array{
    private:
        T *array;
        int n;
    public:
        Array(int size){
            n = size;
            array = new T[n];
        }
        int sizeOfArray() const;
};

template <typename T>
int Array<T>::sizeOfArray() const {
    return n;
}

Hiç yorum yok:

Yorum Gönderme