C++ birçok yeni yazılımcının da bir hevesle başladığı ve muhtemelen 1000 kişiden 999'unun kaçtığı bir dil. Amatörlerin elinde tam bir savaş alanına, güvenlik ve performans cehennemine dönüşüyor. Burada en büyük dikkati istediğini düşündüğüm hafıza yönetimi konusuna değineceğim. Diğer dikkat edilmesi gereken konular ise temel başlıklarıyla; güvenli veri yönetimi, sql injection, xss ve csrf, yetkilendirme sorunları, validations, error handling.
C++ dilinde hafıza yönetiminde aşağıdaki noktalara dikkat etmeniz önerilir:
Dinamik bellek alımı ve serbest bırakma
Dinamik bellek alımı ve serbest bırakma işlemleri sırasında dikkatli olmalısınız. Özellikle, her bellek alımı için serbest bırakma işlemi gerçekleştirilmelidir. Ayrıca, bellek alımı sırasında oluşabilecek hata durumlarını kontrol etmelisiniz.
C++'da dinamik bellek alımı için "new" operatörü kullanılır. Örnek olarak, int tipinde bir değişken için bellek alınması için aşağıdaki kod kullanılabilir:
int* p = new int;
Bu kod int tipinde bir değişken için bellek alır ve "p" adlı bir işaretçiye atar. Dinamik bellekte alınan bu değişken artık "p" işaretçisi ile erişilebilir.
Aynı şekilde, bir dizi için bellek almak için aşağıdaki kod kullanılabilir:
int* p = new int[10];
Bu kod int tipinde 10 elemanlı bir dizi için bellek alır ve "p" adlı bir işaretçiye atar. Bu dizideki elemanlar "p[0]" ile "p[9]" arasındaki değerler olarak erişilebilir.
Bellekte alınan bu değişkenlerin veya dizilerin bellekten serbest bırakılması için "delete" operatörü kullanılır. Örnek olarak, yukarıdaki örnekte alınan int tipinde bir değişken için bellek serbest bırakmak için aşağıdaki kod kullanılabilir:
delete p;
Aynı şekilde, yukarıdaki örnekte alınan int tipinde bir dizi için bellek serbest bırakmak için aşağıdaki kod kullanılabilir:
delete[] p;
Dikkat: Dinamik bellek yönetimi yaparken dikkatli olun, özellikle bellek serbest bırakma işlemi unutulursa sisteminizde bellek sızıntısı oluşabilir.
Veri yıkama
Dinamik bellek alımı sırasında veri yıkama işlemi yapmalısınız. Veri yıkama, bellek alanının içeriğinin belirli bir değerle doldurulmasıdır. Bu sayede bellek alanının içeriğinde güvensiz verilerin kalmamasını sağlar.
Veri yıkama, verinin temizlenmesi ve düzenlenmesi işlemidir. C++ dilinde veri yıkama işlemleri genellikle diziler, vector'ler veya string'ler gibi veri yapılarının içinde yapılır.
Verinin temizlenmesi için C++ dilinde birçok fonksiyon mevcuttur. Örneğin, veri içindeki boşlukların silinmesi için "trim" fonksiyonu kullanılabilir:
#include <algorithm>
#include <string>
std::string data = " Veri yıkama ";
std::string data_clean = data;
data_clean.erase(std::remove(data_clean.begin(), data_clean.end(), ' '), data_clean.end());
Bu kod içindeki boşlukları siler ve "data_clean" adlı bir string içerisinde saklar.
Verinin düzenlenmesi için C++ dilinde önemli fonksiyonlar arasında "replace" fonksiyonu yer almaktadır. Örneğin, veri içindeki tüm noktalama işaretlerinin silinmesi için aşağıdaki kod kullanılabilir:
std::string data = "Veri.yıkama,işlemleri";
std::string data_clean = data;
std::string punctuations = ".,;:!?";
for (char p : punctuations) {
data_clean.erase(std::remove(data_clean.begin(), data_clean.end(), p), data_clean.end());
}
Bu kod veri içindeki tüm noktalama işaretlerini silerek "data_clean" adlı bir string içerisinde saklar.
Dikkat: Veri yıkama işlemleri genellikle veri içinde arama ve değiştirme işlemleri ile gerçekleştirilir. Bu nedenle, veri yıkama işlemleri genellikle diziler, vector'ler veya string'ler gibi veri yapılarının içinde yapılır.
Referans sayacı
Referans sayacı yöntemi, bellek alanının ne zaman serbest bırakılacağını belirlemek için kullanılır. Bu yöntem, bellek alanının birden fazla yerde kullanılması durumunda kullanışlıdır.
Referans sayacı yöntemi, bellekte yer alan nesnelerin kullanım sayısını tutarak otomatik olarak bellek serbest bırakma işlemlerini gerçekleştirir. C++ dilinde, bu yöntemi uygulamak için kullanabileceğiniz birkaç yöntem vardır.
Birincisi, kendi sınıfınızda referans sayacını yönetebilirsiniz. Örnek olarak, aşağıdaki kod bloğunda bir sınıf oluşturulur ve bu sınıfta referans sayacını yöneten bir yapı oluşturulur.
class MyClass {
public:
MyClass() {
ref_count = new int(1);
}
MyClass(const MyClass& other) {
ref_count = other.ref_count;
++(*ref_count);
}
~MyClass() {
--(*ref_count);
if (*ref_count == 0) {
delete ref_count;
// other cleanup
}
}
private:
int* ref_count;
};
Bu kod bloğunda, MyClass sınıfının yapılandırıcısında yeni bir referans sayacı oluşturulur ve yıkıcısında bellek serbest bırakılır. Aynı zamanda, kopyalama yapılandırıcısında referans sayacı arttırılır. Bu şekilde, sınıfın kopyası oluşturulduğunda referans sayacı arttırılır ve sınıfın yok edildiğinde referans sayacı azaltılır.
Diğer bir yolu ise shared_ptr ve unique_ptr gibi standart C++ kütüphanelerinde bulunan smart pointerlar kullanmaktır. Bu tip pointerlar kendilerine atanmış objenin kullanım sayısını tutarlar ve otomatik olarak bellek serbest bırakma işlemlerini gerçekleştirirler. Örnek olarak:
std::shared_ptr<int> p1(new int(5));
std::shared_ptr<int> p2 = p1;
Bu kod bloğunda, p1 ve p2 için aynı bellekteki int değişkeni için referans sayacı oluşur. Bu nedenle, p2 yok edildiğinde veya p1 için başka bir değer atandığında bellek serbest bırakılmaz. Ancak, p1 ve p2 için bellekteki int değişkeni için oluşan referans sayacı sıfır olduğunda bellek serbest bırakılır. Bu yöntem, bellek yönetimini otomatik hale getirir ve programcının bellek serbest bırakma işlemlerini manuel olarak yapmasını gerektirmez.
Bu yöntem, birden fazla noktadan erişilen veya paylaşılan nesneler için özellikle yararlıdır. Örneğin, bir grafik işlemi yaparken, aynı resim dosyasını birden fazla noktadan okuyabilirsiniz ve referans sayacı yöntemi ile bellekteki resim dosyasını sadece gerçekten kullanılmayan noktalarda serbest bırakabilirsiniz.
Ancak, referans sayacı yöntemi bazen bellek sızıntısına yol açabilir. Örneğin, referans sayacı yöntemi kullanılırken döngüler veya özyinelemeli fonksiyonlar kullanılırsa, bellek sızıntısı oluşabilir. Bu nedenle, referans sayacı yöntemi kullanırken dikkatli olunması gerekir.
Smart pointer
Smart pointer, bellek alanının otomatik olarak serbest bırakılmasını sağlar. Smart pointer, bellek alanının kullanım sayısını kontrol eder ve bellek alanının kullanım sayısı 0 olduğunda bellek alanını serbest bırakır.
C++ dilinde, "smart pointer" (akıllı işaretçi) adı verilen bellek yönetim aracı mevcuttur. Smart pointerlar, bellekte yer alan nesnelerin kullanım sayısını tutarak otomatik olarak bellek serbest bırakma işlemlerini gerçekleştirir.
Standart C++ kütüphanelerinde shared_ptr
, unique_ptr
, weak_ptr
gibi smart pointerlar bulunmaktadır. shared_ptr ve unique_ptr referans sayacı yöntemini kullanırken, weak_ptr ise shared_ptr ile ilişkili olarak kullanılır.
shared_ptr, bir nesnenin birden fazla yerde paylaşılmasını sağlar. Örnek olarak, aşağıdaki kod bloğunda iki farklı smart pointer aynı bellekteki bir int değişkenine işaret eder:
std::shared_ptr<int> p1(new int(5));
std::shared_ptr<int> p2 = p1;
Bu kod bloğunda, p1 ve p2 için aynı bellekteki int değişkeni için referans sayacı oluşur. Bu nedenle, p2 yok edildiğinde veya p1 için başka bir değer atandığında bellek serbest bırakılmaz. Ancak, p1 ve p2 için bellekteki int değişkeni için oluşan referans sayacı sıfır olduğunda bellek serbest bırakılır.
unique_ptr ise, bir nesnenin sadece bir yerde kullanılmasını sağlar. Örnek olarak, aşağıdaki kod bloğunda unique_ptr kullanımı gösterilmiştir:
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = std::move(p1);
Bu kod bloğunda, p1 için oluşan unique_ptr p2'ye taşınır ve p1 artık geçerli değildir. Bu yöntem ile, p1'in kullanımı sadece p2 tarafından yapılır ve bellek serbest bırakma işlemi p2 yok edildiğinde gerçekleşir.
weak_ptr ise shared_ptr ile ilişkili olarak kullanılır ve shared_ptr'ın referans sayacını tutar ancak bellek serbest bırakma işlemi gerçekleştirmez. Örnek olarak:
std::shared_ptr<int> sp(new int(5));
std::weak_ptr<int> wp = sp;
Bu kod bloğunda, wp shared_ptr'ın referans sayacını tutar ancak bellek serbest bırakma işlemi gerçekleştirmez. Bu sayede, shared_ptr tarafından yönetilen bellekteki nesnenin kullanım sayısını izleyebilirsiniz, ancak bellek serbest bırakma işlemi gerçekleştirmezsiniz.
Smart pointerlar, bellek yönetimini otomatik hale getirir ve programcının bellek serbest bırakma işlemlerini manuel olarak yapmasını gerektirmez. Ayrıca, shared_ptr ve unique_ptr referans sayacı yöntemini kullanarak bellekte yer alan nesnelerin kullanım sayısını tutarlar ve otomatik olarak bellek serbest bırakma işlemlerini gerçekleştirirler.
Dikkat : Smart pointerlar ile oluşturulan nesnelerin kullanım süresi veya yıkım sırası ile ilgili hatalar oluşabilir ve bellek sızıntısına sebep olabilir. Örneğin, aşağıdaki kod bloğunda unique_ptr ile oluşabilecek bir hata gösterilmiştir:
std::unique_ptr<int> p1(new int(5));
{
std::unique_ptr<int> p2 = std::move(p1);
// p1 is no longer valid
// p2 is now responsible for the memory
}
// p2 goes out of scope and the memory is deleted
std::cout << *p1 << std::endl; // runtime error, p1 is no longer valid
Bu kod bloğunda, p1 unique_ptr'ı p2'ye taşınır ve p1 artık geçerli değildir. Ancak, p1 hala kullanılmaya çalışılır ve program çalışma zamanında hata verir. Bu gibi durumlar için, smart pointerların kullanım süresi ve yıkım sırası dikkatli bir şekilde planlanmalıdır.
Ayrıca, smart pointerlar ile oluşturulan nesnelerin kullanım süresi veya yıkım sırası ile ilgili hatalar oluşabilir. Örneğin, shared_ptr kullanımı gösterilmiştir:
std::shared_ptr<MyClass> p1(new MyClass());
std::shared_ptr<MyClass> p2(p1);
p1.reset();
p2->DoSomething(); // runtime error, p2 is now pointing to a deleted object
Bu kod bloğunda, p1 shared_ptr'ını sıfırlıyor ve p2 artık silinen bir nesneye işaret etmektedir. Bu gibi durumlar için, smart pointerların kullanım süresi ve yıkım sırası dikkatli bir şekilde planlanmalıdır.