[爆卦]member用法是什麼?優點缺點精華區懶人包

為什麼這篇member用法鄉民發文收入到精華區:因為在member用法這個討論話題中,有許多相關的文章在討論,這篇最有參考價值!作者tinlans ( )看板C_and_CPP標題Re: [問題] 求助 class 內的fun...

member用法 在 ? ???????? Yoga Instructor? Instagram 的最佳解答

2020-05-09 20:52:59

🌟 Matricium 再生水 🌟 · 平時成日做戶外運動嘅我, 皮膚紫外線既破壞,肌膚抵禦力愈來愈低,再加上而家日日戴口罩, 攪正皮膚好易就會敏感泛紅! · 最近用左Matricium®醫學細胞再生精華 -再生水, 14年科研並獲全球皮膚組織修復專利—MATRICIUM®注活細胞能量昇華水,活...


※ 引述《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 ([email protected])
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)
VictorTom:推~~順便推辛苦了, 早上六七點還在回專業文....Orz 09/03 09:33
fetosa:好文推! 09/03 09:55
su31o4gj83:PUSH...有學有推...看到一半, 下班後再來看, 謝謝t大 09/03 09:57
QQ29:推~T大 可是我寫(A::*)PTR沒有切割他 也不給過 是為什麼?? 09/03 11:22
Fenikso:規定就是這樣啊.. int *a;也不能寫成int (*)a; 09/03 11:25
Fenikso:type跟expression不一樣 本來就不能亂加括號 09/03 11:30
kkroy:太強了! 又學到一招了! 好文推! 09/03 11:52
akasan:推!! 09/03 14:14
dendrobium:推! 09/03 21:15
ckck18:推! 09/03 21:47
jhs1213:推~ 09/04 01:13

你可能也想看看

搜尋相關網站