PDA

Tam Sürümünü Görmek İçin : function pointer


atg
08/04/2005, 00:38
#include <string>
using namespace std;

typedef string* (*ReloadNotify)( string* );

class IAdStore
{
public:
ReloadNotify Notify;
};

class Rotator
{
private:
IAdStore* store;
public:
string* ReloadRequest( string* notifies )
{
string* s = new string;
return s;
}
void SetStore( IAdStore* Store )
{
store = Store;
store->Notify = ( string* (__cdecl * )(string*) )ReloadRequest ;
}

};


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


error C2440: 'type cast' : cannot convert from 'overloaded-function' to 'std::string *(__cdecl *)(std::string *)'


ben neyi yanlış yapıyorum?


myavuzselim
08/04/2005, 01:03
Merhaba
http://www.newty.de/fpt/fpt.html adresinde su yaziyor: "Regarding their syntax, there are two different types of function pointers: On the one hand there are pointers to ordinary C functions or to static C++ member functions. On the other hand there are pointers to non-static C++ member functions. The basic difference is that all pointers to non-static member functions need a hidden argument: The this-pointer to an instance of the class. Always keep in mind: These two types of function pointers are incompatible with each other."

Verdigin ornekte ReloadRequest fonksiyonunu static yapinca sorunsuz derliyor.

atg
08/04/2005, 01:13
yardımın için sağol, ama o fonksiyonu statik yapamam, dizayn buna müsait değil,

karamemed
08/04/2005, 11:21
Bir sınıfın metodları o sınıfın özellikleri(değişkenleri) üzerinde işlem yapar. Örneğin bir sınıfın xxx isimli değişkenini sıfırlayan bir xmetodu olabilir. Dolayısı ile bir sınıfın yordamları ile değişkenleri içiçedir. Bir sınıfın yordamını alıp başka bir yerde kullanmazsın. Örneğin burada xmetodu xxx değişkenini değiştirecekti. Fakat sınıf dışında bunun bir anlamı yoktur çünkü sınıf dışında xxx diye bir değişken yoktur. Sınıfların yordamları sınıfın dışında düşünülemezler, zira bu yordamlar sınıfın değişkenlerini kullanabilen özel yordamlardır. Static yordamlar sınıfın özellikleri kullanmazlar, yani sınıfa bağlı olmazlar.

Nesneye yönelik dillerde bir sınıfın yordamını kullanabilmek için miras kullanılabilir. Fakat bu tasarımını da bozmamalıdır. Sadece bir yordamı kullanmak için miras kullanılmaz.

şimdilik aklıma gelenler bunlar. Kolay gelsin..

acehreli
08/04/2005, 20:33
atg, burada galiba oge islev gostergeleri kullanman gerekiyor. Yanlis anlamadiysam asagidaki gibi bir cozum isine yarayacaktir. Aciklamalari kodun icine yazdim.

Ali


#include <string>
using namespace std;

// Rotator diye bir sinif oldugunu bildiriyoruz
class Rotator;

// Rotator sinifinin string* alip string* dOndUren oge islev
// gostergesine (member function pointer) RotatorIslevi adini veriyoruz
typedef string* (Rotator::*RotatorIslevi)( string* );

// Bu sinifi degistirdim: rotator diye yeni bir oge ekledim. Yoksa
// 'Notify' adli oge islev, bir nesne olmadan cagrilamaz. (karamemed'in
// de soyledigi gibi)
class IAdStore
{
public:
Rotator * rotator;
RotatorIslevi Notify;

// Hic olmazsa isini yapacagi bir islev tanimliyorum. Uygun
// olacagini dusundugumu icin RotatorIslevi ile ayni arayuzu sectim

string* cagir(string* parametre)
{
// Oge islev gostergeleri cagirirken kullanilan yazim sekline
// dikkat :) Fazladan parantezler bile gereklidir...
return (rotator->*Notify)(parametre);
}
};

class Rotator
{
private:
IAdStore* store;
public:

// Burada parametreyi neden string* olarak gonderiyorsun? 'string
// const &' daha uygun olmaz mi?
string* ReloadRequest( string* notifies )
{
// Parametreyi bir sekilde kullanmis olalim diye
string* s = new string(*notifies);
return s;
}
void SetStore( IAdStore* Store )
{
store = Store;
store->Notify = &Rotator::ReloadRequest ;

// Simdi burada store'un rotator nesnesini de belirlememiz
// gerekiyor
store->rotator = this;
}
};


int main()
{
IAdStore store;
Rotator rotator;

rotator.SetStore(&store);

// Sonunda sakladigimi oge islev gostergesini (dolayli olarak)
// cagiriyoruz
string parametre = "Merhaba";
string* sonuc = store.cagir(&parametre);

cout << "Sonuc: " << *sonuc << '\n';

// Bu temizligi bu temizlikten sorumlu olan bir nesneye vermek cok
// daha iyidir. (Bkz. akilli gostergeler (smart pointers))
delete sonuc;

return 0;
}

atg
09/04/2005, 00:32
@karamemed
açıklamaların için teşekkürler, galiba söylediğin şeyleri anladım,

@acehreli
Örnek kod için teşekkürler, elinize sağlık, karamemedin açıklamalarına somut bir boyut katmış, parametrelerin bazı gereksiz hallerde( string* gibi ) bulunduğunu biliyorum ama bunları düşünemiyorum çünkü bir function pointer meselesi kafamın etini yiyor.

Bir sorum daha var:
Burada merak ettiğim, neden bir sınıfın yordamlarını ben sınıf dışından kullanamıyorum, elimde yordamın tanımı var( function pointer, typedef ) artı o yordamın adreside var( program çalıştığında içinde bulunduğum sınıf ), neden atayamıyorum, nasıl bir şey bu sınıf denen alet, kilitli kutumu?

Amacım:
Aslında benim burada yapmak istediğim( daha doğrusu ben bunu yaptım ama C# ile ) şey şu; bu Rotator denen sınıf adındanda anlaşılacağı üzere bir "reklam evirme-çevirme" sınıfı, ama şu var, bazen( çoğu zaman ) reklamların elde edileceği konum değişiyor, kimisi xml dosyasından alıyor, kimisi web servisinden, kimisi veritabanından, kimiside başka yerden, ve ben reklamların ne zaman, nereden yükleneceğini bilmiyorum, çünkü yazdığım programı ben kullanmıyorum( bu bir sunucu bileşeni ) ve kullanacak kişinin elindede kaynak kodları yok, topu topu bir dll var, dolayısıyla ben miras kullanamam, bende bunun için bir soyut sınıf yaptım( IAdStore ) ve bunun tanımını açıkça bir dosya içinde programcılara belirttim, dolayısıyla programcı, reklamların yüklenme noktası ile bağlantıya geçip, oradan reklamların yüklenmesi/saklanması işini yapan bir modül/ek geliştirecek ve bunu bizim Rotator sınıfımıza atayacak ama ne varki, bu yolla düşününce iletişim sadece tek yönlü oluyor, ya reklamlar gösterimdeyken kaynak değişirse? bu durumda IAdStore'un bunu rotatora belirtmesi gerek, bende bu basit problemi çözebilmek için function pointer ( C# versiyonunda 'delegate' ) denen aleti kullandım ve sonrada bütün sistemi tutup C++ ya çevirmek istedim( tabi sonra başıma gelmedik kalmadı ).


NOT:
Adı geçen sınıfı kullanmak isteyenler ( ve incelemek isteyen profesyonel arkadaşlar ) için, aşağıya çalışan bir versiyonunu( ilk yaptığım versiyonuydu ) ekliyorum, küçük ve basit( itiraf ediyorum: ve bazı dizayn hataları var ), anlaşılması kolay. umarım arkadaşların işine yarar, en azından bir fikir verir.

acehreli
09/04/2005, 03:57
Sinif kabaca, bir takim verilerle o veriler uzerinde islem yapan islevleri bir araya getirir.

C'de, bir yapi ve onun uzerinde islem yapan islevlerin tumu o yapiya bir isareci alirlar. Islemleri yaparken hangi yapi nesnesini degistireceklerini boyle bilebilirler:


#include <stdio.h>

struct Yapi
{
int sayi;
};

typedef struct Yapi Yapi;

/*
O yapi nesneleri uzerinde islem yapan islevler hep 'Yapi *' aliyorlar
*/

void Yapi_kur(Yapi * nesne, int ilk_deger)
{
nesne->sayi = ilk_deger;
}

/*
Bu islev nesnede degisiklik yapmayacagi icin onu 'Yapi const *' olarak
aliyor
*/

void Yapi_goster(Yapi const * nesne, FILE * akim)
{
fprintf(akim, "Nesnenin sayisi: %d\n", nesne->sayi);
}

int main()
{
Yapi nesne;

Yapi_kur(&nesne, 42);
Yapi_goster(&nesne, stdout);

return 0;
}


C++'in siniflarinin (ve yapilarinin) isleyis acisindan bundan hic farki yoktur. [Tabii burada 'virtual' islevlere, kalitima, vs. hic karismiyorum.] Yukaridaki programla onun C++ esdegerinin benzerligine bakalim:


#include <stdio.h>

class Sinif
{
int sayi;

public:

// Fark 1: Islevlerin adlarinin basina Yapi_ gibi bir belirtec
// koymak zorunda degilim. Her oge islevin tam adinin basinda zaten
// 'Sinif::' oldugu varsayilir

// Fark 2: Islevlere nesne isaretcisini kendim gondermek zorunda
// degilim, nesne isaretcisi 'this' adi verilen ozel ve gizli bir
// parametre olarak otomatik olarak gonderilir.

// Fark 3: Nesneyi kurmakla gorevli olan islev(ler)in adlarini
// kendim koymak zorunda degilim. 'kur' gibi kendi icadim olan bir ad
/ yerine tUrUn kendi adi, kurucusunun da adi olur.

Sinif(int ilk_deger);

// Fark 4: 'this' isaretcisini kendim gonderemeyecegime gore,
// nesnede degisiklik yapmayan islevlere acikca 'Sinif const *'
// gonderemem. Bu 'const' ozelligini islevin parametre listesinden
// sonra koyulan bir 'const' ile belirtebilirim

void goster(FILE * akim) const;
};

Sinif::Sinif(int ilk_deger)
:
sayi(ilk_deger)
{}

void Sinif::goster(FILE * akim) const
{
// Fark 5: Acikca 'this->sayi' yazmak zorunda degilim; 'sayi' yazmak
// yetiyor.
fprintf(akim, "Nesnenin sayisi: %d\n", sayi);
}

int main()
{
Sinif nesne(42);
nesne.goster(stdout);

return 0;
}


[Not: Aslinda aralarinda tabii ki baska farklar da var ama konudan fazla ayrilmak istemedim.]

Soylemek istedigim en onemli sey, C++'ta her sinif (ve yapi) oge islevinin gizli bir 'this' isaretcisi aldigidir. Onun icin, oge islevin uzerinde cagrilacagi bir nesne olmadan oge islev cagiramazsin.

Bunun istisnasi, 'this' isaretcisi almayan 'static' sinif islevleridir. Benim bildigim butun derleyicilerde 'static' oge islevler de serbest islevler gibi cagrildiklari icin, onlari serbest islev isaretcisi gereken yerlerde kullanabilirsin. myss'in verdigi Ingilizce yazida da bu soylenmeye calisiliyor.

Sonucta, oge islev isaretcileri ile serbest islev isaretcileri birbirlerinden ayri seylerdir; birisini otekisine atayamazsin.

Ali

atg
10/04/2005, 02:41
@acehreli
Yani aslında koddaki gibiler, işlevler ve nesneler birbirlerinden farklı noktadalar ve birbirlerinden habersizler, yani Sinif isimli sınıfdan bir milyon tane oluştursakta goster işlevinden sadece 1 tane oluyor ve buda nesnelerden bağımsız durumda ve bunları bağlamak içinde this işaretçisi gizli bir parametre olarak geçiyor, eğer işlev, nesnemiz üzerinde hiçbir değişiklik yapmıyorsa biz bunu sizin yukarıda gösterdiğiniz gibi const olarak belirliyoruz. Dolayısıyla ben yukarıdaki gibi bir atama yapınca, boşlukta sahipsiz bir durumdaki kodu( işlevi ) çalıştırmaya kalkmış oluyorum. Sanıyorumki anladım, bu geniş açıklamalarınız için çok çok teşekkür ederim, gerçekten faydalı oldu.