閱讀85 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C++中類成員函數指針詳解

原文出處:點擊打開鏈接

      在C++中類成員函數指針是一種比較特別的指針,盡管直接使用類成員函數的情況不太多,但是還是有必要詳解一下這類指針。

      具體語法

      首先說明一下類成員函數指針的聲明方式:

      

Return_Type (Class_Name::* pointer_name) (Argument_List);
 
Return_Type:   member function return type.
Class_Name:    name of the class in which the member function is declared.
Argument_List: member function argument list.
pointer_name:  a name we'd like to call the pointer variable.
      通過函數指針調用類成員函數需要使用操作符.*或者->*,具體使用參考下麵這段代碼:

#include <iostream>
#include <string>
using std::string;
 
class Foo{
public:
  int f(string str){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  int (Foo::*fptr) (string) = &Foo::f;
  Foo obj;
  (obj.*fptr)("str");//call: Foo::f() through an object
  Foo* p=&obj;
  (p->*fptr)("str");//call: Foo::f() through a pointer
}

    類成員函數指針不是一般的指針

    通常的函數指針指向的地址都是一個準確的代碼入口地址,但是不難想象類成員函數指針指向的應該是一個相對的位置(相對於整個類的偏移地址)。這一點很好證明。
    類中的靜態函數實際和全局函數一樣,有著確定的入口地址,類成員靜態函數沒有this指針,但是卻可以共享整個類的命名範圍,也可以訪問到類中的其他成員。現在來看下麵這段代碼:
#include <iostream>
#include <string>
using std::string;
 
class Foo{
public:
  static int f(string str){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  //<pre name="code" >int (Foo::*fptr) (string) = &Foo::f;//error

int (*fptr) (string) = &Foo::f;//correct (*fptr)("str");//call Foo::f()}

其中 
int (Foo::*fptr) (string) = &Foo::f;//error
會引起如下錯誤:
cannot convert 'int (*)(std::string)' to 'int (Foo::*)(std::string)
這說明了類成員函數指針不是一般的函數指針

    類成員函數指針的轉換

   還是先來看看下麵的代碼:

#include <iostream>
class Foo{
public:
  int f(char* c=0){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
class Bar{
public:
  void b(int i=0){
    std::cout<<"Bar::b()"<<std::endl;
  }
};
 
class FooDerived:public Foo{
public:
  int f(char* c=0){
    std::cout<<"FooDerived::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  typedef  int (Foo::*FPTR) (char*);
  typedef  void (Bar::*BPTR) (int);
  typedef  int (FooDerived::*FDPTR) (char*);
 
  FPTR fptr = &Foo::f;
  BPTR bptr = &Bar::b;
  FDPTR fdptr = &FooDerived::f;
 
  //bptr = static_cast<void (Bar::*) (int)> (fptr); //error
  fdptr = static_cast<int (Foo::*) (char*)> (fptr); //OK: contravariance
 
  Bar obj;
  ( obj.*(BPTR) fptr )(1);//call: Foo::f()
}
 
Output:
Foo::f()

語句:

bptr = static_cast<void (Bar::*) (int)> (fptr);無法正常執行,因為非靜態、非虛的類成員函數指針都是屬於強類型,所以無法進行轉換。

但是語句

fdptr = static_cast<int (Foo::*) (char*)> (fptr);可以執行,這是因為兩者所屬的類存在繼承關係。

Bar obj;
<p>  ( obj.*(BPTR) fptr )(1);//call: Foo::f()可以執行,因為fptr是靜態綁定的</p>



    虛成員函數指針

#include <iostream>
class Foo{
public:
  virtual int f(char* c=0){
    std::cout<<"Foo::f()"<<std::endl;
    return 1;
  }
};
 
class Bar{
public:
  virtual void b(int i=0){
    std::cout<<"Bar::b()"<<std::endl;
  }
};
 
class FooDerived:public Foo{
public:
  int f(char* c=0){
    std::cout<<"FooDerived::f()"<<std::endl;
    return 1;
  }
};
 
int main(int argc, char* argv[]){
  typedef  int (Foo::*FPTR) (char*);
  typedef  void (Bar::*BPTR) (int);
  FPTR fptr=&Foo::f;
  BPTR bptr=&Bar::b;
 
  FooDerived objDer;
  (objDer.*fptr)(0);//call: FooDerived::f(), not Foo::f()
 
  Bar obj;
  ( obj.*(BPTR) fptr )(1);//call: Bar::b() , not Foo::f()
}
 
Output:
FooDerived::f()
Bar::b()

    通過上麵的代碼可以發現,類成員函數指針同樣可以實現多態!

最後更新:2017-04-03 05:39:09

  上一篇:go 【轉自百度知道】關於const int *的解釋
  下一篇:go 互聯網大型應用軟件架構設想與推薦