Top Banner
SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ Yılmaz Kılıçaslan
26

SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

Dec 30, 2015

Download

Documents

lev-fletcher

SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ. Yılmaz Kılıçaslan. Sunum Planı. Bu derste aşağıdaki konuları inceleyeceğiz: “Free store” Atama operatörü this işaretçisi. C ve C++’da Dinamik Bellek Yönetimi. BELLEK YÖNETİMİ • - PowerPoint PPT Presentation
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

Yılmaz Kılıçaslan

Page 2: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

Sunum Planı

Bu derste aşağıdaki konuları inceleyeceğiz:

– “Free store”

– Atama operatörü

– this işaretçisi

2

Page 3: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

C ve C++’da Dinamik Bellek Yönetimi

3

BELLEK YÖNETİMİ <-uzun_ömürlü, -esnek>

 

STATİK OTOMATİK <+uzun_ömürlü, -esnek> <-uzun_ömürlü, +esnek>

• • 

• DİNAMİK

<+uzun_ömürlü, +esnek>

Page 4: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Heap” C’de çalışma zamanında alınıp kullanılabilen bellek bölgesine “heap” denir. C’de heap’ten bellek istemek için malloc fonksiyonu kullanılır:

struct t *t_ptr;

t_ptr = (struct t *) malloc(sizeof(struct t));

C++ için malloc fonksiyonu istenmeyen sonuçlar üretebilir. Neden?

//Ornek

Tarih *tarihPtr;

int i;

tarihPtr = (Tarih *)malloc(sizeof(Tarih));

i = tarihPtr->aySoyle(); //Tanimsiz ay degeri //dondurur

4

Page 5: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Free Store” - 1

C++ “free store” olarak adlandırılan bir bellek bölgesinden dinamik olarak nesne yaratmak ya da yok etmek için bellek kullanımına izin verir.

Tarih *Ptr1, *Ptr2;

int i;

Ptr1 = new Tarih; //Varsayilan yap. fonk. cagrilir

i = Ptr1->aySoyle(); //1 (varsayilan deger) doner

Ptr2 = new Tarih(2,15,1985);//Yap. Fonk. cagrilir

i = Ptr2->aySoyle(); //3 doner

5

Page 6: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Free Store” - 2

Derleyici new operatörünün döndürdüğü işaretçinin kendisi için bellek ayrılan nesne için olup olmadığını kontrol eder:

void *Ptr;

Ptr = new Tarih; // Tip uyumsuzluğu

6

Page 7: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“delete” - 1

Nasıl malloc fonksiyonunun eşleniği olan bir free fonksiyonu varsa, new operatörünün de eşleniği olan bir delete operatörü vardır.

delete operatörü ayrılan bellek bölgelerini daha sonra kullanılabilmek üzere “free store” bölgesine iade eder:

Tarih *Ptr1;

İnt i;

Ptr1 = new Tarih(3, 15, 1985);

İ = Ptr1->aySoyle();

delete Ptr1;

7

Page 8: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“delete” - 2

delete operatörü belleği geri vermeden önce otomatik olarak yıkıcı fonksiyonu çağırır.

delete operatörünü yalnızca new ile döndürülen işaretçilere ve yalnızca bir kez uygulamalısınız.

delete operatörünü, 0 değerli bir işaretçiye (“null pointer”) uygulayabilirsiniz.

8

Page 9: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Free store” ve diğer veri tipleri - 1

new ve delete operatörlerini yalnızca sınıflarla değil diğer derleyici ile birlikte gelen veri tipleri ile de kullanabilirsiniz:

// Ornek 1

int *ip;

ip = new int;

// ...

delete ip;

9

Page 10: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Free store” ve diğer veri tipleri - 2

// Ornek 2

int uzunluk;

char *cp;

// uzunluk degiskenine bir deger atanir

cp = new char[uzunluk];

// ...

delete [] cp;

10

Page 11: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

“Free store” ve diğer veri tipleri - 3

// Ornek 3

int (*matrix)[10];

int boyut;

// boyut degiskenine bir deger atanir

matrix = new int[boyut][10];

// ...

delete [] matrix;

11

Page 12: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 1 new ve delete operatörlerini bir sınıfın eleman fonksiyonları içinde kullanabilirsiniz. Örnek: Her nesnesinin bir karakter katarı içereceği bir String sınıfı.

#include <iostream>;#include <cstring>;class String{ public:

String();String( const char *s );String( char c, int n );void belirle( int index, char yeniK );char soyle( int index ) const;int uzunlukSoyle() const { return uzunluk; }void goruntule() const { cout << buf; }~String();

private:int uzunluk;char *buf; };

12

Page 13: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 2

// Varsayilan Yapici FonksiyonString::String(){ buf = 0;

uzunluk = 0; }

// const char * alan Yapici FonksiyonString::String( const char * s ){ uzunluk = strlen( s );

buf = new char[uzunluk + 1]; strcpy( buf, s ); }

// char ve int alan Yapici FonksiyonString::String( char c, int n ){ uzunluk = n;

buf = new char[uzunluk + 1]; memset( buf, c, uzunluk ); buf[uzunluk] = ‘\0’; }

13

Page 14: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 3

// String içinde bir karakterin belirlenmesivoid String::belirle( int index, char yeniK){ if( (index > 0) && (index <= uzunluk) ) buf[index - 1] = yeniK ; }

// String içinden bir karakterin okunmasichar String::soyle( int index ) const{ if( (index > 0) && (index <= uzunluk) ) return buf[index - 1]; else

return 0; }

// Yikici FonksiyonString::~String(){ delete [] buf; }

14

Page 15: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 4

int main(){ String string1(“birinci Karakter Katari”);

string1.belirle(1, ‘B’); return 0;

}

15

Page 16: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 5 Her String nesnesi, birisi uzunluk ve buf eleman sahalarını içeren ve diğeri

karakterlerin kendilerini depolayan iki bloktan oluşur. Nesnelerin içeriğini dinamik olarak değiştirmek mümkündür:

void String::ekle( const char *ek ){ char *temp;

uzunluk += strlen( ek );temp = new char[uzunluk + 1];strcpy( temp, buf );strcat( temp, ek );delete [] buf;

buf = temp; }

int main(){ String string1(“birinci karakter katari”);

string1.ekle(“ ve devami”); return 0; }

16

Page 17: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 6

Bir String nesnesi kapsam alanının dışına çıkınca, uzunluk ve buf değerlerinin içeren bellek bloğu otomatik olarak serbest bırakılır.

Fakat, karakter bloğu new ile alındığı için, açıkça serbest bırakılmalıdır. delete operatörünü kullanan yıkıcı fonksiyon bu işlevi yerine getirir.

Yine de String sınıfı bazı problemlere potansiyel olarak açıktır. main fonksiyonuna aşağıdaki kod parçasını eklediğimizi varsayalım:

String string2(“ikinci karakter katari”); string2 = string1;

Bir nesnenin bir diğerine atanmasını istediğimizde derleyici eleman bazında atama yapar. Yani, yukarıdaki atama deyimi aşağıdaki koda denktir:

string2.uzunluk = string1.uzunluk; string2.buf = string1.buf;

Böyle bir atama işlemi sonucunda doğabilecek problemleri belirleyin.17

Page 18: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

İşaretçi Elemanlı Sınıflar - 7

Bir String nesnesini diğerine doğrudan atamamız neticesinde karşılaşacağımız problemler şunlardır:– String nesnelerinden herhangi birine yapılacak bir değişiklik

diğerini de etkileyecektir; bu muhtemelen arzu edilir bir durum olmayacaktır.

– string1 ve string2 nesnelerinin buf işaretçisi yıkıcı fonksiyonları üzerinden ayrı ayrı silinecektir. İşaretçiler aynı değeri taşıdığı için bu tahmin edilemeyen sorunlara yol açabilir.

– “ikinci karakter katari” değerini taşıyan string2 nesnesinin orijinal ‘buffer’ bloğu serbest bırakılmadan kaybolmuştur.

18

Page 19: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

ATAMA OPERATÖRÜ - 1 Nasıl fonksiyon isimleri aşırı yüklenebiliyorsa (gerçekte birer fonksiyon olan) operatör

isimleri de aşırı yüklenebilir. Aşağıda atama operatörünün aşırı yüklenmesini görmekteyiz:

#include <iostream> #include <cstring>

class String{ public:

String();String( const char *s );String( char c, int n );void operator=( const String &digeri );

// ... };void String::operator=(const String &digeri){ uzunluk = digeri.uzunluk; delete [] buf; buf = new char[uzunluk+1];

strcpy( buf, digeri.buf ); }

19

Page 20: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

ATAMA OPERATÖRÜ - 2int main()

{ String string1(“birinci karakter katari” ); string1.goruntule(); cout << ‘\n’;

String string2(“ikinci karakter katari” ); string2.goruntule(); cout << ‘\n’;

string2 = string1; string2.goruntule(); cout << ‘\n’; return 0; }

Derleyici string2 = string1;

deyimini aşağıdaki fonksiyon çağrımı gibi yorumlayacaktır:string2.operator=( string1 );

20

Page 21: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

ATAMA OPERATÖRÜ - 3

Programcının yanlışlıkla String sınıfından bir nesneyi kendisine atadığını varsayalım:

string1 = string1;

Elbette çok az programcı böyle bir ifade yazacaktır. Fakat kendi kendine atama işlemi daha dolaylı yollardan da gerçekleştirilebilir:

String *stringPtr = &string1;// ...string1 = *stringPtr;

operator= fonksiyonunun böyle bir atama işlemi esnasında ne yapacağını belirleyin.

21

Page 22: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

this İŞARETÇİSİ - 1 this işaretçisi statik eleman fonksiyonlar haricindeki eleman fonksiyonların

erişebildiği özel bir işaretçidir: Eleman fonksiyonu çağıran nesneye işaret eder. Daha açıkçası bir eleman fonksiyonu bir nesne için çağırdığınız zaman, derleyici

önce bu nesnenin adresini this işaretçisine atar ve ardından fonksiyonu çağırır. Eleman fonksiyon sınıfın eleman sahalarına her erişiminde örtük olarak this

işaretçisini kullanır. Örneğin,

void Tarih::ayBelirle( int mn ){ ay = mn; }

// ...tarih1.ayBelirle( 3 );

biçimindeki C++ kod parçasının C karşılığı şöyle olacaktır:

void ayBelirle(Tarih *const this, int mn ){ this->ay = mn; }

// ...ayBelirle( &tarih1, 3 );

22

Page 23: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

this İŞARETÇİSİ - 2

Bir eleman fonksiyonu yazarken herhangi bir eleman sahaya erişmek için this işaretçisini açıkça kullanmak ya da fonksiyonu çağıran nesneye referansta bulunmak için *this ifadesini kullanmak meşru yöntemlerdir. Aşağıdaki örnekteki üç deyim birbirine denktir:

void Tarih::ay_goruntule(){ cout << ay; cout << this->ay; cout << (*this).ay; }

23

Page 24: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

this İŞARETÇİSİ - 3 this işaretçisi bir eleman fonksiyonu çağıran nesne ile

bu fonksiyona parametre olarak gönderilen nesnenin aynı nesneler olup olmadığını anlamak için kullanılabilir ve bu şekilde örneğin nesneyi kendisine değer olarak atama probleminden kaçınılabilir:

void String::operator=(const String &digeri){ if( &digeri == this ) return; delete [] buf; uzunluk = digeri.uzunluk; buf = new char[uzunluk+1];

strcpy( buf, digeri.buf ); }

24

Page 25: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

this İŞARETÇİSİ - 4

Hem C’de hem C++’da bir atama deyimi atananı değer olarak alan bir ifade gibi düşünülebilir. Örneğin,

i = 3;

değeri 3 olan bir ifadedir.

Bu durumun bir neticesi birden fazla atama deyiminin zincirleme olarak birbirine bağlanabilmesidir:

a = b = c; Atama operatörü sağdan birleştirmeli olduğundan yukarıdaki ifade

aşağıdakiyle denktir:

a = (b = c);

25

Page 26: SINIFLAR VE DİNAMİK BELLEK YÖNETİMİ

this İŞARETÇİSİ - 5 Bu zincirleme atama özelliğini aşırı yüklenmiş nesne atama fonksiyonunuzun da

kullanmasını istiyorsanız, fonksiyonun atama sonucunu değer olarak döndürmesini sağlamalısınız:

String &String::operator=(const String &digeri){ if( &digeri == this ) return *this; delete [] buf; uzunluk = digeri.uzunluk; buf = new char[uzunluk+1];

strcpy( buf, digeri.buf ); return *this; }

Artık cout ifadelerinin de nasıl birden fazla çıktı değeri aldığını açıklayabiliriz:

cout << a << b << c;

Aşırı yüklenmiş sola kaydırma operatörü *this değerini cout nesnesi olarak döndürmektedir.

26