PDA

Tam Sürümünü Görmek İçin : Dinamik bellek boyunu girilen stringe göre ayarlamak?


shurzan
09/07/2005, 03:25
Öncelikle merhaba;
Forunuzda henüz yeniyim, sevgili hocam Ali beyin bu forumdan bir konuyu kaynak göstermesi ve forumdaş sckz tanımam ile sizlerleyim.
Bende sizler gibi C ve C++ gönüllüsüyüm ve öğrenme azmim her yeni konuda biraz daha artmakda fakat her zaman her kaynağa erişmek kolay olmamakda, Elimdeki kitap (Dietel & Dietel [çeviri Metin Zavrak]) ise çoğu yerde yetersiz kamakta. O yüzden bir konuda sizlerden sorunuma yardım bekliyorum şimdiden teşekkürler.

Biri dizi sınıfımız var

//-----------------------------
#ifndef DIZI_H
#define DIZI_H
class Dizi{
public:
Dizi(char *);
~Dizi(){delete [] Ptr;}
void Yaz(char *);
void Oku() const;
private:
char *Ptr;
};
#endif


//-----------------------------
#include <iostream>
#include <cstring>
#include <cassert>
#include "dizi.h"

Dizi:: Dizi(char *a)
{
Yaz(a);
}

void Dizi::Yaz(char *Str)
{
Ptr = new char[strlen(Str)+1];
assert(Ptr !=0);
strcpy(Ptr, Str);
}

void Dizi::Oku() const
{
cout<<Ptr<<'\n';
}

int main()
{
Dizi *Str;
// işte burada ne yapacağımı bilmiyorum!
/*...........*/


Str->Oku();
return 0;
}

Girilen yazının boyu önceden bilinmiyor o yüzden Ptr için ayrılacak bellek boyu string girildikten sonra string boyu kadar yer ayırılacak
"cin.get" mi kullanacağım "cin.getline" mi yada başaka ne ayrıca Dizi sınıfın nesnesi Str için burada da bellek tahsisi yapmak gerekir mi? ve standart girişten alınan string için tampon bir dizi yaratmadan yapmalıyım.
Kodlamayı ekisik yapmış olabilirim ama öğrenmek istediğimi sanırım doğru sormuş olmalıyım.
Birde standart girişten alınan string tamponlanıyormu bu konuda da biraz bilgi lütfen.
Saygılar....


sckz
09/07/2005, 06:47
bildiğim kadarıyla ne get() ne de getline() işini görür. std::string, bu olayı becerebilmesi için muhtemelen bir tampon a sahiptir. Zaten Msvc 7 de string sınıfının unsigned int tipinde _Mysize ile _Myres adında iki üyesi var (bundan başka üyeleri de var tabi).. _Myres in tuttuğu değer herzaman _Mysize dan büyük oluyo. Zaten içinde bafır mafır birsürü üye var.. Yazarken muhtemelen bunun gibi bişi olmalı..

Dizi sınıfın nesnesi Str için burada da bellek tahsisi yapmak gerekir mi?aslında Str burada bir nesne değil. Ortada henüz nesne yok. senin new ile birtane edinmen gerek.

ve standart girişten alınan string için tampon bir dizi yaratmadan yapmalıyım.bana yapmalısın gibi geliyor. Ne bileyim 10 karakterlik bir tampon oluştur.. eğer 10 karakter girildiğinde hala \n ya da space e ulaşılamamışsa, bu tamponu genişletip okumaya devam et.. zaten operator >>() fonksiyonu ilk boşluğa kadar okur.. istream::get() bildiğim kadarıyla std::string ile çalışmıyo (istream::getline() da aynı şekilde)..
(ama bunun yerine, string için, std::getline(); var.. eğer 2 parametreli halini kullanırsan \n e kadar okur.. ya da 3. bi argüman olarak bitiş karakterini verebilirsin..)

Neyse biraz da kodun üzerinde durmak istiyorum :
keşke .h dosyası içinde ~Dizi(){delete [] Ptr;} satırını yazmasaydın..ama yinede bunun bir sorun yaratçağını zannetmiyorum..

bir diğer önemli husus ise assert(); satırının hatalı olduğu.. Bu C de kullanılan bişeydi.. malloc() ile yer ayırma başarısız olursa NULL döner biz de bunu ilgili if içerisinde kontrol ederdik. ama new ile bu durum söz konusu değil.. Yani new nesne oluşturamadığı durumda NULL döndürmek yerine std::bad_alloc fırlatır (aslında sadece bellek ayırmıyoruz).. Bizde bunu catch ile yakalayıp gerekeni yaparız..

acehreli
09/07/2005, 09:42
shurzan, hosgeldin... :)

Bu isi yapmak icin std::string gibi bir sinifi, ve hatta isini ondan yararlanarak halleden std::istringstream'i kullanmak daha akillica olur. Tabii sen onlarin nasil yazildiklarini ogrenmek icin boyle bir sinif yaziyorsun.

C++ kutuphanesini kullanmak yerine alt duzey C olanaklarini kullanmanin ne kadar zor bir is oldugunu gosteren soyle bir yazi var:

http://acehreli.org/~ali/turkcecpp/cpp_ogrenmek.html

O sayfa cikmazsa, nette "Standart C++'i Yeni Bir Dil Olarak Ögrenmek" basligi altinda baska yerlerde de bulunuyor.

Yazinin Ingilizce asli da surada:

http://www.research.att.com/~bs/new_learning.pdf

O yazinin bas tarafinda tam da yapmaya calistigin isin ne kadar zor oldugu gosterilir... Cok isine yarayacagindan eminim.

Once kodun hakkinda hizlica bir yorum:

1) "Uc islevler kurali"ni unutma: bir sinifin bozucu, kopyalayici, veya atama islecinden en az birisi tanimlanmissa; digerlerinin de en azindan bildirilmesi gerekir.

Yoksa Dizi sinifi bu haliyle dogru calisamaz:


{
Dizi dizi1(bir_bellek_alani);
Dizi dizi2 = dizi1; // kopyaladik
}
// Bu noktada bir_bellek_alani iki kere delete[] edilmis olacak: tanimsiz davranis.


Yani ya kopyalayici ve atama islecini bildirecek ve derleyicinin kendisin yazmamasini saglayacagiz, ya da ikisini dogru seyi yapacak sekilde kendimiz yazacagiz. Birinci yontemi uygularsak:


class Dizi
{
// Bildiriyoruz ama tanimlamayacagiz:
Dizi(Dizi const &);
Dizi & operator= (Dizi const &);

/* ... */
};


Boylece dizi2'nin dizi1'den kopyalanmasi mumkun olmayacak ve tanimsiz davranisa dusme riskimiz olmayacak.

2) sckz'nin de dedigi gibi, new ve new[] hicbir zaman NULL dOndurmezler. Ya bellek ayirirlar ve kullanisli bir deger dondururler, ya da bellek ayiramazlar ve hic donmezler.

Bu arada, assert makrosu programcinin kendisini ve program mantigini denetlemek icin kullandigi bir aractir. Bellek yetersizligi gibi calisma zamaninda olusabilecek bir duruma uygun degildir.

3) sckz'nin de degindigi gibi, main icinde elinde bir Dizi isaretcisi var; bir Dizi yok. Ama sckz'nin dedigi gibi new ile bir tane olusturmak yerine neden otomatik bir nesneyle yetinmeyelim:


char * bir_bellek_alani = bir_sekilde_elde_et();
Dizi dizi(bir_bellek_alani); // <--- otomatik nesne


4) Yaz islevi genel erisime acik (public) oldugu icin, kullanicilar onu pes pese cagirabilirler. Bu durumda Ptr'in gosterdigi bellek hic silinmedigi icin bellek kaybi olusacaktir.

Yukarida da dedigim gibi, o yaziyi okudugun zaman Dizi sinifini alt duzey olanaklarla istedigin gibi yazabileceksin. Senin Dizi sinifinin arayuzunun bir benzerini kullanan ve C++ kutuphanesinin ust duzey olanaklarindan yararlanan bir Dizi'yi soyle yazabiliriz:


#include <string>
#include <iostream>

class Dizi
{
std::string karakterler_;

// Bu sinif hicbir kaynagi acikca kendisi
// sahiplenmedigi icin; bozucu, kopyalayici, ve
// atama islecini tanimlamamiza veya bildirmemize
// gerek yok.
//
// Bunun nedeni, derleyicinin otomatik olarak
// yazacagi bu islevlerin bu sinifta zaten dogru
// olarak calisacak olmasidir.

public:

Dizi(std::string const & kaynak)
{
Yaz(kaynak);
}

void Yaz(std::string const & yeni)
{
karakterler_ = yeni;
}

// Daha kullanisli hale getirmek icin,
// karakterlerin hangi akima gonderileceklerini de
// belirleyebilelim
void Oku(std::ostream & cikis) const
{
cikis << karakterler_ << '\n';
}
};

#include <sstream>
#include <iterator>

using namespace std;

// Sozcuk aralari tek bir bosluk olarak belirlenir
string giristen_sozcukler_oku_1(istream & giris)
{
ostringstream okunanlar;

while (giris)
{
string sozcuk;
giris >> sozcuk;
okunanlar << sozcuk << ' ';
}

return okunanlar.str();
}

// Bu islev de giristen_sozcukler_oku_1'in yaptigi isin
// aynisini yapar; ama sozcuk aralarindaki bosluklar
// '-' karakteriyle degistirilir.
//
// (Bu islevi programda kullanmiyorum.)
string giristen_sozcukler_oku_2(istream & giris)
{
ostringstream okunanlar;

copy(istream_iterator<string>(giris),
istream_iterator<string>(),
ostream_iterator<string>(okunanlar, "-"));

return okunanlar.str();
}

// Bu isi yapmanin baska yollari oldugunu da biliyorum
// ama simdi arastirmaya useniyorum :)

// string giristen_sozcukler_oku_3(istream & giris);

void bilgi_ver(string const & baslik, Dizi const & dizi)
{
cout << baslik << ": ";
dizi.Oku(cout);
}

int main()
{
Dizi dizi1("sabit dizgi");
bilgi_ver("dizi1", dizi1);

// standart giristen okuyalim
Dizi dizi2(giristen_sozcukler_oku_1(cin));
bilgi_ver("dizi2", dizi2);
}


Ali

Not 1: Mektuplarina ekledigin kod orneklerinden onceki satira

[ code ]

ve sonraki satira da

[ / code ]

yazarsan, kodundaki girintiler de korunur. Ama bosluk karakterleri konmayacak tabii ki. Yani [ karakterinden sonra hemen code gelecek; bosluk olmadan. Ayni sey [ / code ] icin de gecerli...

Not 2: Ben senin mektubunu oyle duzelttim.