#include <iostream> using namespace std; //第四步才看 class A; class B; typedef void (A::*Apointer)(); typedef void (B::*Bpointer)(); //第一步開始看 class A { public: void (A::*click)(); void onClicked(){ cout<<"按A上面的按鈕,調用了自己的onClick函數!"<<endl; } //第四步才看 B* slotObj; void TreatClickEvent(){ (slotObj->*(Bpointer)click)(); } }; //第三步才看 class B { public: int x=5; void onClicked(){ cout<<"按A上面的按鈕,調用了B的onClick函數! 成員變量x的值為"<<x<<endl; } }; //第一步開始看:復習成員變量指針 void runMemberVariblePointer(A * obj, int A::* pMember) { cout<<obj->*pMember<<endl; } //第一步開始看:復習成員函數指針 void runfuncName(A * obj, void (A::*func)()){ (obj->*func)(); } //第一步看:組合成員變量指針和成員函數指針 void runfuncPointer(A * obj, void (A::*( A::*pfunc ))()){ //Apointer A::* pfunc (obj->*(obj->*pfunc))(); } //typedef void (A::*Apointer)(); //第二步才看 //typedef void (A::*(A::*Signal))(); typedef Apointer A::* Signal; void connect(A* a, Signal signal, Apointer slot){ //void (A::*slot)() a->*signal = slot; } //第三步才看 void connect(A* a, Signal signal, Bpointer slot){ a->*signal = (Apointer) slot; } //第四步才看 void connect(A* a, Signal signal, B* b, Bpointer slot){ a->*signal = (Apointer) slot; a->slotObj = b; } int main(int argc, char *argv[]) { //第一步、理解信號的本質:成員函數指針類型的特殊成員變量 //第二步、連接A本身的信號與槽 A a; runfuncName(&a,&A::onClicked); connect(&a,&A::click,&A::onClicked);//a.click = &A::onClicked; (a.*a.click)(); runfuncPointer(&a,&A::click); //第三步:連接A的信號到B的槽 B b; B * fb = &b; connect(&a, &A::click, &B::onClicked);//a.click = (void (A::*)())&B::onClicked; (a.*a.click)(); (b.*(Bpointer)a.click)();//(fb->*(Bpointer)a.click)(); //第四步:完善連接A的信號到B的槽 connect(&a, &A::click, fb, &B::onClicked); a.TreatClickEvent(); return 0; }
第一步:
首先通過runMemberVariblePointer(A * obj, int A::* pMember)、runfuncName(A * obj, void (A::*func)())復習了之前成員變量指針和成員函數指針的用法。
然后再來看runfuncPointer(A * obj, void (A::*( A::*pfunc ))()),這里參數void (A::*( A::*pfunc ))()是指向成員函數指針的指針,在主函數中通過runfuncPointer(&a,&A::click);調用它,其中&A::click是在A類中聲明的成員函數指針的地址,但這里(A::*click)()沒有指向任何一個函數,所以繼續往下看。
第二步:
首先通過typedef Apointer A::* Signal;定義了指向成員函數指針的指針,這么做的目的是為了之后更加簡潔的定義指向成員函數指針的指針。
然后來看connect(A* a, Signal signal, Apointer slot) 函數,signal就是指向成員函數指針的指針,slot為成員函數指針,這是再回頭看之前的問題,主函數在調用runfuncPointer(&a,&A::click);之前,調用了connect(&a,&A::click,&A::onClicked);,其中&A::click是在A類中聲明的成員函數指針的地址,&A::onClicked是成員函數onClicked()的地址,通過connect()函數可以將函數地址slot傳遞給函數指針a->*signal,相當於連接signal和slot,這時調用runfuncPointer(&a,&A::click);就可以得到結果"按A上面的按鈕,調用了自己的onClick函數!"。
第三步:
首先看 connect(A* a, Signal signal, Bpointer slot),與第二步相同,只不過slot是Bpointer類型,並且函數體中a->*signal = (Apointer) slot;用了強轉。
在主函數中調用connect(&a, &A::click, &B::onClicked);,這里傳入了B類成員函數的地址,然后對象a調用成員函數指針(a.*a.click)();,結果為"按A上面的按鈕,調用了B的onClick函數! 成員變量x的值為4358424",這里x為隨機數,因為這里通過強轉訪問的B的onClick函數,但它並沒有訪問b對象的內存。
之后主函數中(b.*(Bpointer)a.click)();,將A類的成員函數指針強轉為B類型的,並用對象b調用,結果為"按A上面的按鈕,調用了B的onClick函數! 成員變量x的值為5",這里用對象b調用,所以x=5。
第四步:
首先看connect(A* a, Signal signal, B* b, Bpointer slot),它的函數體將b的地址給了A類中B類型的對象指針slotObj,其他的與第二步分析相同。
主函數中connect(&a, &A::click,fb, &B::onClicked);連接signal和slot,最后調用a.TreatClickEvent();,直接訪問B的onClick函數,所以結果為"按A上面的按鈕,調用了B的onClick函數! 成員變量x的值為5"。