Re: [問題] 求助 class 內的function pointer用法

看板C_and_CPP (C/C++)作者 ( )時間16年前 (2009/09/03 06:49), 編輯推噓11(1100)
留言11則, 10人參與, 最新討論串6/6 (看更多)
※ 引述《kkroy (大豬小豬肉一斤)》之銘言: : 很感謝QQ29為我解答,不過有些細節我還是沒有搞懂,希望大家指點指點。 : : #include <iostream> : : using namespace std; : : class A; : : typedef void(A::*PTR)(int,int); : 其實這一行我就沒有看很懂,後來爬了文發現QQ大先前也有一樣的問題, : typedef int b 的用法 一直以為 可以用 b 來宣告int型態的變數,僅此而已。 : 後來才知道有 typedef void (*A)(int,int); 的用法, : 亦即 以A 來做為 宣告 void(*)(int,int) 型態(function pointer)的變數, : 這樣理解應該沒錯吧? : 至於先宣告class A 也很好理解,因為A在此時尚未定義, : 需要qualify PTR 為其member function。 : 但是A::* 我就不懂了,A::*PTR的意思是什麼呢? C++ 的書都會告訴你: class T { public: void foo() { value = 1; // 這其實是 this->value = 1; } int value; }; 意思就是說 non-static member 實際上還要透過 this pointer 才能存取, 實際上 non-static member function 的工作方式都像是有一個隱藏參數一樣: class T { public: void foo(T *ptr) { ptr->value = 1; } int value; }; 當你寫這樣時: int main() { T obj, obj2; obj.foo(); obj2.foo(); } 實際上是類似 foo(&obj) 和 foo(&obj2) 這樣的呼叫方式工作, 把 obj 和 obj2 的 address 傳進隱藏的第一個參數裡, 這樣才有辦法去定址 value 這個 data member。 如果你想要透過 pointer 去存取 value, 這樣寫是不可能有意義的: int main() { int *ptr = &T::value; } 因為 non-static data member 需要一個物件存放, 你連物件都還沒造出來又怎麼可能知道它的位址, C++ 的 &T::value 其實大都只是放一個 offset, 比方說: class T2 { public: int value1; int value2; }; 實際上 &T2::value1 在記憶體內部可能是用 0 表示, 然後 &T2::value2 在記憶體內部可能是用 4 表示 (如果 int 是 4-byte 的話), 如果你真的很好奇裡面裝的是什麼東西, Inside the C++ Object Model 有教一招, 就是直接 call C 的 printf() 無視型別用 %x 或 %p 當整數或指標輸出, 但是你就算看過了也不能假設每個 compiler 都這樣實作, 而且當你用上繼承、virtual function、virtual 繼承、多重繼承後, 可能又會有其它不同變化; 所以: int main() { T2 obj; obj.value1 = 1; obj.value2 = 2; } 假設 obj 被配置在 0x40000000, 那麼 obj.value1 就是 0x40000000, 而 obj.value2 就是 0x40000004, 如果你又定義了另一個物件 obj2, 那麼它內部的 value1 和 value2 又是不同位址。 透過 pointer 來存取的話就要寫成: int main() { int T2::*ptr = &T2::value1; // ptr 實際存的只是 0 這種數字 T2 obj; obj.*ptr = 1; } 原本你在寫一般 pointer 的時候, 是寫成 int *ptr = &var; 這樣, 但是 ptr 裡面存放的東西是 var 的真正位址而不是一個類似 offset 的東西, 對它做 dereference 並不需要搭配一個物件位址做計算, C++ 為了區別這兩種 pointer, 所以指向 member 的 pointer 在 * 前面還要加上 T2:: 這種東西, 對照一下就能發現 T2::* 和 * 是同一類語法元素: int *ptr = &var; int T2::*ptr = &T2::value1; C++ 也新增了兩個 operator 叫做 .* 和 ->*, 這是專門為這種新的 pointer 型別訂製的 operator, 如果你寫: T2 obj; int T2::*ptr = &T2::value1; 那使用時就要寫: obj.*ptr = 1; 如果你是寫: T2 *obj = new T2; int T2::*ptr = &T2::value1; // 這行跟上面一樣 這時就要寫: obj->*ptr = 1; 簡單說使用 .* 還是 ->* 就是要看它左邊是 value/ref 還是 pointer, 是 value/ref 的話就用 .*, 是 pointer 的話就用 ->*。 : 為什麼我加上A::(*PTR) compiler 不會過? 就跟上面說過的一樣, A::* 和 * 是同一種語法元素, 你不能分割它, 分割它就像是把 * 砍成兩半來寫一樣無聊。 : : class A : : { : : public: : : A(){} : : void QQ(int a,int b){cout<<a<<b;} : : void XD( PTR P){ (this->*P)(5,6);} : ↑ 我也不知道為什麼要加上* 這個應該上面也回答到了。 : : void DO() : : { : : XD(&A::QQ); : ↑這裡我也有問題,QQ已經是class A的成員函數 : 為何需要qualify呢? : 那qualify後為何又要取址呢? : 為什麼寫成 XD( &(A::QQ) ) Compiler也不會過? 上面講的都是 data member, 也說過指向 data member 的 pointer 內部存的常常只是 offset, 不過 member function 這東西不管你產生多少個物件它都只有一份, 所以 &A::QQ 的確就能得到一個 function address 沒錯, 那麼為什麼不能用普通的 function pointer 去指向它? 因為前面也提到過了 member function 其實有隱藏參數, 如果隨便讓你寫 void (*ptr)(int,int) = &&A::QQ; 這種東西, 那 compiler 看到你寫 (*ptr)(1, 2) 時就會少傳一個隱藏參數, 而當你這樣寫時: void (A::*ptr)(int,int) = &A::QQ; A obj; (obj.*ptr)(1, 2); 實際上的工作方式就很像這樣: (*ptr)(obj, 1, 2); 為了對兩種 function pointer 有所區別, 所以你還是得用 A::* 這種東西代替 * 。 至於為什麼一定要寫成 &A::QQ 不能寫成 &(A::QQ), 這是語法規定也是語意規定, 如果你在 & 後面包了一對小括號的話, 小括號裡面必須是某種運算式, 當 QQ 是 static member 時這樣寫在語意上合法, 這就像你有個 free function 是 void foo() { }, 然後你單獨寫了一個 foo 也是合法的一樣, 可是當 QQ 是一個 non-static member 時, 它如果沒有搭配物件或物件指標一起使用在語意上就無效, 所以 compile 一樣不給你過 (這算是 semantic error 而非 syntax error), C++ 語言本身在設計上就特別為 &A::QQ 這種寫法開了新的語法規則, 讓 &A::QQ 跟 &(A::QQ) 走不同條路徑, 只有新的這條規則會特別產生「指向 non-static member 的 pointer」, 而走其它語法規則就不會做這種特殊處理, 至於為什麼不讓它更聰明一點使 &(A::QQ) 也會 work, 這個就真的說來話長, 在這裡講也沒有意義, 姑且把它當成是規定就好。 根據以往經驗, 字打很多又很久沒睡所以難免會有地方出現缺失或語無倫次, 如果有發現到的話再回頭修文吧。 : : } : : }; : : int main() : : { : : A T; : : T.DO(); : : return 0; : : } : : 這樣是可以run的 是不是你想要的?? : : 有錯請各位多指教 : : ps.有誰知道我寫this->這個 為啥不寫會錯= = 我想不透 : 很抱歉,我真的很新, : 希望能為小弟解惑,感謝大家! -- Ling-hua Tseng (uranus@tinlans.org) Department of Computer Science, National Tsing-Hua University Interesting: C++, Compiler, PL/PD, OS, VM, Large-scale software design Researching: Software pipelining for VLIW architectures Homepage: https://www.tinlans.org -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 118.160.109.33 ※ 編輯: tinlans 來自: 118.160.109.33 (09/03 07:04)

09/03 09:33, , 1F
推~~順便推辛苦了, 早上六七點還在回專業文....Orz
09/03 09:33, 1F

09/03 09:55, , 2F
好文推!
09/03 09:55, 2F

09/03 09:57, , 3F
PUSH...有學有推...看到一半, 下班後再來看, 謝謝t大
09/03 09:57, 3F

09/03 11:22, , 4F
推~T大 可是我寫(A::*)PTR沒有切割他 也不給過 是為什麼??
09/03 11:22, 4F

09/03 11:25, , 5F
規定就是這樣啊.. int *a;也不能寫成int (*)a;
09/03 11:25, 5F

09/03 11:30, , 6F
type跟expression不一樣 本來就不能亂加括號
09/03 11:30, 6F

09/03 11:52, , 7F
太強了! 又學到一招了! 好文推!
09/03 11:52, 7F

09/03 14:14, , 8F
推!!
09/03 14:14, 8F

09/03 21:15, , 9F
推!
09/03 21:15, 9F

09/03 21:47, , 10F
推!
09/03 21:47, 10F

09/04 01:13, , 11F
推~
09/04 01:13, 11F
文章代碼(AID): #1AdlQLYS (C_and_CPP)
文章代碼(AID): #1AdlQLYS (C_and_CPP)