Saya ingin menyiapkan penunjuk fungsi sebagai anggota kelas yang merupakan penunjuk ke fungsi lain di kelas yang sama. Alasan mengapa saya melakukan ini rumit.
Dalam contoh ini, saya ingin hasilnya menjadi "1"
class A {
public:
int f();
int (*x)();
}
int A::f() {
return 1;
}
int main() {
A a;
a.x = a.f;
printf("%d\n",a.x())
}
Tapi ini gagal dalam kompilasi. Mengapa?
c++
oop
function-pointers
Mike
sumber
sumber
Jawaban:
Sintaksnya salah. Penunjuk anggota adalah kategori tipe yang berbeda dari penunjuk biasa. Pointer anggota harus digunakan bersama dengan objek kelasnya:
class A { public: int f(); int (A::*x)(); // <- declare by saying what class it is a pointer to }; int A::f() { return 1; } int main() { A a; a.x = &A::f; // use the :: syntax printf("%d\n",(a.*(a.x))()); // use together with an object of its class }
a.x
belum menjelaskan pada objek apa fungsi itu dipanggil. Itu hanya mengatakan bahwa Anda ingin menggunakan penunjuk yang disimpan di objeka
. Mempersiapkana
waktu lain sebagai operan kiri ke.*
operator akan memberi tahu compiler tentang objek apa yang akan memanggil fungsi tersebut.sumber
(a.*a.x)()
Mengapa(a.*x)()
tidak berfungsi?a.x
adalah pointer ke fungsi anggota kelas A.*a.x
dereferensi pointer jadi sekarang referensi fungsi.a.(*a.x)
"mengikat" fungsi ke sebuah instance (sepertia.f
).(a.(*a.x))
diperlukan untuk mengelompokkan sintaks yang kompleks ini, dan(a.(*a.x))()
benar - benar memanggil metodea
tanpa argumen.int (*x)()
bukan penunjuk ke fungsi anggota. Sebuah pointer ke fungsi anggota ditulis seperti ini:int (A::*x)(void) = &A::f;
.sumber
Panggil fungsi anggota pada perintah string
#include <iostream> #include <string> class A { public: void call(); private: void printH(); void command(std::string a, std::string b, void (A::*func)()); }; void A::printH() { std::cout<< "H\n"; } void A::call() { command("a","a", &A::printH); } void A::command(std::string a, std::string b, void (A::*func)()) { if(a == b) { (this->*func)(); } } int main() { A a; a.call(); return 0; }
Perhatikan
(this->*func)();
dan cara mendeklarasikan function pointer dengan nama classvoid (A::*func)()
sumber
Anda perlu menggunakan penunjuk ke fungsi anggota, bukan hanya penunjuk ke fungsi.
class A { int f() { return 1; } public: int (A::*x)(); A() : x(&A::f) {} }; int main() { A a; std::cout << (a.*a.x)(); return 0; }
sumber
Meskipun ini didasarkan pada jawaban yang sangat bagus di tempat lain di halaman ini, saya memiliki kasus penggunaan yang tidak sepenuhnya diselesaikan oleh mereka; untuk vektor pointer ke fungsi lakukan hal berikut:
#include <iostream> #include <vector> #include <stdio.h> #include <stdlib.h> class A{ public: typedef vector<int> (A::*AFunc)(int I1,int I2); vector<AFunc> FuncList; inline int Subtract(int I1,int I2){return I1-I2;}; inline int Add(int I1,int I2){return I1+I2;}; ... void Populate(); void ExecuteAll(); }; void A::Populate(){ FuncList.push_back(&A::Subtract); FuncList.push_back(&A::Add); ... } void A::ExecuteAll(){ int In1=1,In2=2,Out=0; for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){ Out=(this->*FuncList[FuncId])(In1,In2); printf("Function %ld output %d\n",FuncId,Out); } } int main(){ A Demo; Demo.Populate(); Demo.ExecuteAll(); return 0; }
Sesuatu seperti ini berguna jika Anda menulis juru bahasa perintah dengan fungsi terindeks yang perlu digabungkan dengan sintaks parameter dan petunjuk bantuan, dll. Mungkin juga berguna dalam menu.
sumber
typedef int (A::*AFunc)(int I1,int I2);
Meskipun Anda sayangnya tidak dapat mengonversi penunjuk fungsi anggota yang sudah ada menjadi penunjuk fungsi biasa, Anda dapat membuat template fungsi adaptor dengan cara yang cukup mudah yang membungkus penunjuk fungsi anggota yang dikenal pada waktu kompilasi dalam fungsi normal seperti ini:
template <class Type> struct member_function; template <class Type, class Ret, class... Args> struct member_function<Ret(Type::*)(Args...)> { template <Ret(Type::*Func)(Args...)> static Ret adapter(Type &obj, Args&&... args) { return (obj.*Func)(std::forward<Args>(args)...); } }; template <class Type, class Ret, class... Args> struct member_function<Ret(Type::*)(Args...) const> { template <Ret(Type::*Func)(Args...) const> static Ret adapter(const Type &obj, Args&&... args) { return (obj.*Func)(std::forward<Args>(args)...); } };
int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;
Perhatikan bahwa untuk memanggil fungsi anggota, turunan dari
A
harus disediakan.sumber
Berdasarkan jawaban @ IllidanS4, saya telah membuat kelas template yang memungkinkan hampir semua fungsi anggota dengan argumen yang telah ditentukan dan instance kelas untuk diteruskan dengan referensi untuk dipanggil nanti.
template<class RET, class... RArgs> class Callback_t { public: virtual RET call(RArgs&&... rargs) = 0; //virtual RET call() = 0; }; template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> { public: T * owner; RET(T::*x)(RArgs...); RET call(RArgs&&... rargs) { return (*owner.*(x))(std::forward<RArgs>(rargs)...); }; CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {} }; template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> { public: T* owner; RET(T::*x)(Args...); RET call() { return (*owner.*(x))(std::get<Args&&>(args)...); }; std::tuple<Args&&...> args; CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x), args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {} };
Tes / contoh:
class container { public: static void printFrom(container* c) { c->print(); }; container(int data) : data(data) {}; ~container() {}; void print() { printf("%d\n", data); }; void printTo(FILE* f) { fprintf(f, "%d\n", data); }; void printWith(int arg) { printf("%d:%d\n", data, arg); }; private: int data; }; int main() { container c1(1), c2(20); CallbackCreattimeArgs<container, void> f1(&c1, &container::print); Callback_t<void>* fp1 = &f1; fp1->call();//1 CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout); Callback_t<void>* fp2 = &f2; fp2->call();//20 CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith); Callback_t<void, int>* fp3 = &f3; fp3->call(15);//20:15 }
Jelas, ini hanya akan berfungsi jika argumen yang diberikan dan kelas pemilik masih valid. Sejauh keterbacaan ... maafkan saya.
Edit: hapus malloc yang tidak perlu dengan membuat tuple penyimpanan normal. Menambahkan jenis yang diwariskan untuk referensi. Menambahkan opsi untuk memberikan semua argumen pada waktu panggilan sebagai gantinya. Sekarang bekerja untuk memiliki keduanya ....
Edit 2: Seperti yang dijanjikan, keduanya. Hanya batasan (yang saya lihat) adalah bahwa argumen yang telah ditentukan harus datang sebelum argumen yang diberikan runtime dalam fungsi callback. Terima kasih kepada @Chipster untuk beberapa bantuan terkait kepatuhan gcc. Ini berfungsi pada gcc di ubuntu dan studio visual di windows.
#ifdef _WIN32 #define wintypename typename #else #define wintypename #endif template<class RET, class... RArgs> class Callback_t { public: virtual RET call(RArgs... rargs) = 0; virtual ~Callback_t() = default; }; template<class RET, class... RArgs> class CallbackFactory { private: template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> { private: T * owner; RET(T::*x)(CArgs..., RArgs...); std::tuple<CArgs...> cargs; RET call(RArgs... rargs) { return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...); }; public: Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda); ~Callback() {}; }; public: template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)); }; template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {} template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) { return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...); }
sumber