PDA

Tam Sürümünü Görmek İçin : C++ Soruları I


atg
02/09/2005, 16:38
Soru 1 - )

#include "stdafx.h"



void f1( std::vector<std::string>& vect )
{
std::string s = "abc xyz";
vect.push_back( s );
}

int _tmain(int argc, _TCHAR* argv[])
{//12

std::vector<std::string> vect;

for( int i = 0; i < 500000; i++ )
f1( vect );

int x = 5;//19

vect.clear();

x = 7;//23

return 0;
}


Yukarıdaki basit programın çalışması esnasında, 12 nolu satıra gelindiğinde program hafıza üzerinde 1556K yer kaplamakta, 19. satıra gelindiğinde bu değer 23552K ya yükseliyor, 23 numaralı satırda ise programın hafıza üzerinde kapladığı yer üzerinde hiçbir değişim yok; 23552K.

Adı geçen string dinamik olarak ayrılmadığı ve clear() çağırıldığı halde neden vector ün içi boşaltılmış vaziyette değil?


Soru 2 - )

#include <hash_map>

int _tmain(int argc, _TCHAR* argv[])
{
stdext::hash_map<std::string*, int> hm;

std::string* s1 = new std::string( "abc xyz" );
std::string* s2 = new std::string( "abc xyz 2" );

hm.insert( make_pair( s1, 1 ) );
hm.insert( make_pair( s2, 2 ) );

stdext::hash_map<std::string*, int>::iterator i;

for( i = hm.begin(); i != hm.end(); ++i )
delete *i;//16

return 0;
}


Yukarıdaki kodlarda( Satır 16 ) neden hata( * ) alıyorum? yada en azından bunu düzeltmem için ne yapmam gerekiyor?

* main.cpp(35) : error C2440: 'delete' : cannot convert from 'std::pair<_Ty1,_Ty2>' to 'void *'

Soru 3 - )

#include <iostream>
#include <vector>

class A
{
public:
virtual void Perform() = 0;
};


class AA : A
{
public:
void Perform()
{
std::cout<< "Merhaba Dunya";
}
};

class B
{
private:
std::vector<A*> vect;

public:
void push( A* a )
{
vect.push_back( a );
}
//Bu fonksiyon private olarak geçecek
//İfade amacıyla public durumunda
void Execute( )
{
std::vector<A*>::iterator i;
for( i = vect.begin(); i != vect.end(); ++i )
(*i)->Perform();
}

};

int _tmain(int argc, _TCHAR* argv[])
{
B* obj_b = new B();

AA* aa = new AA();

obj_b->push( ( A* )aa );

obj_b->Execute();

return 0;
}


Yukarıdaki gibi bir durumda A sınıfına bağlı Perform() işlevinin bir parametre alması gerekiyor ama parametre A sınıfı yerine ondan türetilmiş AA sınıfı içerisinden kullanılacak, parametreyi veren kişi A sınıfından AA sınıfını türeten programcı olacak ve yine parametreyi AA::Perfom() fonksiyonunda kullanacak, fakat parametrenin 'cinsi' belli değil, yani bir int, std::vector yada başka bir şey olabilir, tamamiyle belirsiz, belirli olan tek şey ise bizim sadece parametrenin 'taşıyıcılığını' yaptığımız.

Bu şartlar altında A::Perform fonksiyonuna verilebilecek parametrenin 'cinsi' konusunda en fazla esnekliği ve program tutarlılığını yakalayabilmemiz için ne tür bir yol izlemeliyiz? template? void*? taban nesne? üye değişken? yada ne?


mr1yh1
02/09/2005, 19:33
1-)
system("pause");
vect.clear();
system("pause");

şu şekilde test edersen belleğin serbest bırakıldığı görülüyor. ( mingW ile çalışıyorum ).

mr1yh1
02/09/2005, 20:07
3-
türetmenin anlamlı olabilmesi için taban nesne kullanımı daha doğru geliyor bana. ( Object, TObject vs gibi )

acehreli
03/09/2005, 00:53
atg, keske sorulari ayri konular olarak acsaydin. :) Simdi butun konular karisacak... :)

Cevap 1: vector::clear'in bellegi ne yapacagi konusu belirli degildir. Yine gerekecegini dusunurek elinde tutabilir.

Bu vector'un sonlanmasini ve yeni bir vector'le tekrar baslamak istiyorsan, bunu en kolay olarak kapsamlarla halledebilirsin. Ornegin vector'un basindan sonuna kadar yasadigi bolumu bir isleve tasirsan, islevden cikilirken butun bellek de geri verilir.

Eger daha uzun sure yasamasi gereken bir vector'un bellegini tamamen geri vermek istiyorsan, o zaman onu bos bir vector'le degis tokus etme yontemini kullanabilirsin:



typedef vector<string> Vector;

void vectoru_temizle(Vector & vector)
{
Vector bos_vector;
bos_vector.swap(vector);
}


Simdi isleve gonderdigimiz vector'un icerigi bos_vector'le degistirilmis olacak ve islevden cikildiginda bos_vector sonlanirken asil vector'un bastan tuttugu bellegi geri verecek.

Bundan baska konunun programin icindeki bellek yoneticisi ile de ilgisi var. Program da isletim sisteminden aldigi bellegi yine gerebilecegini dusunerek elinde tutar. Linux'ta isler boyle yurur.

Yani ne yaparsan yap, belirli bir anda programin elinde tuttugu bellek, o ana kadar gerek duydugu en fazla buyukluktedir. En azindan Linux'ta ve g++ ile derlenmis programlarda boyle.

Ali

acehreli
03/09/2005, 00:58
Cevap 2: *i'nin turu bir isaretci degildir, bir pair'dir. Sunu dene:


delete i->first;


Ya da daha acik olsun diye:


std::string * anahtar = i->first;
int deger = i->second;

/* islem yap */

delete anahtar;


Ote yandan, string'leri dinamik olarak ayirmana gerek yok. Kisaca soyle yapar ve bellekle ugrasmamis olursun:


string const s1("abc");
hm.insert(make_pair(s1, 1));

/* Artik delete'e gerek yok */


Hatta hash_map'in operator[] islecini tanimlayip tanimlamadigina da bir bak. Eger tanimliyorsa, daha da kisaca:


string const s1("abc");
hm[s1] = 1;


Ali

acehreli
03/09/2005, 01:05
Ucuncu soruya biraz daha bilgi gerekiyor. Perform'a gonderilecek parametrenin turu her an degisik mi olacak, yoksa bir AA nesnesi icin belirli mi olacak?

Boyle mi olacak:



AA nesne;
nesne.Perform(1);
nesne.Perform('a');


Yoksa soyle mi:


AA<int> nesne;
nesne.Perform(1);
nesne.Perform(2);


Kim tasiyici? A mi AA mi? Tam olarak ne yapmaya calisiyorsun? Belki turetme kullanmana gerek yoktur. (?)

Ali

atg
03/09/2005, 14:58
Öncelikle cevaplar için arkadaşlara teşekkür ederim. Ben soruları sorarken genellikle gerçek kodları verip karmaşa oluşturmak istemem daha ziyade, sembolik kodlar veriyorum. Öyle sanıyorumki derleyicimide yazsaydım iyi olur du. Derleyici VC++ 8.0, Windows 2k3, sgi-stl yok.

@acehreli
sanıyorumki birinci sorunun cevabını anladım, benden kaynaklanan bir problem değil, bu bir davranış biçimi.

ikinci soruda bahsettiğiniz şekilde problemi çözdüm.


Üçüncü soruya gelince, sanırım bu soruyu kafam gerçekten davula dönmüş bir vaziyetteyken sormuşum. C++ ya göre düşünmeyi unutunca işler bu hale varıyor. zaten soruda taban nesne modeli uygulanmış halde. Ama ben soruyu daha farklı bir boyutuyla ve daha farklı bir problemleriyle sorayım.


//İş türleri için taban nesne
class A
{
public:
virtual void Perform() = 0;
};

//Programcının Thread Pool a vermek üzere türettiği
//kendi özel nesnesi
class AA : A
{
private:
int a;
int b;
public:
void Perform()
{
std::cout<< "a = " << a << " b = " << b;
}
};

//Thread Pool
class B
{
private:
//std::queue<A> olarak geçmedim AA sınıfını türeten ben olmadığım
//için AA'nın kopyalanacabilecek vaziyette olup olmadığını
//bilmiyorum. std::queue yada bunun yerine kullanılabilecek
//bir konteynerin copy constructor çağırdığını varsayımından
//yola çıkıyorum.
std::queue<A*> isler;
//Bu değişken varsayılan olarak false ve
//Thread Pool'un kapatılıp kapatılmadığını gösteriyor.
bool Cikis_İstemi_Yapildimi;

public:
void push( A* a )
{
//Boşta bekleyen thread var mı? varsa işi ona gönder

//Boşta bekleyen thread yoksa ve işlerin sayısı fazla
//çoğaldıysa yeni thread oluştur

//1------------------------------------------------
boost::thread th1( this->Execute );
//1------------------------------------------------
}
//Bu fonksiyon sırada bekleyen işlerden birini alır
//Yada belirli bir süre boyunca bir işin gelmesini bekler
//İş gelmezse NULL döndürür.
A* İsGetir( int nsec_timeout );

void Execute( )
{
while( !Cikis_İstemi_Yapildimi )
{
//Burada İsGetir fonksiyonundan İş getirmesi isteniyor
A* is = İsGetir( 20 * 1000 );

if( is == NULL )
{
//bu kısımda starvation uyguluyorum.
//thread, çıkış ile beraber sonlanıyor yada ben
//öyle tahmin ediyorum. Tabii hafıza üzerinde
//bir açığa sebeb oluyor mu bilemiyorum.
break;
}
else
{
is->Perform();
}
}
}
};

1 ile işaretlendirilmiş noktada Execute() işlevini çağırmam gibi bir durumun mümkün olmadığını biliyorum( function-pointer konusunda ağzımın payını aldım ). bunu mümkün kılmak için ne yapacağımı bilmiyorum, sanki boost::function, mem_fn ve bind(1st/2nd) kullanarak bu işi yapabileceğimi sanıyorum ama tam kestiremedim. Bir ikincisi bu noktada B::push işlevi sona erdiğinde 'th1' nesnesinin sanırım silinmesi gerekiyor ama yaptığım basite indirgenmiş testlerde B::push işlevi sona ermiş olmasına rağmen Execute() işlevinin çalışmaya devam ettiğini fark ettim.


#include <boost/thread/condition.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

boost::mutex m;


void f1()
{
boost::mutex::scoped_lock lk( m );

std::cout<< " kilit cozuldu--";
}

void f2()
{

boost::thread th( f1 );

std::cout<< " thread baslatildi--";
}

int _tmain(int argc, _TCHAR* argv[])
{
boost::mutex::scoped_lock lk( m );

f2();

lk.unlock();

int b = 0;

std::cin>>b;

return 0;

}

atg
03/09/2005, 22:36
öyle sanıyorumki son sorununda cevabını buldum.

class A
{
public:
void foo()
{
boost::function0<void> f1;

f1 = boost::bind( &A::bar , this );

boost::thread th( f1 );

th.join();
}
void bar()
{
std::cout<< "merhaba dunya";
}
};

A a;

int _tmain(int argc, _TCHAR* argv[])
{
a.foo();
return 0;
}