C++ programlama dilinin kalbine, yani Nesne Yönelimli Programlama (Object-Oriented Programming – OOP) dünyasına hoş geldiniz! Bjarne Stroustrup, C++ dilini ilk tasarladığında ona “Sınıflı C” (C with classes) adını vermişti. Bu isimlendirme bile, sınıfların (classes) ve nesnelerin (objects) bu dil için ne kadar hayati bir öneme sahip olduğunu göstermeye yeterlidir.
C gibi prosedürel diller, programları bir dizi fonksiyona bölerek (yukarıdan aşağıya yaklaşım) çalışır ve verilerin sistem içinde serbestçe dolaşmasına izin verir,. Ancak projeler büyüdükçe bu yaklaşım kodun yönetilmesini zorlaştırır. C++ ise “aşağıdan yukarıya” (bottom-up) bir yaklaşım benimseyerek, verileri ve bu veriler üzerinde işlem yapan fonksiyonları tek bir güvenli çatı altında birleştirir.
Bu rehberde, C++ dilinde sınıfların (classes) ne olduğunu, nesnelerin (objects) nasıl oluşturulduğunu, veri gizleme (data hiding) prensibini ve üye fonksiyonların (member functions) nasıl kullanıldığını bol örneklerle derinlemesine öğreneceksiniz.
1. C Yapılarından (Structures) C++ Sınıflarına Geçiş
C dilinde farklı veri tiplerini tek bir isim altında toplamak için struct (yapı) kullanıldığını hatırlayabilirsiniz. Ancak geleneksel C yapılarının iki büyük dezavantajı vardır:
- Sadece verileri barındırabilirler, bu verileri işleyecek fonksiyonları içlerinde tutamazlar.
- Veri gizleme (data hiding) özelliklerine sahip değillerdir; yani yapı içindeki değişkenler programın herhangi bir yerindeki bir fonksiyon tarafından doğrudan erişilip değiştirilebilir.
C++, prosedürel dillerin bu sınırlarını aşmak için class (sınıf) adı verilen yepyeni bir veri tipini hayatımıza sokmuştur,. C++ sınıfları, hem verileri hem de bu veriler üzerinde işlem yapacak fonksiyonları bir araya getirerek modern yazılım mühendisliğinin en temel ihtiyaçlarından biri olan “kapsülleme” (encapsulation) işlemini gerçekleştirir,.
2. Sınıf (Class) Nedir ve Nasıl Tanımlanır?
Sınıflar (classes), kendi nesnelerinizi oluşturabilmeniz için kullandığınız kullanıcı tanımlı veri tipleridir (user-defined data types),. Bir sınıfı, bir araba üretmek için kullanılan “fabrika planı” veya “mühendislik çizimi” olarak düşünebilirsiniz. Planın kendisi fiziksel bir araba değildir, ancak arabanın hangi özelliklere (renk, model, hız) ve hangi yeteneklere (çalışma, durma, hızlanma) sahip olacağını tanımlar.
C++ dilinde bir sınıf bildiriminin temel sözdizimi (syntax) şu şekildedir:
class SinifAdi
{
private:
// Sadece sınıf içinden erişilebilen veri ve fonksiyonlar
public:
// Dışarıdan erişilebilen veri ve fonksiyonlar
}; // Sınıf tanımı noktalı virgül ile bitmelidir!
Erişim Belirleyiciler (Access Specifiers) ve Veri Gizleme
Sınıf tanımlamasında gördüğünüz private (özel) ve public (genel) ifadelerine görünürlük etiketleri veya erişim belirleyiciler denir.
- Private:
privatebölümünde tanımlanan verilere veya fonksiyonlara sınıfın dışından veya normal fonksiyonlardan kesinlikle erişilemez. Sadece o sınıfın kendi içindeki “üye fonksiyonları” bu gizli verilere ulaşabilir. Bu mekanizmaya veri gizleme (data hiding) denir,. C++’ta bir sınıfın üyeleri varsayılan (default) olarak her zamanprivatekabul edilir. - Public:
publicbölümünde tanımlanan üyelere ise programın herhangi bir yerinden (örneğinmain()fonksiyonunun içinden) doğrudan erişilebilir. Genellikle veriler (değişkenler) güvenliği sağlamak içinprivateyapılırken, bu verileri okuyup değiştirecek olan fonksiyonlarpublicyapılır.
3. Nesne (Object) Nedir ve Nasıl Oluşturulur?
Sınıfı bir “fabrika planı” olarak tanımlamıştık. İşte bu plandan üretilen gerçek, fiziksel arabalara nesne (object) adı verilir. Nesneler, nesne yönelimli sistemlerin en temel çalışma zamanı (run-time) varlıklarıdır. Bir sınıf tanımladıktan sonra, tıpkı int veya float tipinde bir değişken tanımlar gibi o sınıfın tipinde değişkenler oluşturabiliriz.
// "Ogrenci" adında bir sınıf tanımladığımızı varsayalım
Ogrenci ogr1; // ogr1, Ogrenci sınıfının bir nesnesidir
Ogrenci ogr2, ogr3; // Aynı anda birden fazla nesne de oluşturulabilir
Oluşturduğumuz nesneler, sınıfın public (genel) kısmında yer alan fonksiyonlara Nokta Operatörü (.) kullanarak erişebilir. Örneğin ogr1.notlariGoster(); komutu, ogr1 nesnesine ait notları ekrana yazdırmak için bir mesaj gönderir,.
Nesnelerin Bellek Yönetimi Nasıl Çalışır?
Nesnelerin bellekte nasıl yer kapladığını anlamak çok önemlidir. Bir nesne oluşturulduğunda, C++ sadece o nesnenin veri üyeleri (değişkenleri) için bellekte ayrı bir alan ayırır. Sınıfın üye fonksiyonları (metotları) ise her nesne için ayrı ayrı kopyalanmaz; fonksiyonlar bellekte yalnızca bir kez oluşturulur ve o sınıfa ait tüm nesneler tarafından ortaklaşa kullanılır. Bu sayede bellekten devasa oranda tasarruf edilir.
4. Üye Fonksiyonların (Member Functions) Tanımlanması
Sınıfın verileri üzerinde işlem yapan fonksiyonlara üye fonksiyonlar denir. Üye fonksiyonlar iki farklı şekilde tanımlanabilir:
- Sınıfın İçinde Tanımlama: Fonksiyonun kodları doğrudan
classbloklarının arasına yazılır. Sınıf içinde tanımlanan fonksiyonlar, C++ derleyicisi tarafından otomatik olarak “inline” (satıriçi) fonksiyon olarak kabul edilir ve genellikle çok kısa işlemler için tercih edilir. - Sınıfın Dışında Tanımlama: Gerçek dünya projelerinde sınıfların içi çok kalabalık olmasın diye fonksiyonların sadece isimleri sınıfın içine yazılır (prototipleme), fonksiyonun gerçek kodları ise sınıfın dışına yazılır. Dışarıda tanımlama yaparken, o fonksiyonun hangi sınıfa ait olduğunu belirtmek için Kapsam Çözünürlük Operatörü (Scope Resolution Operator –
::) kullanmak zorunludur.
Kapsamlı C++ Sınıf ve Nesne Örneği
Öğrendiğimiz tüm bu soyut kavramları birleştiren tam teşekküllü bir C++ programı yazalım. Bir banka hesabı sistemini modelleyeceğiz.
#include <iostream>
#include <string>
using namespace std;
// Sınıf Tanımlaması
class BankaHesabi
{
private:
string hesapSahibi; // Veriler dışarıdan erişime kapalı (Data Hiding)
double bakiye;
public:
// Sınıf içinde tanımlanan fonksiyon (Inline davranış)
void baslangicDegeriAta(string isim, double miktar)
{
hesapSahibi = isim;
bakiye = miktar;
}
// Sadece prototipleri yazılan fonksiyonlar
void paraYatir(double miktar);
void bakiyeGoster(void);
};
// Sınıf dışında üye fonksiyon tanımlaması (Kapsam Çözünürlük Operatörü :: kullanımı)
void BankaHesabi::paraYatir(double miktar)
{
bakiye = bakiye + miktar; // Üye fonksiyonlar private verilere doğrudan erişebilir
cout << miktar << " TL yatirildi. Yeni bakiye: " << bakiye << " TL" << endl;
}
void BankaHesabi::bakiyeGoster(void)
{
cout << "Hesap Sahibi: " << hesapSahibi << endl;
cout << "Mevcut Bakiye: " << bakiye << " TL" << endl;
}
int main()
{
// Nesne oluşturma (Instantiation)
BankaHesabi musteri1;
BankaHesabi musteri2;
cout << "--- Musteri 1 Islemleri ---" << endl;
musteri1.baslangicDegeriAta("Ahmet Yilmaz", 1000); // Nokta operatörü ile public fonksiyona erişim
musteri1.paraYatir(500);
musteri1.bakiyeGoster();
cout << "\n--- Musteri 2 Islemleri ---" << endl;
musteri2.baslangicDegeriAta("Ayse Demir", 2500);
musteri2.bakiyeGoster();
// musteri1.bakiye = 50000; // HATA! 'bakiye' private olduğu için dışarıdan doğrudan değiştirilemez.
return 0;
}
Bu örnekte görebileceğiniz gibi, musteri1 ve musteri2 nesnelerinin hesap bilgileri birbirine karışmaz; çünkü her nesnenin kendi veri üyeleri için bağımsız bir bellek alanı vardır. Ayrıca bakiye değişkeni private olduğu için main() içerisinden doğrudan müdahale edilmesi (hacklenmesi veya bozulması) engellenmiştir,.
5. İleri Düzey Kullanımlar: Nesne Dizileri ve Argüman Olarak Nesneler
Eğer bir şirketteki 100 farklı çalışanın veya bir okuldaki 50 öğrencinin verilerini tutmak isteseydik, tek tek Ogrenci ogr1, ogr2, ogr3... yazmak mantıksız olurdu. C++, tıpkı temel veri tiplerinde olduğu gibi nesnelerin de dizilerini (arrays) oluşturmamıza izin verir. Örneğin; BankaHesabi musteriler; komutu, BankaHesabi sınıfından 50 farklı nesneyi bellekte ardışık olarak yaratır. Daha sonra bir for döngüsü yardımıyla musteriler[i].bakiyeGoster(); diyerek tüm müşterilerin hesapları üzerinde hızlıca işlem yapabiliriz.
Nesneler aynı zamanda fonksiyonlara parametre (argüman) olarak da gönderilebilirler. Bir nesneyi fonksiyona geçirmenin iki yolu vardır:
- Değer ile Çağırma (Pass-by-value): Nesnenin bir kopyası çıkartılarak fonksiyona gönderilir. Fonksiyon içinde nesneye yapılan değişiklikler orijinal nesneyi etkilemez.
- Referans ile Çağırma (Pass-by-reference): Nesnenin doğrudan bellek adresi fonksiyona iletilir. Böylece çağrılan fonksiyon, doğrudan orijinal nesnenin verileri üzerinde çalışır ve yapılan değişiklikler kalıcı olur. Bu yöntem, büyük nesnelerin kopyalanması ekstra bellek ve zaman israfına yol açacağı için çok daha verimlidir.
Özet
C++’ta sınıflar (classes) ve nesneler (objects), modern yazılım mühendisliğinde kodun karmaşıklığını yönetmenin, tekrar kullanılabilir (reusable) sistemler inşa etmenin ve verileri güvende tutmanın en güçlü yoludur. private ve public erişim belirleyicileri sayesinde verilerinizi dış müdahalelerden saklayabilir (kapsülleme), üye fonksiyonlar ile dış dünyaya güvenli bir iletişim arayüzü sunabilirsiniz. Sınıfların temel sözdizimini ve nokta operatörünün çalışma mantığını kavradığınızda, bir sonraki aşama olan Kalıtım (Inheritance) ve Çok Biçimlilik (Polymorphism) gibi daha ileri C++ konularını anlamanız çok daha kolay olacaktır.





