指向类成员的指针
指向類成員的指針,印象中似乎很少用到,重新學習C++的過程中,才發現自己是忽視了一個很重要的東東,以前我一直認為類的成員函數不能作為回調函數,所以很多C程序都始終無法移植到C++上來,現在才知道,這是對指向類成員的指針不了解的緣故。
1、指向非靜態成員的指針
其實指向非靜態的類成員的指針很容易,它們與普通指針唯一的區別是,他們受類的限制。如下:
class A
{
int _val;
int val();
};
int (A::*p_val) = &A::_val;
int ( A::*p_func )() = &A::val;
看到了嗎,是的,和普通的指針的區別是,指向類成員的指針必須把類也一并帶上,上面的例子中就是要把A::這個限定符一起戴上,然后?用法和普通指針一樣的就是了。
2、指向靜態成員的指針
指向靜態成員的指針,聲明的方式和普通指針完全一樣,只是賦值的時候,還得加上類的限定符。為什么這樣?我想可以這樣來理解,對于非靜態成員,其存在取決于類,類消亡的時候,非靜態成員隨之消亡,所以,其聲明必須與類的限定符綁在一起,而靜態成員對于類而言并無依附關系,所以,不需要類的限定符。如下:
class A
{
static int _val;
static int val();
};
int *p_val = &A::_val;
int (*p_func)() = &A::val;
3、好處:
一個好處是,通過指向成員的函數指針,可以很輕松的調用各個成員函數了;另一個好處是,對于靜態成員函數,可以成為C里的回調函數啦。
下面是一個例子,加深一下理解:
#include <iostream>
#include <string>
using namespace std;
typedef void (*funchandler)();
void register_func(funchandler f)
{
cout << "register_func" << endl;
(*f)();
}
class A
{
public:
A() : _val( 0 ) { cout << "create A..." << endl; }
void test() { cout << "test..." << endl; }
void test1() { cout << "test1..." << endl; }
void test2() { cout << "test2..." << endl; }
int val() { return _val; }
static void test3() { cout << "test3..." << endl; }
int _val;
private:
};
int main()
{
A a;
int ( A::*p_val ) = 0;
p_val = &A::_val;
cout << "a.*p_val: " << a.*p_val << endl;
void (A::*p_func)();
p_func = &A::test;
a.test();
(a.*p_func)();
p_func = &A::test1;
( a.*p_func )();
p_func = &A::test2;
( a.*p_func )();
void (* pp_func)();
pp_func = &A::test3;
(*pp_func)();
register_func( pp_func );
return 0;
}
?
通過成員指針調用成員函數
可以在不必知道函數名的情況下,通過成員指針調用對象的成員函數。例如,函數dispatcher有一個變量pmf,通過它調用類成員函數,不管它調用的是strcpy()函數還是strcat()函數。指向外部原函數的指針和指向類成員函數的指針是有很大區別的。后者必須指向被調函數的宿主對象。因此,除了要有成員指針外,還要有合法對象或對象指針。
現舉例做進一步說明。假設A有二個實例,成員函數指針支持多態性。這樣在成員指針調用虛成員函數時是動態處理的(即所謂后聯編 - 譯注)。注意,不可調用構造和析構函數。示例如下:
A?*p=?&a1;?//創建指向A的指針
//創建指向成員的指針并初始化
void?(A::*pmf)(char?*,?const?char?*)?=?&A::strcpy;
//要將成員函數綁定到pmf,必須定義呼叫的對象。
//可以用*號引導:
void?dispatcher(A?a,?void?(A::*pmf)(char?*,?const?char?*))
{
char?str[4];
(a.*pmf)(str,?“abc”);?//將成員函數綁定到pmf
}
//或用A的指針表達方式指向成員指針:
void?dispatcher(A?*?p,?void?(A::*pmf)(char?*,?const?char?*))
{
char?str[4];?(p->*pmf)(str,?“abc”);
}
//函數的調用方法為:
dispatcher(a,?pmf);?//?.*?方式
dispatcher(&a,?pmf);?//?->*?方式?
?
成員指針數組
class?A
{
public:
void?strcpy(char?*,?const?char?*);
void?strcat(char?*,?const?char?*);
};
在下例,聲明了一個含有二個成員指針的數組,并分配類的成員函數地址給成員指針:
typedef void(A::*PMA)(char *, const char *);
PMA pmf[2]= {&A::strcpy, &A::strcat};?
也就是
????? void (A::*PMA[2])(char *, const char *)= {&A::strcpy, &A::strcat};
這樣的數組在菜單驅動應用中很有用。選擇菜單項后,應用將調用相應的回叫函數,如下所示:
int?main()
{
MENU_OPTIONS?option;?char?str[4];
//從外部資源讀取選項
switch?(option)
{
case?COPY:
(pa->*pmf[COPY])(str,?“abc”);
break;
case?CONCAT:
(pa->*pmf[CONCAT])(str,?“abc”);
break;
//…
}
}?
?
Const 類型的成員函數
成員指針的類型應該與成員函數類型一致。上面例子中的pmf 可以指向A的任意函數,只要該函數不是const類型。如下所示,如果將touppercase()的地址分配給pmf,將導致編譯出錯,因為touppercase() 的類型是const。
{
public:
? void?strpcy(char?*,?const?char?*);
? void?strcat(char?*,?const?char?*);
? void?touppercase(char?*,?const?char*)?const;
};
pmf=&A::touppercase;?//出錯,類型不匹配
//解決的方法是聲明一個const類型的成員指針:
void?(A::pcmf)(char?*,?const?char?*)?const;
pcmf=&A::touppercase;?//?現在可以了?
有些差勁的編譯器允許一個非const類型的成員指針指向const類型的成員函數。這在標準C++是不允許的。
轉載于:https://www.cnblogs.com/weiqubo/archive/2011/03/22/1991364.html
總結
- 上一篇: 可乐猪为什么叫可乐猪?
- 下一篇: 数学基础学习随笔--序言