PDA

Tam Sürümünü Görmek İçin : vector::erase neden nesneyi sonlandirmiyor


acehreli
04/07/2008, 18:15
Bir arkadas bana soyle bir soru sordu:

Asagidaki programin ciktisina bakarsak, vec.erase(it) yapildigi halde o erisicinin gosterdigi nesne sonlanmiyor? Neden cikista "2. Adama Silindi" yazisi tam sondan ikinci for dOngUsunde vec.erase(it) yapildigi anda olmuyor?

Kod soyle:

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <string>
#include <vector>



using namespace std;
class Adam{
private:
string Adi;
public:
int No;
Adam(){
cout << "Adam Olustu" << endl;
};
~Adam(){
cout << No << ". Adam Silindi" << endl;
};
void SetAdam(int vNo,string isim){
No = vNo;
Adi = isim;
Yaz();
};
void Yaz(){
cout << "Isim =" << Adi << endl;
cout << "No =" << No << endl;
};
void Soyle(){
cout << No << " numarali adamim. Adim =" << Adi << endl;
};

};

int main()
{
typedef boost::shared_ptr<Adam> CAdamPtr;


std::vector<CAdamPtr> vec;

CAdamPtr A1=CAdamPtr(new Adam);
A1->SetAdam(1,"fatih");
CAdamPtr A2=CAdamPtr(new Adam);
A2->SetAdam(2,"fatma");
CAdamPtr A3=CAdamPtr(new Adam);
A3->SetAdam(3,"sehide");
vec.push_back(A1);
vec.push_back(A2);
vec.push_back(A3);

cout << "Boyut =" << vec.size() << endl <<endl;
A2->SetAdam(2,"yeni isim");
cout << "Boyut =" << vec.size() << endl <<endl;
A1->Soyle();
A2->Soyle();
A3->Soyle();

std::vector<CAdamPtr>::iterator it;

for ( it=vec.begin() ; it < vec.end(); it++ ){
CAdamPtr temp;
temp = *it;
if (temp->No==2){
vec.erase(it);
break;
};
};

cout << "Boyut =" << vec.size() << endl <<endl;
for ( it=vec.begin() ; it < vec.end(); it++ ){
CAdamPtr temp;
temp = *it;
temp->Soyle();
};

cout << "Boyut =" << vec.size() << endl <<endl;
return 0;
}

Cikisin son satirlari soyle oluyor (yerini anlamadigimiz satiri isaretledim):

...

1 numarali adamim. Adim =fatih
2 numarali adamim. Adim =yeni isim
3 numarali adamim. Adim =sehide
Boyut =2

1 numarali adamim. Adim =fatih
3 numarali adamim. Adim =sehide
Boyut =2

2. Adam Silindi
1. Adam Silindi
3. Adam Silindi


Ama ciktinin son satirlarinin soyle olacagini dusunurduk:

...

1 numarali adamim. Adim =fatih
2 numarali adamim. Adim =yeni isim
3 numarali adamim. Adim =sehide
2. Adam Silindi
Boyut =2

1 numarali adamim. Adim =fatih
3 numarali adamim. Adim =sehide
Boyut =2

1. Adam Silindi
3. Adam Silindi


Belki baskalarinin da ilgisini ceker diye buraya da yazdim. Neden vec.erase(it) yapildiginda silinmiyor? :)

Ali


acehreli
04/07/2008, 18:18
Konu boost::shared_ptr ile ilgili oldugu icin onu anlatan Turkce bir kaynak soyle:

http://www.ceviz.net/index.php?case=article&id=273

Ali

quasimodo
06/07/2008, 22:30
Bu linkteki verilenleri okuyunca bu soruyu cozecek seviyeye gelmis olur muyuz?

acehreli
07/07/2008, 01:50
Hepsini okumaya da gerek yok aslinda. :) Soru boost::shared_ptr'in nesne sayaci bulundurduguyla (reference counting) ve sayac sifira indiginde nesneyi sonlandirdigiyla ilgili...

Ali

quasimodo
07/07/2008, 16:59
Sorunun cozumunu anladim ama belki pek iyi anlatamayabilirim.


for( it = vec.begin() ; it < vec.end(); it++ ) {
CAdamPtr temp;
temp = *it;

if (temp -> No == 2) {
vec.erase( it );
break;
}
}

Her dongude yeniden bir temp yaratiliyor. Nesneyi silme isi onu gosteren son
shared_ptr ' a ait oldugundan vectorden siliniyor ama hala onu gosteren bir
tane share_ptr oldugundan yikicisi cagirilmiyor. Benim anlamadigim da neden
ilk seferde 2. nin yikicisi cagiriliyor?

acehreli
07/07/2008, 17:32
Evet ama biraz daha dusunmen gerekiyor! :)

temp'in sayaci bir arttirdigi konusunda haklisin tabii, ama kendisi dOngU sonlandiginda sonlandigi icin sayaci hemen tekrar da eksiltir.

shared_ptr'in sayac degerini use_count() ile ogrenebilirsin. Ornegin Soyle() fonksiyonunu onu da soyleyecek sekilde degistirsen ve son dOngUde soyle cagirsan:


// ilgisiz: sabit nesneler icin de cagrilabilmesi icin 'const' olsa iyi olur
void Soyle(size_t sayac) const {
cout << No << " numarali adamim. Adim =" << Adi << endl;
cout << "sayac: " << sayac << endl;
};

/* ... */

// ilgisiz: it < vec.end() yerine daha genel olan it != vec.end() de kullandim
for ( it=vec.begin() ; it != vec.end(); it++ ){
CAdamPtr temp;
temp = *it; // [Ali] Bu kopya nedeniyle sayac 1 artiyor
temp->Soyle(temp.use_count());
}; // [Ali] temp sonlaninca sayac yine 1 eksiliyor


Bende ciktinin o bolumu soyle oldu:

1 numarali adamim. Adim =fatih
sayac: 3
3 numarali adamim. Adim =sehide
sayac: 3
Boyut =2


Bir kopya vector'de ve bir kopya temp'te... Ucuncusu nerede? :)

Ali

quasimodo
07/07/2008, 19:11
push_back() ilgili birsey mi ?

acehreli
07/07/2008, 19:28
push_back shared_ptr'in bir kopyasini aliyor ve vector icinde tutuyor. Bundan baska bir etkisi yok. Dolayisiyla sonucta sayacin bir adedi vector icinde... Bir tanesi temp... Otekisi de sana bakiyor... :p

Ali

quasimodo
07/07/2008, 21:04
A2->SetAdam(2,"fatma");
burada bir tane var
vectorun icinde bir kopyasi var,
birde temp te kopyasi var?

acehreli
07/07/2008, 21:46
Dogru! :) Yani A1, A2, ve A3 de shared_ptr olduklari icin onlar yasadigi surece nesneler de yasamaya devam ediyorlar.

Programi kendimce biraz degistirdim. Bunda A1, A2, ve A3 yok. SetAdam()'i da kaldirdim:

#include <boost/shared_ptr.hpp>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Adam
{
string Adi;
int No;

public:

Adam(int vNo, const string & isim)
:
Adi(isim),
No(vNo)
{
cout << "Adam Olustu: " << Adi << endl;
}

~Adam() {
cout << No << ". Adam Silindi" << endl;
}

int Numarasi() const {
return No;
}

void Yaz() const {
cout << "Isim =" << Adi << endl;
cout << "No =" << No << endl;
}

void Soyle() const {
cout << No << " numarali adamim. Adim =" << Adi << endl;
}
};

int main()
{
typedef boost::shared_ptr<Adam> CAdamPtr;

std::vector<CAdamPtr> vec;
vec.push_back(CAdamPtr(new Adam(1, "fatih")));
vec.push_back(CAdamPtr(new Adam(2, "fatma")));
vec.push_back(CAdamPtr(new Adam(3, "sehide")));

cout << "Boyut =" << vec.size() << endl <<endl;
vec[1] = CAdamPtr(new Adam(2,"yeni isim"));
cout << "Boyut =" << vec.size() << endl <<endl;
vec[0]->Soyle();
vec[1]->Soyle();
vec[2]->Soyle();

std::vector<CAdamPtr>::iterator it;

for ( it=vec.begin() ; it != vec.end(); it++ ){
CAdamPtr temp;
temp = *it;
if (temp->Numarasi() == 2){
vec.erase(it);
break;
};
};

cout << "Boyut =" << vec.size() << endl <<endl;
for ( it=vec.begin() ; it < vec.end(); it++ ){
CAdamPtr temp;
temp = *it;
temp->Soyle();
};

cout << "Boyut =" << vec.size() << endl <<endl;
return 0;
}

Ali

quasimodo
08/07/2008, 15:17
Sormayi unuttum ama neden 2. nin yikicisi once cagriliyor?

Tahmini cavap :
Cunku son noktaya gelindiginde A2 den 1 tane var digerlerinden 2 ser tane var?

acehreli
08/07/2008, 17:40
Dogru ama yeterli cevap degil! :)

Asil programda A2'nin tanimini vec'ten onceye al; yani tek bir satirin yerini degistir:

CAdamPtr A2=CAdamPtr(new Adam);
std::vector<CAdamPtr> vec;


Simdi cikisin sonu soyle oluyor:

1. Adam Silindi
3. Adam Silindi
2. Adam Silindi


Ali

khaos
19/07/2008, 15:34
Ben bu yontemi sunun icin kullanıyorum. Bir container icinde saklayacagım elemanları, cok farklı dosya ve fonksiyonlar icinde olusturduktan sonra container icinde saklamak istiyorum. Ben baska bi şekilde cozemedim. O yuzden shared_ptr en iyi cozum gibi geldi bana. Bu yontemi OGRE icinde gormustum. Ama OGRE nın tamamını incelemedigim icin anlamamıstım. Sonra bi arkadas shared_ptr den bahsedince kafamdaki ampul yandı :)

khaos
22/07/2008, 10:48
Arkadaslar, bu konunun devamı olarak soyle bi sorusormak istiyorum. Shared_ptr olarak tanımlanan degişkenimi, program sonlanmadan , ihtiyacım oldugu anda sonlandırmak istiyorum. Bunu nasıl yapacagım? Yani use_count() sadece 1 dondurdugunde nasıl sonlandıracagım..?

acehreli
23/07/2008, 19:45
Herhangi bir shared_ptr'in reset()'ini cagirabilirsin. O zaman "NULL isaretci" gibi olur ve eger son sahip oysa, zaten nesneyi sonlandirir:

p.reset();

Ali

khaos
24/07/2008, 08:44
tamam ustadım. demek resetin gorevini yanlıs anlamısım. teşekkur ederim