C & C++ Dinamik Bellek Yönetimi & Extern C Bildirimi

Bu konuda fazla derine inmeden c dilinden c++ diline geçerken dinamik bellek yönetiminde bizi bekleyen bazı değişikliklerden bahsetmek istiyorum. Aynı zaman da extern c bildirimi ve interface tasarımı ile ilgili birkaç noktaya değinmek istiyorum.

  • C dilinde stdlib kütüphanesinde ki fonksiyonları kullanıyorduk.. (malloc, alloc vs.)

  • C++ dilinde bunlara gerek kalmayacak. Yeni operatörlerimiz var ismi new olan operatörler ve ismi delete olan operatörler.

  • new operatörler nesneyi HAYATA GETİRİR.

  • delete dinamik nesnelerin HAYATINI SONLANDIRIR.

  • malloc hayat başlatmıyor sadece yer ayırıyordu ama new nesneyi hayata getiriyor.(initialize etme, yazma)
    Bellekte bir nesnenin yerinin olması onu aslında nesne yapmıyor. Storage nesnenin gerek şartı ama yeter şartı değil.


NEDEN ?

  • Primitive türler olduğunda şu değişkene bir veri atarım diyebiliriz fakat sınıf olduğunda olay bu kadar basit olmayacak. O bellek alanın kullanılabilir bir hale getirilmesi için birçok şey yapılacak(kaynaklar edinilecek vs vs)

  • Yer elde etmek başka birşey, elde edilen yerde bir nesneyi hayata getirmek başka birşey.

  • Bellekte yeri olmayan nesneden bahsetmek mümkün değil.

  • new operatörünün 4–5 biçimi var, biz şimdilik 2 biçiminde bahsedeceğiz.

  • Primitive türlerden örnek: new int → int türden bir dinamik nesneyi hayata başlatıyorum

#include <iostream>
#include <stdlib.h>


int main() {

int *p1 = new int;
int *p2 = new int[10];

// kodlar

delete p1;
delete []p2;

return 0;

}
#include <iostream>
#include <stdlib.h>

int main() {

int *p1 = new int{}; // *p1 = 0
int *ptr1 = new int{12}; // *ptr1 = 12
int *ptr2 = new int(20); // *ptr2 = 20

int *p2 = new int[10];

/// ....

delete p1;
delete []p2;

return 0;

}
  • auto keyword’ü tanıttığı ismi, ilk değer vereceğimiz ifadenin türü yapar.

  • memory leak ile resource leak birbirine karıştırmamak gerek.

  • memory leak → bellek sızıntısı, resource → kaynak sızıntısı

  • İşimiz bittikten sonra nesneleri delete etmemiz gerekiyor. Etmezsek bellek sızıntısı olur, birçok user defined tür için de kaynak sızıntı işi olur.


Basitçe arka planda ne oluyor ?

  • new operatörü 2 işi birden yapıyor (derleyicinin oluşturduğu kodda)

  • Hayata gelen nesne için bellek alan gerekiyor. sizeof_t kadar. Arka planda bunu operator new isimli fonksiyona yaptırıyor

  • new operatörü ile operator new fonksiyonu (karıştırılmamalı)

  • operator fonksiyonları, isimlerinin doğal parçası operator… Örnek toplama operatörü ise operator+ gibi.

  • operator new, malloc un parametrik yapısının aynısı.


C++ niye malloc kullanılmamış?

  • Exception handling için

  • new operatörü bellek elde etmek dışında, sınıf nesnelerinin hayata gelmesi için gerekli olan constructor fonksiyonunu çağrıyor.

  • delete ise destructor’ı çağırır, operator delete kullanır.

  • C++ dilinde default olarak garbage collector olmaması sonucunda delete operatörü var. Biz de hazır bir garbage collector yok.

  • Dinamik nesne oluşturma ile, dinamik nesnelerden oluşan bir liste oluşturma sentaksı farklıdır.

operator delete(ptr); // free'nin karsiligi

operator new -> malloc
operator delete -> free

operator new // Diger gorevi CONSTRUCTOR cagirmak
operator delete // Diger goreci DESTRUCTOR cagirmak

// dinamik bir nesne hayata getirme
T *p = new T;

// exp kadar dinamik nesne olasaca
new int[exp];


// Cogul
T *p = new T[n];
delete []p;

int *p1 = new int; // COP DEGER
int *p2 = new int[10]; // COP DEGER


// Tekil ise
delete p1;

// Cogul ise
delete [p2]; // koseli parantez icine birsey yazilmaz

int *p1 = new int{}; // *p1 = 0
int *p1 = new int{12}; // *p1 = 12 ilk deger verilebilir

new int(10); // 1 tane
new int[10]; // 10 tane
  • C++ 11 öncesi [] ile hayata getirilen primitive türlere ilk değer vermek yoktu. C++11 ile {} initializer list geldi.
int n = 5;
int *p = new int[n] {1, 2, 3, 4, 5};
  • Nesneleri kısmen öldürmek gibi birşey söz konusu değil. Toplu halde bildirim, toplu halde hayat bitirme.
// C Pointer, MALLOC
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
int row, col;
int **pd; // Elemanlari int* turunden olan bir dizinin adresi int** turundendir
int i, k;

srand((unsigned)time(NULL));

printf("Matrisiniz satir ve sutun sayisini giriniz: ");
scanf("%d%d", &row, &col);

pd = (int **) malloc(row * sizeof(int *));
if(pd == NULL){
printf("Cannot allocate memory!\n");
exit(EXIT_FAILURE);
}

for(i = 0; i < row; i++){
pd[i] = (int *) malloc(col * sizeof(int));
if(pd[i] == NULL){
printf("Cannot allocate memory!\n");
exit(EXIT_FAILURE);
}
}

for(i =0; i < row; i++)
for(k = 0; k < col; k++)
pd[i][k] = rand() % 10;


for(i =0; i < row; i++)
for(k = 0; k < col; k++)
printf("%d", pd[i][k]);
printf("\n");

for(i = 0; i < row; i++)
free(pd[i]);

free(pd);

return 0;
}
// C++
#include <cstdlib>
#include <ctime>
#include <iostream>

int main()
{
int row, col;
srand(static_cast<unsigned>(time(nullptr)));

std::cout << "Matrisiniz satir ve sutun sayisini giriniz: ";
std::cin >> row >> col;
int **pd{new int *[row]};

for(int i = 0; i < row; i++)
pd[i] = new int[col];

for(int i =0; i < row; i++)
for(int k = 0; k < col; k++)
pd[i][k] = rand() % 10;


for(int i = 0; i < row; i++){
for(int k = 0; k < col; k++)
std::cout << pd[i][k];
std::cout << "\n";
}

for(int i = 0; i < row; i++)
delete []pd[i];

delete[]pd;

return 0;
}

Not: endl alt satıra geçirmekten baska bufferı flush eder. http://www.cplusplus.com/reference/ostream/endl/


Extern C Bildirimi

  • C++ programcısı C++ kaynak dosyasında C dilinde derlenmiş bir dosyayı çağırıyor. Mesela C dilinde yazdığımız bir isprime fonksiyonunu derledim ve sonra C++ projesi oluşturuyorum. Compile time’da sorun yok ama runtime da problem oluyor. Bu linkera bağlı bir problem

  • C dilinde function overloading yok. C dilinde function overloading olmadığı için C de derleyiciler dışsal referans olarak bir fonksiyona atıfta bulunduklarında(bir fonksiyon inline değil ise derleyici object koda fonk çağırıldığına yönelik bir isim yazıyor özel bir notasyonla linker için yazıyor).

  • C derleyicileri bir fonksiyon çağrısını dışsal referansa dönüştürdüklerinde sadece fonksiyon isminden haraketle bir fonksiyon oluşturuyorlar.

  • C++ derleyicileri linkera overloadlardan hangisini çağırdığını anlamaları için parametrelerin türleri ve sayısını belirten bir notasyonda kullanmak zorunda

  • Bu problem, derleyiciye anlatmamız gerekiyor, bu bildirime extern c bildirimi deniyor. Bunun amacı bu fonksiyon C dilinde derlenmiştir, bu fonksiyona yapılan çağrıyı C nin notasyonuyla yaz, derleyici bulsun demek.

// kerem.h - Extern C Bildirimi
#ifdef __cplusplus
extern "C" { //extern c birkez yazip, blok icine almak
#endif

void k1(int);
void k2(int);
void k3(int);
void k4(int);

#ifdef __cplusplus
}
#endif

C++ Interface Tasarımı Hakkında

  • Client’ın ihtiyaç duymuyacağı bir ismi header dosyasına koymayın. Tasarım her zaman dıştan içe doğru yapılır.(interface tasarımı)

  • Önce başlık dosyası, sonra cpp (önce interface, sonra implemantasyon)

  • Tasarım dışardan içeriye olacak, interface’ten → implemantasyon’a