C++回調函數、靜態函數、成員函數踩過的坑。
明確一點即回調函數需要是靜態函數。原因:
- 普通的C++成員函數都隱含了一個this指針作為參數,這樣使得回調函數的參數和成員函數參數個數不匹配。
若不想使用C式函數作為回調函數呢?(破壞封裝性)
解決方法
- 使用static修飾成員函數。static不含this指針。
- 不用static修飾的成員函數,需要借助中間變量來訪問。這個演示的很好。
- 也可以在回調函數中增加一個變量,類型為該類的類型,作為this指針的傳遞。
下面講解一下第二點,我認為比較好用。
class A
{
public:
static void staticmember(){cout<<"static"<<endl;} //static member
void nonstatic(){cout<<"nonstatic"<<endl;} //nonstatic member
virtual void virtualmember(){cout<<"virtual"<<endl;};//virtual member
};
int main()
{
A a;
//static成員函數,取得的是該函數在內存中的實際地址,而且因為static成員是全局的,所以不能用A::限定符
void (*ptrstatic)() = &A::staticmember;
//nonstatic成員函數 取得的是該函數在內存中的實際地址
void (A::*ptrnonstatic)() = &A::nonstatic;
//虛函數取得的是虛函數表中的偏移值,這樣可以保證能過指針調用時同樣的多態效果
void (A::*ptrvirtual)() = &A::virtualmember;
//函數指針的使用方式
ptrstatic();
(a.*ptrnonstatic)();
(a.*ptrvirtual)();
}
直接用類名引出非靜態成員函數,賦值給函數指針:
//nonstatic成員函數 取得的是該函數在內存中的實際地址
void (A::*ptrnonstatic)() = &A::nonstatic;
隨后需要運行回調函數的時候,使用一個輔助變量來運行,格式為:(變量名. * 函數指針)(參數)。
(a.*ptrnonstatic)();
應用:類中成員函數的函數指針寫法
如果在我們的編程過程中:
- 需要使用到回調函數。
- 回調函數是一個類的成員函數。
- 此成員函數不是靜態函數,也不適合做靜態函數。
我們可以使用:
bool (DoTask::*function)(void *arg);
其中DoTask是一個類名,上面寫的就是一個返回值為bool類型,參數為void *類型的函數指針,指向DoTask這個類中的成員函數。
然后調用的時候我們可以:
Dotask d;
//注意ReadData函數不用加括號,要加作用域和地址符
bool (DoTask::*function)(void *arg) = &DoTask::ReadData;
(d.*function)(NULL);
注意點就是賦值給函數指針的時候需要取地址符、加作用域、函數不加括號,直接函數名即可。
調用的時候,用一個實例去調用函數指針即可。
其它
假如我們將函數指針放在一個結構體中:
struct task
{
bool (DoTask::*function)(void *arg);
void *arg;
};
那么我們調用它的時候也是一樣的。
struct task t;
t.function = &DoTask::ReadData;
...
DoTask d;
(d.*(t.function))(NULL); //完成調用。
現在看調用這一步。參照上面的:
(d.*function)(NULL)
只不過function現在存在結構體中,替換成從結構體中取出來即可:
function 替換成 t.function
代入即可。