Tam Sürümünü Görmek İçin : copy constructor
canadanali
25/10/2005, 23:37
copy constructor, bir metodun referans yoluyla parametresi olan bir nesne için mi oluşturulur? ya da bir nesneyi başka bir nesneye atarken???
String(const String &)
String A_nesnesi=B_nesnesi gibi....
kısacası bu konuyu pek anlamadım..konuyu aşagıdaki örnekten izah eden biri olursa sevinirim. teşekkürler
// Kopyalama Kurucusu (Copy Constructor)
#include <iostream>
#include <cstring> // string fonksiyonları için
using namespace std;
class String{ // Örnek (karakter katarı) String sınıfı
int boy; // Katarın boyu
char *icerik; // Katarın içeriği
public:
String(const char *); // Kurucu
String(const String &); // Kopyalama kurucusu
void goster(); // Katarları ekrana çıkaran üye fonksiyon
~String(); // Yok edici fonksiyon
};
//Kurucu Fonksiyon
// Parametre olarak aldığı katarı nesnenin içeriğine kopyalar
String::String(const char *gelen_veri)
{
cout<< "Kurucu çalıştı" << endl;
boy = strlen(gelen_veri); // gelen katarın boyu hesaplandı
icerik = new char[boy +1]; // icerik için yer ayrıldı, +1 null icin
strcpy(icerik, gelen_veri); // gelen veri icerik'in gosterdigi yere kopyalanıyor
}
// Kopyalama kurucusu
String::String(const String &gelen_nesne)
{
cout<< "Kopyalama Kurucusu çalıştı" << endl;
boy = gelen_nesne.boy;
icerik = new char[boy + 1]; // +1 null karakteri icin
strcpy(icerik, gelen_nesne.icerik);
}
void String::goster()
{
cout<< icerik << ", " << boy << endl; // Katar ve boyu ekrana yazılıyor
}
// Yok edici Fonksiyon
// icerik tarafından isaret edilen bellek geri veriliyor
String::~String()
{
cout<< "Yok edici çalıştı" << endl;
delete[] icerik;
}
//-------- Ana Program ----------------
int main() // Ana fonksiyon
{
String string1("Katar 1");
string1.goster();
String diger = string1; // Kopyalanma kurucusu çalisir
String baska(string1); // Kopyalanma kurucusu çalisir
diger.goster();
baska.goster();
return 0;
}
acehreli
26/10/2005, 00:30
Oncelikle hatirlatmak istiyorum: mektubun icine kod yerlestirirken, kodun basina
[ c o d e ]
sonuna da
[ / c o d e ]
belirtecleri koyunca, kod baska bir pencere icinde ve metin duzeni korunarak gosteriliyor. Ama tabii karakterlerin aralarinda bosluk karakterleri olmayacak. Ben gorunebilsin diye bosluklu yazdim. Neyse...
"referans yoluyla" derken ne kasdettiginden emin degilim, ama kopyalama kurucusunun (ben bazen kisaca "kopyalayici" da diyorum) aciklamasi cok basit: Bir nesneyi, baska bir nesneden kopyalayarak olustururken kullanilan islevdir.
"Baska bir nesneye atarken" dedigin yerde de bir uyari gerekiyor: atama isleci ile kopyalama kurucusu farklidir. Atama, zaten var olan bir nesneye baska bir nesneyi atarken kullanilir; kopyalayici ise yeni bir nesne kurarken.
Bu ornegi nereden aldigini soyleyebilir misin? "Kurucu" sozcugunu yayan kisilerden birisi olarak; baska kodlarda da kullanildigini gormek hosuma gitti :)
Kodun icine [Ali] ile baslayan aciklamalar koyuyorum:
// Kopyalama Kurucusu (Copy Constructor)
#include <iostream>
#include <cstring> // string fonksiyonlar? için
// [Ali] Yukaridaki aciklama bir C++ programinda biraz garip olmus;
// cunku oradaki 'string', C++'in std::string turu degil; C'den gelen
// char* tUrUndeki dizgileri kastediyor.
using namespace std;
class String{ // Örnek (karakter katar?) String s?n?f?
int boy; // Katar?n boyu
char *icerik; // Katar?n içerig(i
public:
String(const char *); // Kurucu
String(const String &); // Kopyalama kurucusu
// [Ali] Bu sinifta buyuk bir hata var: Bu sinif "3 islevler"
// kuralina uymamis. O kurala gore; eger bir sinifin 3 ozel
// islevinden en az birisi tanimlanmissa, digerlerinin de en azindan
// bildirilmesi gerekir.
//
// Burada bahsedilen 3 ozel islev; kopyalama kurucusu, atama isleci,
// ve bozucudur (yok edici).
//
// Bu sinif bu haliyle tehlikeli, cunku atama isleci olarak
// derleyicinin gercekleyecegi islev kullanilacak. Derleyicinin
// yazdigi o islev, 'icerik' ogesinin gerektirdigi ozenden
// yoksundur.
//
// Duzeltmek icin su 4 satiri ekliyorum:
private: // [Ali]
// [Ali] Bildiriyoruz ama tanimlamayacagiz:
String & operator= (const String &);
public: // [Ali]
void goster(); // Katarlar? ekrana ç?karan üye fonksiyon
~String(); // Yok edici fonksiyon
};
/* ... */
int main() // Ana fonksiyon
{
// [Ali] Burada char* alan kurucu cagriliyor
String string1("Katar 1");
string1.goster();
// [Ali] 'diger' adli yeni bir nesne 'string1' adli nesneden
// kopyalanarak kuruluyor.
String diger = string1; // Kopyalanma kurucusu çalisir
// [Ali] Ayni sekilde, 'baska' adli yeni bir nesne 'string1' adli
// nesneden kopyalanarak kuruluyor.
//
// Yukarida da soyle yazabilirdik, ayni sey olurdu:
//
// String diger(string1);
//
String baska(string1); // Kopyalanma kurucusu çalisir
diger.goster();
baska.goster();
return 0;
}
Ali
myavuzselim
26/10/2005, 00:48
Bilgigim kadariyla:
String baska(string1); ile String baska = string1; ayni isi yapiyor, yani copy constructor'u calistiriyor.
Ama String baska; baska = string1; dediginde ilkinde String() contructor'u ile nesne yaratiliyor, ikincisinde de operator= cagriliyor.
Soyle test edebilirsin (not: operator=, String() falan filan fonksiyonlarini dogru sekilde yazip yazmadigimi bilmiyorum, ama bu bizi zaten fazla ilgilendirmiyor galiba)
// Kopyalama Kurucusu (Copy Constructor)
#include <iostream>
#include <cstring> // string fonksiyonlari icin
using namespace std;
class String
{ // Ornek (karakter katari) String sinifi
int boy; // Katarin boyu
char *icerik; // Katarin icerigi
public:
String (); // default constructor
String (const char *); // Kurucu
String (const String &); // Kopyalama kurucusu
void goster (); // Katarlari ekrana cikaran Uye fonksiyon
String & operator= (const String &);
~String (); // Yok edici fonksiyon
};
String::String ()
{
cout << "default constructor cagrildi." << endl;
icerik = new char[1];
icerik[0] = '\0';
boy = 0;
}
String & String::operator= (const String & str)
{
cout << "operator= cagrildi." << endl;
boy = str.boy;
icerik = new char[boy + 1]; // +1 null karakteri icin
strcpy (icerik, str.icerik);
return *this;
}
//Kurucu Fonksiyon
// Parametre olarak aldigi katari nesnenin icerigine kopyalar
String::String (const char *gelen_veri)
{
cout << "Kurucu calisti" << endl;
boy = strlen (gelen_veri); // gelen katarin boyu hesaplandi
icerik = new char[boy + 1]; // icerik icin yer ayrildi, +1 null icin
strcpy (icerik, gelen_veri); // gelen veri icerik'in gosterdigi yere kopyalaniyor
}
// Kopyalama kurucusu
String::String (const String & gelen_nesne)
{
cout << "Kopyalama Kurucusu calisti" << endl;
boy = gelen_nesne.boy;
icerik = new char[boy + 1]; // +1 null karakteri icin
strcpy (icerik, gelen_nesne.icerik);
}
void
String::goster ()
{
cout << "\"" << icerik << "\", " << boy << endl; // Katar ve boyu ekrana yaziliyor
}
// Yok edici Fonksiyon
// icerik tarafindan isaret edilen bellek geri veriliyor
String::~String ()
{
cout << "Yok edici calisti" << endl;
delete[]icerik;
}
//-------- Ana Program ----------------
int
main () // Ana fonksiyon
{
String string1 ("Katar 1");
string1.goster ();
String diger = string1; // Kopyalanma kurucusu calisir
diger.goster ();
String baska (string1); // Kopyalanma kurucusu calisir
baska.goster ();
String yine; // default constructor calisir
yine = string1; // operator= calisir
yine.goster();
String aaa;
aaa.goster();
return 0;
}
kopya kurucular biraz bulanık şeyler.
bir kurucu çağrıldığında nesneyi iyi bir duruma getirmeli.
bu varsayılan bir "iyi durum" olabilir ya da kurucuya aktarılan parametrelerle belirlenebilir.
mesela klavye üretmek istiyorum.
Klavye k = new Klavye();
k nın rengi , dili , multimedya tuşlarının olup olamadığı belli değil.
varsayılan olarak beyaz ,bir standart Q-türkçe klavye üretebilirim.
ya da Klavye k = new Klavye ( layout, color ) gibi bir yöntem kullanabilirim.
birisi bana bu parametreleri vereceğine ,
" şu klavyenin aynından üret " diyebilir.
Klavye l = new Klavye( k ); // kopya kurucu çalıştı.
peki klavye değilde , bir otomobil olsaydı
ve ben plakay™ da kopyalasaydım ne olacakdı .
bu tümüyle kopya kurucudan , kullanıcının beklentisi ile ilgili.
bir veri tabanı bu nesnenin bir kopyasını istiyorsa, plaka kopyalanmalı.
ama bir fabrika bu arabayı üretmek için bu fonksiyonu çağırıyorsa kopyalanmamalı.
( aynısı klavye ile de ilgilendirilebilir. seri numarası filan.. )
ya da "başka bir nesneye bir referans içeren" bir nesnenin kopyalanmasında ne yapmam gerekli.
( mesela bir node un kopyası, onun ait olduğu bağlı listeye mi ait olmalı ,
ya da tüm listenin ayrı bir kopyası mı oluşturulmalı
ya da yeni nodun işaret ettiği nodlarla bağı mı kırılmalı.)
a kişisi , b kişisinin klonu ve hücrelerine kadar aynılar diyelim.
fakat a nın anne ve babası, b nin anne ve babası değildir.
sonuçta, nesne için bir tane sınırlı kopya kurucu yerine,birden çok fonksiyon düşünülebilir. ( factory metodlar)
kopya kurucuda olduğu gibi ad sınırlaması olmadığı için fonksiyon adları daha anlamlı olabilir.
canadanali
26/10/2005, 22:43
bu örneği buzluca.com dan aldım. ben daha çok bellekteki işlemleri merak ediyorum. copy constructor kullanılmasının nedeni parametre olarak kullanılan nesnenin orjinal halinin korunması değil mi?
aşagıdaki örnekte uyarı vermesinin nedeni nedir?
#include <iostream>
#include <cstring> // string fonksiyonlari icin
using namespace std;
class String
{ // Ornek (karakter katari) String sinifi
int boy; // Katarin boyu
char *icerik; // Katarin icerigi
public:
String (); // default constructor
String (const char *); // Kurucu
void goster (); // Katarlari ekrana cikaran Uye fonksiyon
~String (); // Yok edici fonksiyon
};
String::String ()
{
cout << "default constructor cagrildi." << endl;
icerik = new char[1];
icerik[0] = '\0';
boy = 0;
}
//Kurucu Fonksiyon
// Parametre olarak aldigi katari nesnenin icerigine kopyalar
String::String (const char *gelen_veri)
{
cout << "Kurucu calisti" << endl;
boy = strlen (gelen_veri); // gelen katarin boyu hesaplandi
icerik = new char[boy + 1]; // icerik icin yer ayrildi, +1 null icin
strcpy (icerik, gelen_veri); // gelen veri icerik'in gosterdigi yere kopyalaniyor
}
void String::goster ()
{
cout << "\"" << icerik << "\", " << boy << endl; // Katar ve boyu ekrana yaziliyor
}
// Yok edici Fonksiyon
// icerik tarafindan isaret edilen bellek geri veriliyor
String::~String ()
{
cout << "Yok edici calisti" << endl;
delete[]icerik;
}
//-------- Ana Program ----------------
int
main () // Ana fonksiyon
{
String string1 ("Katar 1");
string1.goster ();
String diger = string1;
diger.goster ();
return 0;
}
canadanali
26/10/2005, 23:38
String string1 ("Katar 1"); // burda bizim kurucu çalışıyor
string1.goster (); //ekrana "Katar1",7 basıyor.
String diger = string1;// burda diger için bizim kurucu çalışmıyor.
diger.goster ();
//sonrasında yıkıcı 2 kez çalışıyor ve sanırım sorun burada.
acehreli
27/10/2005, 00:49
canadanali, verdigin ornek daha once de soyledigim gibi yanlis. Uc islevler (the rule of three) kuralina uymuyor. Bu ornegi fazla dikkate alma. Belki de karisikliklarin nedeni, bu ornekten fazla beklentin oldugu icindir.
Kopyalayicinin nedeni, "parametrenin orijinal halinin korunmasi" degildir. O gibi durumlarda calisir ama nedeni bu degildir.
Kopyalayicinin nedeni cok daha basittir: zaten var olan bir nesnenin bir kopyasini almaktir. Baska nedeni yoktur. Herhangi bir nesnenin kopyalandigi her durumda, ornegin senin de bahsettigin parametre gonderirken de kullanilir.
Sordugun uyarinin neden oldugunu bilemiyorum cunku ne uyarisi aldigini soylemiyorsun. :(
Son mesajinda
String diger = string1;// burda diger için bizim kurucu çalismiyor.
diyorsun. Cunku orada "zaten var olan bir nesnenin bir kopyasi aliniyor." O yuzden kopyalayici cagriliyor. Kaldi ki, bizim kurucunun aldigi bir parametre var: const char *. Yukaridaki kullanimda 'const char *' olarak kullanilacak bir sey goremiyorum. Neden bizim kurucu cagrilsin...
Kopyalayici, atama isleci ve bozucu ozel islevlerdir: eger biz yazmazsak (aslinda bildirmezsek), onlari derleyici bizim icin yazar. Derleyicinin yazacagi islevler ise String sinifi gibi kendi kaynagina sahip siniflara uygun degildir. Onun icin bizim yazmamiz gerekir.
Gordugun iki yikici da iki nesne icin cagriliyor: string1 ve diger. C++, omru biten her nesnenin bozucusunun cagrilacagini garanti eder; onu goruyorsun.
String sinifinin hatasiz yazilmis olmasi icin, kopyalayici ve atama isleclerinin en azindan private olarak bildirilmeleri gerekir. Soyle:
class String
{
int boy;
char *icerik;
// Bunlari bildiriyoruz ama tanimlamayacagiz
String (const String &);
String & operator= (const String &);
public:
String ();
String (const char *);
void goster ();
~String ();
};
Eger o islevler gercekten gerekiyorsa, o zaman public olarak bildirilmeleri ve soyle yazilmalari uygun olur:
// Aslinda buyukluk icin size_t tUrU uygundur ama ornekte int
// kullanildigi icin oyle devam edecegim.
//
// Bunun dOndUrdugu kaynagin delete[] ile geri verilmesi gerekir.
char * dizgi_kopyala_new_ile(char const * kaynak, int boy)
{
char * hedef = new char[boy + 1];
strcpy(hedef, kaynak);
return hedef;
}
class String
{
/* ... */
public:
String (const String & diger)
:
boy(diger.boy),
icerik(dizgi_kopyala_new_ile(diger.icerik, diger.boy))
{
cout << "kopyalayici calisti\n";
}
// operator= islecinin geleneksel yazimi boyledir; aksine bir neden
// olmadikca her zaman (her zaman!) boyle yazin.
String & operator= (const String & diger)
{
String gecici(diger);
this->swap(gecici);
cout << "operator= calisti\n";
return *this;
}
// Bu nesne ile verilen nesneyi degis tokus eder
void swap(String & diger)
{
std::swap(boy, diger.boy);
std::swap(icerik, diger.icerik);
}
/* ... */
};
Ali
myavuzselim
27/10/2005, 03:08
Kopyalayici, atama isleci ve bozucu ozel islevlerdir: eger biz yazmazsak (aslinda bildirmezsek), onlari derleyici bizim icin yazar. Derleyicinin yazacagi islevler ise String sinifi gibi kendi kaynagina sahip siniflara uygun degildir. Onun icin bizim yazmamiz gerekir.
Gordugun iki yikici da iki nesne icin cagriliyor: string1 ve diger. C++, omru biten her nesnenin bozucusunun cagrilacagini garanti eder; onu goruyorsun.
Tahminimce oradaki hata soyle olusmus: derleyici kendi sagladigi copy-constructor'da icerik'i sadece pointer olarak kopyalamis. Programin sonunda 2 kere icerik'e delete[] uygulanmis. Ilkinden sonra icerik bir dangling pointer oluyor.
Bu linux (gcc)'de hata veriyor, windows (mingw)'de vermiyor. Ama her halukarda hatali.
mr1yh1 : Teşekkürler. Çok hoş bi yazı olmuş. Yalnız araya Java karışmış :)
mr1yh1 : Teşekkürler. Çok hoş bi yazı olmuş. Yalnız araya Java karışmış :)
evet , karıştı :D effective java da bahsediliyordu,
önce javada referans ve doğrudan bellek erişimi olmadığı için kıskançlıklarından yapıyolar zannettim ama sonradan fikir mantıklı geldi.
Arkantos
09/11/2005, 09:11
Gerçekten güzel bir konuymuş. Ben de şimdi okumaya başladım. Henüz sona gelmedim.
Anladığım kadarıyla kopyalama yapıcısını (copy constructor) sınıfımıza ait veri üyelerinden birisi bir gösterge türünde olduğunda kullanıyoruz. Yani sınıfımız kaynakları yönetiyorsa (örneğin dinamik olarak bellekten yer ayırmak gerekiyorsa) bu durumda kendi kopyalama kurucusuna ihtiyaç duyuyoruz. Böyle dedim çünkü anladığım kadarıyla biz kendi kopyalama kurucumuzu tanımlamasak bile derleyici varsayılan kurucu (default constructor) (parametre almayan) kurucuya sahip her sınıf için bir kopyalama kurucusu oluşturuyor.
Bir de o henüz bu örneği deneyemedim ama bu örneğe benzer bir örnekte ben de uyarı aldım. Sanırım "duplicate deletion of pointers" gibi bir şeydi. Bunu MingW'de vermiyor ama Linux gcc veriyor. VC++ 7.1 ile de program derlendikten sonra direkt çakılıyor!
Bir de yine sınıfımızın veri üyelerinden biri gösterge türünde (dinamik olarak bellekten yer ayırıyorsak) ise ve global bir fonksiyonumuz varsa ve bu fonksiyona nesneyi parametre olarak gönderdiğimiz durumlarda kopyalayıcıyı kullanmamız kaçınılmaz oluyor. Çünkü derleyici bu fonksiyona parametre olarak gönderdiğimiz nesnenin birebir kopyasını gönderiyor. Gönderilen bu kopyadaki gösterge de yine bu asıl nesneye işaret ettiği için global fonksiyonumuzun kapsamı dışına çıkıldığında nesne yokediliyor. Böylece olmayan bir nesneye işaret eden bir göstergeye sahip oluyoruz!? (Ya da ben mi yanlış anlamışım) Ve program çakılıyor.
Dikkat ettiğim bir diğer nokta Üç İşlevler Kuralına uyduğumuzda (yani bu özel işlevleri en azından bildirdiğimizde) derleyici --programı derleyip programın çakılması yerine-- hatalı programı derlemeyip yakınıyor ve bir hata veriyor.
acehreli
09/11/2005, 09:35
Bir kac not...
1) Gosterge turunde bir ogeye sahip olmak, kopyalama kurucusunu yazmak icin bir neden olabilir. Ama aslinda yazip yazmama kararini vermek cok daha kolaydir. Bunun icin "uc islevler" kuralini uygulayabiliriz. Hatirlatiyorum:
Uc islevler kurali: Eger bir sinifin uc ozel islevinden (bozucu, kopyalayici, operator=) herhangi birisini yazmissak; digerlerini de ya acikca tanimlamaliyiz; ya da derleyici tanimlamasin diye private olarak bildirmeliyiz.
2) Derleyici, biz tanimlamadigimiz zaman her sinif icin kopyalayici olusturur. Varsayilan kurucunun bu konuyla bir ilgisi yok.
Belki de su kuralla karistiriyorsun: Herhangi bir kurucu tanimlarsak derleyici varsayilan kurucuyu olusturmaz. Ama bunun kopyalayiciyla bir ilgisi yok...
Cogunu guzel aciklamissin... :)
Ali
Arkantos
10/11/2005, 11:49
O zaman arkadaşın yazdığı programın düzeltilmiş hali şu şekilde olmuş oldu:
#include <iostream>
#include <cstring>
char * dizgiKopyala (char const * eski, int boy)
{
char * yeni = new char [boy + 1];
strcpy (yeni, eski);
return yeni;
}
class String
{
public:
String ();
String (const char *);
String (const String & );
String & operator= (const String & );
~String ();
void degisTokusYap (String & );
void goster ();
private:
char * icerik_;
int boy_;
};
String::String ()
: boy_ (0)
{
icerik_ = new char [boy_ + 1];
icerik_ [0] = '\0';
}
String::String (const char * girilenler)
: boy_ (strlen (girilenler))
{
icerik_ = new char [boy_ + 1];
strcpy (icerik_, girilenler);
}
String::String (const String & diger)
: boy_ (diger.boy_),
icerik_ (dizgiKopyala (diger.icerik_, diger.boy_))
{
}
String & String::operator= (const String & diger)
{
String gecici (diger);
this->degisTokusYap (gecici);
return *this;
}
String::~String ()
{
delete [] icerik_;
}
void String::degisTokusYap (String & diger)
{
std::swap (boy_, diger.boy_);
std::swap (icerik_, diger.icerik_);
}
void String::goster ()
{
std::cout << icerik_ << " " << boy_ << '\n';
}
int main ()
{
String isim;
isim = "Erdem";
isim.goster ();
String baskaIsim (isim);
baskaIsim.goster ();
}
Tabi kendi string sınıfımızı yazmaya çalışsaydık bunun gibi mi yazardık bilemiyorum :)
acehreli
10/11/2005, 18:43
Bence olmus :) Kendi string sinifimizi herhalde yine boyle yazardik. Bazi string gerceklemeleri 'reference counted' COW (copy on write) string'ler kullanirlar.
Oyle yapinca performansin artacagi dusunuluyordu. Bu yontemde kopyalanan nesneler icin ayri ayri yer ayrilmaz; hepsi ayni icerigi gosterir. Ancak birisinde degisiklik yapilacagi zaman (write) o nesne icin kendi yeri ayrilir.
Tabii bu yontemin dogru calismasi icin gereken yeni ogeler ve islemler kazanilacagi umulan performansin bir bolumunu tuketir. Ek olarak, COW string'ler multithread destegi konusunda da fazla gucluk cikartirlar.
Sonucta COW string'lerin o kadar da iyi bir fikir olmadigina karar verilmistir. O yuzden, verdigin string bence iyi bir baslangic olmus :)
Ali
canadanali
13/11/2005, 20:56
öncelikle arkadaşlar yardımlar için teşekkürler..aşağıdaki örnekte herhangi bir hata var mı?
#include<iostream>
using namespace std;
class Kisi
{
int yas;
char *isim;
public:
Kisi();
Kisi(char *,int);
Kisi(const Kisi &);
Kisi &operator=(const Kisi &);
~Kisi();
void Goster();
//int YasAl()const{return yas;}
};
Kisi::Kisi()
{
cout<<"Default Kurucu calisti...."<<endl;
}
Kisi& Kisi::operator=(const Kisi &gelen_nesne)
{
if(this!=&gelen_nesne){
yas=gelen_nesne.yas;
cout<<"atama operatoru calisti"<<endl;
isim=new char[20];
strcpy(isim,gelen_nesne.isim);
cout<<"gelen_nesne="<<&gelen_nesne<<endl;
cout<<"this="<<this<<endl;
}
return *this;
}
Kisi::Kisi(char *name,int age)
{
cout<<"Parametreli Kurucu calisti...."<<endl;
isim=new char[10];
strcpy(isim,name);
yas=age;
}
void Kisi::Goster()
{
cout<<isim<< " " <<yas<<endl;
}
Kisi::Kisi(const Kisi &digerKisi)
{
cout<<"kopya kurucusu calisti...."<<endl;
yas=digerKisi.yas;
isim=new char[10];
strcpy(isim,digerKisi.isim);
cout<<"diger_kisi nesnesinin adresi="<<&digerKisi<<endl;
}
Kisi::~Kisi()
{
delete[] isim;
cout<<"Yikici calisti"<<endl;
}
int main()
{
Kisi k1("burak",21);
cout<<"k1 nesnesinin adresi="<<&k1<<endl;
k1.Goster();
Kisi k2(k1);
k2=k1;
cout<<"k2 nesnesinin adresi="<<&k2<<endl;
k2.Goster();
return 0;
}
acehreli
13/11/2005, 23:53
1) std::string yerine C dizisi kullaniyorsun. Buna gerek var mi?
2) Varsayilan kurucuda hata var: ne isim ne de yas dogru deger aliyor.
3) operator= islecini neden oyle yazdin? Bu konuyu sen actin degil mi; yazdiklarimizin dogal olarak hep senin icin oldugunu dusunebilirsin. Yukarida operator= islecinin nasil yazilacagini once ben gosterdim (swap ile); sonra Arkantos Turkce duyarliligi gostererek (degisTokusYap ile) tekrar yazdi. Ama sen baska kaynaklardan ogrendigin this!=&gelen_nesne yontemini kullandin.
this!=&gelen_nesne yontemini kullanan kaynaklarin ya cok eski, ya da kendilerini yenilemeyen ve dogrulari arastirmaya deger vermeyen kaynaklar olduklarini bil.
[Not: Senin Kisi sinifinda oyle yapmanin bir zarari yok; cunku Kisi'nin ogelerinin hepsi temel turler. Yoksa genel olarak, her zaman icin (her zaman icin) degisTokusYap kullanan yontemi uygula.]
4) Kopyalarken (ve atarken) 20 ve 10 gibi garip sayilar kullaniyorsun. Onun yerine strlen islevini kullanmak isteyebilirsin. (Tabii eger std::string kullanirsan boyle seylere hic gerek kalmaz.)
5) Goster islevin const degil... Eger oyle yaparsan sabit Kisi nesnelerinin yazdiramazsin; bu da cok sacma olurdu degil mi...
Eger ogelerini hep kendi baslarinin cagresine bakabilen turler olarak secersen; yani kopyalanabilen, atanabilen, ve duzgunce bozulabilen turlerse; hic kopyalayici filan yazmaya gerek kalmaz. Bak bu sinif seninkinin esdegeri ama hem zaten dogru calisiyor, hem de daha az satir yazdigim icin hata riski de daha az:
#include <iostream>
#include <string>
using namespace std;
// Derleyicinin yazdigi kurucu, operator=, ve bozucu islevleri
// bu sinif icin zaten dogru calisir. Super! :)
class Kisi
{
int yas_;
string isim_;
public:
Kisi();
Kisi(string const & isim, int yas);
// 1) Kullanisli olsun diye nereye gosterilecegini de
// bildirebiliyoruz
//
// 2) Islevi sabit olarak belirliyoruz
void Goster(ostream & cikis) const;
};
Kisi::Kisi()
:
yas_(0),
isim_()
{}
Kisi::Kisi(string const & isim, int yas)
:
yas_(yas),
isim_(isim)
{}
void Kisi::Goster(ostream & cikis) const
{
cikis << isim_ << ' ' << yas_ << '\n';
}
int main()
{
Kisi k1("burak", 21);
cout << "k1 nesnesinin adresi=" << &k1 << endl;
k1.Goster(cout);
Kisi k2(k1);
k2 = k1;
cout << "k2 nesnesinin adresi=" << &k2 << endl;
k2.Goster(cout);
return 0;
}
Ali
Forum Yazılımı : vBulletin v3.6.8, Copyright ©2000-2008, Jelsoft Enterprises Ltd.