{譯}函數指針漫談(2)

C/C++函數指針的語法

http://blog.csdn.net/luckheadline
1. 定義函數指針
    有兩種函數指針類型:一個是C函數指針或靜態C++成員函數指針;另外一個是非靜態的C++成員函數指針。基本的區別是所有的非靜態成員函數指針都需要一個隱藏參數:this指針是一個類的實例。記住:這兩種函數指針彼此不兼容。
    因爲函數指針只是一個變量,因此它必須被定義。在下面例子裏我們定義了三個函數指針pt2Function, pt2Member and pt2ConstMember。他們都有三個參數(一個float型,兩個char型),返回都是int型。在假定的C++例子裏,這些函數都是TMyClass的非靜態成員函數。
// 2.1 define a function pointer and initialize to NULL
int (*pt2Function)(float, char, char) = NULL;                        // C
int (TMyClass::*pt2Member)(float, char, char) = NULL;                // C++
int (TMyClass::*pt2ConstMember)(float, char, char) const = NULL;     // C++

2. 調用約定(Calling Convention)
通常你沒必要考慮函數的調用約定:如果你沒有特別指定,編譯器缺省會假定爲__cdecl。調用約定會告訴編譯器諸如如何傳遞參數或者怎樣產生函數名。其它的調用約定有__stdcall, __pascal and __fastcall。調用約定屬於函數簽名:因此不同調用約定的函數和函數指針彼此不兼容!對於Borland和Microsoft編譯器,我們要在返回類型和函數或者函數指針名之間指明具體的調用約定。對於GNU GCC,我們使用__attribute__關鍵字。
// 2.2 define the calling convention
void __cdecl DoIt(float a, char b, char c);                             // Borland and Microsoft
void         DoIt(float a, char b, char c)  __attribute__((cdecl));     // GNU GCC

3. 爲函數指針分配地址
    很容易就可以將一個函數的地址分配給一個函數指針。我們就簡單的使用一個合適的已知函數或成員函數的名字,但是對大多數編譯器我們可以選擇在函數名前加上取地址符&。我們可能需要使用成員函數完整的名字,包括類名和域操作符(::)。而且,我們需要保證訪問的函數確實在可以訪問的域裏。
// 2.3 assign an address to the function pointer
//     Note: Although you may ommit the address operator on most compilers
//     you should always use the correct way in order to write portable code.

// C
int DoIt  (float a, char b, char c){ printf("DoIt/n");   return a+b+c; }
int DoMore(float a, char b, char c)const{ printf("DoMore/n"); return a-b+c; }

pt2Function = DoIt;      // short form
pt2Function = &DoMore;   // correct assignment using address operator

// C++
class TMyClass
{
public:
   int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
   int DoMore(float a, char b, char c) const
         { cout << "TMyClass::DoMore" << endl; return a-b+c; };

   /* more of TMyClass */
};

pt2ConstMember = &TMyClass::DoMore; // correct assignment using address operator
pt2Member = &TMyClass::DoIt; // note: <pt2Member> may also legally point to &DoMore

4. 比較函數指針
    我們同樣可以使用比較運算符(==, !=)。下面的例子是檢查是否函數pt2Function和pt2Member實際包含了函數DoIt和TMyClass::DoMore的地址。
// 2.4 comparing function pointers
// C
if(pt2Function >0){                           // check if initialized
   if(pt2Function == &DoIt)
      printf("Pointer points to DoIt/n"); }
else
   printf("Pointer not initialized!!/n");

// C++
if(pt2ConstMember == &TMyClass::DoMore)
   cout << "Pointer points to TMyClass::DoMore" << endl;

5. 使用函數指針調用函數
    C語言中,通過顯式的用*運算符去引用可以調用一個函數。同樣你也可以使用函數指針的名字而不是函數名。在C++中,操作符.*和.->*與類的實例一起以調用成員函數。如果調用發生在另一個成員函數中,我們可以使用this指針。
// 2.5 calling a function using a function pointer
int result1 = pt2Function    (12, 'a', 'b');          // C short way
int result2 = (*pt2Function) (12, 'a', 'b');          // C

TMyClass instance1;
int result3 = (instance1.*pt2Member)(12, 'a', 'b');   // C++
int result4 = (*this.*pt2Member)(12, 'a', 'b');       // C++ if this-pointer can be used

TMyClass* instance2 = new TMyClass;
int result4 = (instance2->*pt2Member)(12, 'a', 'b');  // C++, instance2 is a pointer
delete instance2;

6. 如何將函數指針當作參數傳遞
    我們可以將函數指針作爲函數的一個參數傳遞,特別如果想要傳遞一個指針作爲callback函數。下面的代碼表明如何傳遞一個指針給函數,函數有一個float型參數和兩個char型參數,返回int型:
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
// <pt2Func> is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
   int result = (*pt2Func)(12, 'a', 'b');     // call using function pointer
   cout << result << endl;
}

// execute example code - 'DoIt' is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
   cout << endl << "Executing 'Pass_A_Function_Pointer'" << endl;
   PassPtr(&DoIt);
}

7. 如何返回函數指針
    這個會有點微妙但一個函數指針是可以作爲一個函數的返回值的。下面的例子中有兩個解決方案關於如何返回一個函數指針,它有兩個float型參數並返回float型。如果想返回一個成員函數指針,我們不得不改變所有函數指針的定義/聲明。
//------------------------------------------------------------------------------------
// 2.7 How to Return a Function Pointer
// 'Plus' and 'Minus' are defined above. They return a float and take two float
// Direct solution: Function takes a char and returns a pointer to a
// function which is taking two floats and returns a float. <opCode>
// specifies which function to return
float (*GetPtr1(const char opCode))(float, float)
{
   if(opCode == '+')
      return &Plus;
   else
      return &Minus; // default if invalid operator was passed
}

// Solution using a typedef: Define a pointer to a function which is taking
// two floats and returns a float
typedef float(*pt2Func)(float, float);

// Function takes a char and returns a function pointer which is defined
// with the typedef above. <opCode> specifies which function to return
pt2Func GetPtr2(const char opCode)
{
   if(opCode == '+')
      return &Plus;
   else
      return &Minus; // default if invalid operator was passed
}

// Execute example code
void Return_A_Function_Pointer()
{
   cout << endl << "Executing 'Return_A_Function_Pointer'" << endl;

   // define a function pointer and initialize it to NULL
   float (*pt2Function)(float, float) = NULL;

   pt2Function=GetPtr1('+');   // get function pointer from function 'GetPtr1'
   cout << (*pt2Function)(2, 4) << endl;   // call function using the pointer

   pt2Function=GetPtr2('-');   // get function pointer from function 'GetPtr2'
   cout << (*pt2Function)(2, 4) << endl;   // call function using the pointer
}

8. 如何使用函數指針數組
    操作函數指針數組非常有趣。它提供了選擇一個函數作爲索引的可能。它的語法很難,經常容易導致疑惑。下面是兩種如何在C/C++中定義和使用函數指針數組的方式。第一種是使用typedef,第二種直接定義數組。實際中可以選擇自己偏好的方式。
//------------------------------------------------------------------------------------
// 2.8 How to Use Arrays of Function Pointers

// C ---------------------------------------------------------------------------------

// type-definition: 'pt2Function' now can be used as type
typedef int (*pt2Function)(float, char, char);

// illustrate how to work with an array of function pointers
void Array_Of_Function_Pointers()
{
   printf("/nExecuting 'Array_Of_Function_Pointers'/n");

   // define arrays and ini each element to NULL, <funcArr1> and <funcArr2> are arrays
   // with 10 pointers to functions which return an int and take a float and two char

   // first way using the typedef
   pt2Function funcArr1[10] = {NULL};

   // 2nd way directly defining the array
   int (*funcArr2[10])(float, char, char) = {NULL};


   // assign the function's address - 'DoIt' and 'DoMore' are suitable functions
   // like defined above in 2.1-4
   funcArr1[0] = funcArr2[1] = &DoIt;
   funcArr1[1] = funcArr2[0] = &DoMore;

   /* more assignments */

   // calling a function using an index to address the function pointer
   printf("%d/n", funcArr1[1](12, 'a', 'b'));         //  short form
   printf("%d/n", (*funcArr1[0])(12, 'a', 'b'));      // "correct" way of calling
   printf("%d/n", (*funcArr2[1])(56, 'a', 'b'));
   printf("%d/n", (*funcArr2[0])(34, 'a', 'b'));
}


// C++ -------------------------------------------------------------------------------

// type-definition: 'pt2Member' now can be used as type
typedef int (TMyClass::*pt2Member)(float, char, char);

// illustrate how to work with an array of member function pointers
void Array_Of_Member_Function_Pointers()
{
   cout << endl << "Executing 'Array_Of_Member_Function_Pointers'" << endl;

   // define arrays and ini each element to NULL, <funcArr1> and <funcArr2> are
   // arrays with 10 pointers to member functions which return an int and take
   // a float and two char

   // first way using the typedef
   pt2Member funcArr1[10] = {NULL};

   // 2nd way of directly defining the array
   int (TMyClass::*funcArr2[10])(float, char, char) = {NULL};


   // assign the function's address - 'DoIt' and 'DoMore' are suitable member
   //  functions of class TMyClass like defined above in 2.1-4
   //nd use an array of function pointers in C and C++. The first way uses a typedef, the second way directly defines the array. It's up to you which way you prefer.

   funcArr1[0] = funcArr2[1] = &TMyClass::DoIt;
   funcArr1[1] = funcArr2[0] = &TMyClass::DoMore;
   /* more assignments */

   // calling a function using an index to address the member function pointer
   // note: an instance of TMyClass is needed to call the member functions
   TMyClass instance;
   cout << (instance.*funcArr1[1])(12, 'a', 'b') << endl;
   cout << (instance.*funcArr1[0])(12, 'a', 'b') << endl;
   cout << (instance.*funcArr2[1])(34, 'a', 'b') << endl;
   cout << (instance.*funcArr2[0])(89, 'a', 'b') << endl;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章