C++ sınıflarında temel işlevler, yani kurucu fonksiyonlar, yıkıcı fonksiyonlar ve kopyalama işlevleri, sınıf özelliklerinin ayarlanması, örnek nesnelerin bellek yönetimi ve sınıf örneklerinin kopyalanması gibi işlemler için kullanılır Kurucu fonksiyonlar, sınıf örneği oluşturulurken çağrılırken yıkıcı fonksiyonlar sınıf örneği yok edildiğinde çağrılır Kopyalama işlevleri de bir sınıf örneği başka bir örneğe kopyalandığında çağrılır Bu işlevlerin tanımlanması sınıfın doğru bir şekilde kullanılması için oldukça önemlidir Derin kopya işlevleri ise dinamik bellek alanlarını derinlemesine kopyalamak için kullanılır
C++ dilinde yer alan nesne yönelimli programlama prensipleri arasında sınıflar önemli bir yer tutmaktadır. Sınıflar, C++ dilinde özellikler ve işlevler içeren bir datalar topluluğudur. Bu datalar, sınıfa özgü değişkenler ve fonksiyonlar olarak tanımlanır. Temel işlevler adı verilen üç önemli işlev, her sınıf içinde tanımlanmalıdır.
Bu temel işlevler şunlardır:
- Kurucu fonksiyonlar
- Yıkıcı fonksiyonlar
- Kopyalama işlevleri
Kurucu fonksiyonlar, bir sınıf örneği oluşturulurken otomatik olarak çağrılan işlevlerdir. Kurucu fonksiyonlar sınıfların özelliklerinin ayarlanması için kullanılır ve genellikle özellikler için parametre alabilirler. Örnek olarak;
MyClass(int a, double b){ this->a = a; this->b = b; }
Yıkıcı fonksiyonlar ise bir sınıf örneği yok edildiğinde otomatik olarak çağrılan işlevlerdir. Yıkıcı fonksiyonlar, özellikle örnekte yer alan bellek alanlarının serbest bırakılması gibi işlemler için kullanılır. Örnek olarak;
~MyClass(){ delete[] pointer; }
Kopyalama işlevleri, bir sınıf örneği başka bir örneğe kopyalandığında otomatik olarak çağrılan işlevlerdir. Kopyalama işlevleri, genellikle bir sınıf örneğinin özelliklerini başka bir örneğe kopyalamak için kullanılır. Örnek olarak;
MyClass& operator=(const MyClass& other){ this->a = other.a; this->b = other.b; // pointer için derin kopya işlemi yapılmalı }
Temel işlevlerin tanımlanması sınıfların doğru bir şekilde kullanılması için oldukça önemlidir. Bu işlevlerin tanımlanması için örneklerin incelenmesi faydalı olacaktır.
Kurucu Fonksiyonlar
Kurucu fonksiyonlar, bir sınıf örneği oluşturulurken otomatik olarak çağrılan işlevlerdir. Bu fonksiyonlar, sınıf içindeki değişkenlerin başlangıç değerlerinin atanmasını sağlar. Ayrıca sınıf tanımlandıktan sonra, bir nesne oluşturulmadan önce sınıfın hazırlık aşamasını tamamlar.
Kurucu fonksiyonlar, sınıfın adı ile aynı isimde olmalıdır. Bu işlevler, dönüş tipi belirtilmemiş bir işlev olarak tanımlanır ve herhangi bir değer döndürmezler. Kurucu fonksiyonlar, varsayılan olarak, sınıf üzerinde bir çıktı üretmezler. Ancak, gerektiğinde, sınıf üzerinde bir çıktı da üretebilirler.
Bir sınıf örneği oluşturulurken, kurucu fonksiyon otomatik olarak çağrılır. Kurucu fonksiyon, sınıfın örneği için bellekte gereken alanı ayırır ve örneğin başlangıç değerlerini atar.
Örneğin, aşağıdaki örnek, a basamaklı sayıların depolandığı bir sınıf tanımlar ve kurucu fonksiyonu ile örnek nesnenin tanımlanmasını gösterir:
Kod Örneği | Çıktı |
---|---|
class Sayi{ private: int a; public: Sayi(): a(0){}}; int main(){ Sayi sayi1; return 0;} | --- |
Burada, Sayi adlı sınıf, bir a değişkeni içerir ve kurucu fonksiyon sayesinde a değişkeni sıfıra atanır. Daha sonra, main işlevine geri dönülür ve sayi1 adlı nesne oluşturulur.
Yıkıcı Fonksiyonlar
Yıkıcı fonksiyonlar, bir sınıf örneği yok edildiğinde otomatik olarak çağrılan işlevlerdir. Bu fonksiyonlar, örnek öğenin bellekte yer işgalini bırakması ve kaynakların bırakılması işlemlerini gerçekleştirirler.
Yıkıcı fonksiyonlar, sınıfın adı ile tanımlanırlar ve fonksiyon adı önünde tilda(~) işareti kullanılarak belirtilirler.Örnek olarak:
```class Araba{ public: int hiz; Araba(); ~Araba(); //Yıkıcı Fonksiyon tanımı};
Araba::Araba(){ hiz = 0; cout << "Araba olusturuldu" << endl;}
Araba::~Araba(){ cout << "Araba yok edildi"<< endl;}
int main(){ Araba *araba = new Araba(); delete araba; return 0;}```
Yukarıda yer alan kod bloğunda Araba sınıfının kurucu ve yıkıcı fonksiyon tanımları yapılmıştır. Kurucu fonksiyonun görevi arabayı oluştururken yıkıcı fonksiyonun görevi ise arabayı siler.
Yukarıdaki kod bloğunu çalıştırdığımızda önce "Araba oluşturuldu" çıktısını alarak arabayı oluştururuz. Sonrasında, delete anahtar kelimesiyle nesneyi sileriz ve bu işlem yıkıcı fonksiyonun çalışmasına neden olur. Dolayısıyla, terminate() fonksiyonuna benzer şekilde yıkıcı fonksiyon, bir sınıftaki kaynakların serbest bırakılması için kullanılabilir.
Sonuç olarak, yıkıcı fonksiyonlar, sınıfın bellek yönetimi ile ilgili olarak yapacağı işlemleri kolaylaştırabilir. Bu nedenle, sınıfın tanımında yıkıcı fonksiyonlara yer verilmesi, iyi yazılmış bir sınıfta önemlidir.
Kopyalama İşlevleri
Kopyalama işlevleri, bir sınıf örneği başka bir örneğe kopyalandığında otomatik olarak çağrılan işlevlerdir. Bu işlevler, bir sınıf örneğinin bellek adresini değil, örnekteki değerleri kopyalar. Bu nedenle, bellekte diskte bulunan dosyalarda değişiklikler yapılması gerektiğinde yararlıdır.
Kopyalama işlevleri, copy constructor ve operator= işlevleri ile tanımlanabilir. Copy constructor, yeni bir sınıf örneğinin, mevcut bir sınıftan oluşturulması durumunda çağrılır ve bir parametre kullanır. Operator= ise, zaten oluşturulmuş bir sınıf örneğine başka bir örneğin atanması durumunda çağrılır.
Bir nesneyi kopyalarken, kopyalama işlevleri üzerinde çalışırken, dikkat edilmesi gereken bazı özel kurallar vardır. Örneğin, bir sınıfın göstericilerinin kopyalanması söz konusu olduğunda, başvuru sayısı değiştirilmelidir.
Derin Kopya İşlevleri
Derin kopya işlevleri, örnekte yer alan dinamik bellek alanlarını doğru bir şekilde kopyalamayı sağlar. Böylece, sınıf örneği farklı bir nesneye atandığında, kopyalanan örnekteki dinamik bellek alanının değerleri değişse bile orijinal örnekteki değerler kaybolmaz. Bu işlevler sayesinde, bir sınıf içerisinde dinamik bellek kullanan bir veri yapısı bulunuyorsa, bu yapıların bir kopyası oluşturulabilir.
Derin kopya işlevlerinin tanımlanması sırasında, değer aktaran değil, bellekleri atan türden bir kopyalama işlemi yapılır. Bu sebeple, kopyalama işlemlerinde kullanılan bellek adresleri orijinal bellek adreslerinden farklı olmalıdır. Kopyalama işlemi, veri yapılarının bellek adreslerine ilişkin bir dizi işlemi birarada gerçekleştirir. Bu işlemler daha sonra nesnenin yayında gerçekleştirilmesi sırasında kullanılır.
Derin kopya işlevlerinin kullanım örneklerinde, bir sınıf örneğinin kopyalanması/taşınması söz konusu olduğunda bu işlevleri kullanırız. İşlevler, sınıf örneğini yaratmada ve parametre olarak geçmekte kullanılırlar. Derin kopya işlevi belirli örneklere özgü bir yapıdır ve sınıfın yapısına bağlıdır. İşlev, sınıfın yönetimine ilişkin özel bir işlevdir. Derin kopya işlevleri sınıf yapısına göre değişiklik gösterir ve bu sebeple sınıf yapısında belirtilmesi gereken destekleyici işlevler içerirler.
Derin kopya işlevlerinin kullanımlarında şu özelliklere dikkat etmek gereklidir:
- Derin kopya işlevi, orijinal nesneden bağımsızdır.
- Derin kopya işlevi kullanılırken, bellek yönetimi dikkate alınır.
- Derin kopya işlevi, bellek kirliliği ve diğer yan etkileri ortadan kaldırır.
Derin kopya işlevleri, C++ sınıflarında kullanılan temel işlevlerden biri olarak öne çıkmaktadır. Bu işlevlerin kullanımı, sınıf yapısı içerisinde kullanılan dinamik bellek yönetimi işlemlerini doğru bir şekilde gerçekleştirmeye olanak tanır. Bu sayede, sınıf örneği kopyalandığında veya taşındığında bellek yönetimi sorunları yaşanmaz ve sorunsuz bir sınıf yapısı oluşturulur.
Yer Değiştirme İşlevleri
C++ sınıfları kullanılırken, örneklerin birbirleriyle yer değiştirmesi gerekebilir. Bu işlemin otomatik olarak gerçekleştirilmesi için yer değiştirme işlevleri kullanılır. Yer değiştirme işlevleri, bir örneğin başka bir örnekle yer değiştirdiğinde çağrılan işlevlerdir.
Yer değiştirme işlevleri,
- bir referans döndürmelidir,
- kendisine atanan örneği işaret etmelidir,
- geçersiz kılınmalıdır.
Yukarıdaki şartlar yerine getirilmeden yer değiştirme işlevleri doğru şekilde çalışmayacaktır. Aşağıda örnek bir yer değiştirme işlevi yer almaktadır:
class MyClass { | ||
---|---|---|
public: | ||
MyClass& operator=(MyClass&& other) noexcept { | ||
if (this != &other) { | ||
delete data; | ||
data = other.data; | ||
} | ||
return *this; | ||
} | ||
private: | ||
int* data; | ||
}; |
Yukarıdaki örnekte, yer değiştirme işlevi, bir referans döndürmektedir. Burada eşitliğin solundaki operandın belleğinin silinmesi ve sağ operandın belleğine aktarılması sağlanmaktadır.
Yer değiştirme işlevleri, sınıfların başarılı çalışması için önemli bir rol oynamaktadır. Bu işlevlerin doğru şekilde tanımlanması ve kullanılması, programların daha güvenilir ve hata oluşma ihtimali az olan bir şekilde geliştirilmesini sağlar.
Atama İşlemleri
Atama işlemleri, bir sınıf örneği başka bir örneğe atandığında otomatik olarak çağrılan işlevlerdir. Bu işlevler sayesinde, bir sınıf örneğindeki veriler diğer bir örneğe aktarılabilir. Atama işlemleri, aynı zamanda kopyalama işlevlerinden farklıdır. Kopyalama işlevleri, bir sınıf örneğinin bir başka örnek tarafından kopyalanması durumunda çağrılırken, atama işlemi sırasında bir sınıf örneği başka bir örneğe atandığında çağrılır.
Atama işlemleri, C++ dilinde '=' sembolü kullanılarak tanımlanır. Örneğin, aşağıdaki kodda, MyClass sınıfının bir örneği olan obj1 obj2'ye atanmıştır.
MyClass obj1;MyClass obj2;obj2 = obj1;
Bu kodda, obj2, obj1'in değerlerini alır. Ancak, atama işlemi sadece değerleri kopyalamaz, aynı zamanda dinamik bellek alanlarının da doğru bir şekilde kopyalanmasını sağlar. Atama işlemi için kopyalama işlevleri ve derin kopya işlevleri kullanılabilir.
Aşağıdaki örnekte, MyClass sınıfının kopyalama işlevi '=' sembolü ile tanımlanmıştır. Bu işlev, bir sınıf örneğinin başka bir örneğe kopyalanması durumunda çağrılır:
class MyClass {public: // Kopyalama işlevi MyClass& operator=(const MyClass& other) { // Değerleri kopyala x = other.x; y = other.y; // Bellek alanlarını kopyala delete[] data; data = new int[other.size]; size = other.size; for (int i = 0; i < size; i++) { data[i] = other.data[i]; } return *this; }private: int x; int y; int* data; int size;};
Bu örnekte, MyClass sınıfının bir örneği başka bir örneğe atandığında, mümkün olan en iyi kopyalama işlemi gerçekleştirilir. Değerleri kopyalamanın yanı sıra, 'data' adlı int türünden bir dinamik bellek alanının da doğru bir şekilde kopyalanmasını sağlar.
Operator Overloading
C++, diğer programlama dillerine kıyasla, sınıf türünden örneklerde matematiksel ve mantıksal işlemlerin yapılmasını sağlayan bir özelliğe sahiptir. Bu özellik, operator overloading olarak adlandırılır. Operator overloading'in amacı, özel semantik davranışlar sağlamak için yerleşik operatör işlevlerine özelleştirilmiş davranış atamaktır.
Operator overloading'in kullanımı, kodun okunabilirliğini ve bakımını artırır. Örneğin, bir sınıfın iki örneği arasında toplama işlemi yapmak için işlem operatörünü (+) kullanmamız gerekir. Bu, sınıfın iki örneğini toplama işleminin birincil operandı, dönüştürülecek tür (int, double vb.) ise ikincil operandı olarak ele alır.
Operator overloading, sınıf türünden örneklerde birçok matematiksel işlemi gerçekleştirebilir. Ayrıca, birçok mantıksal operatörü (==, !=, <, > vb.) yeniden tanımlamak için de kullanılır. Örneğin, iki sınıf örneği arasında '<' operatörünü kullanmak istiyorsak, operator overloading kullanarak bu davranışı özelleştirebiliriz.
Bir örnekteki operator overloading işlevleri, bir parametre olarak geçerli bir sınıf örneği alır ve sonuç olarak yeni bir sınıf örneği döndürür. Bu sayede, sınıf örnekleri arasındaki matematiksel ve mantıksal işlemleri gerçekleştirmek için özel işlevler yazabiliriz.
Operator overloading, C++ dilinde yer alan birçok özelliğden biridir ve yazdığımız kodların okunabilirliğini artırır. Bu özellik, sınıf türünden örneklerde matematiksel ve mantıksal işlemlerin yapılmasını kolaylaştırır. Bu nedenle, C++ programlama dilinde öğrenilmesi gereken önemli bir özellik olarak karşımıza çıkar.