如何在C和C++中實現回調

來源:互聯網
上載者:User
1 回呼函數介紹

函數指標提供了回呼函數的概念。現在我們試著用一個排序函數qsort來理解回調。這個函數根據使用者指定的排序法則來對需要排序的field進行排序。需要排序的field可能是任何類型的;它通過一個void 類型的指標傳遞給qsort 進行排序。當然需排序的field的數量和每個field的size也必須被傳遞。現在問題是:既然需要排序的field的任何類型資訊都沒有被傳遞,那麼qsort怎麼對它們進行排序呢?答案很簡單:qsort函數接收到指向一個比較函數的指標,這個比較函數的參數是兩個void類型的指標,比較函數比較傳入的兩個變數,得出一個int類型的傳回值。因此每當排序函數需要對比兩個item的大小時,它就通過函數指標調用這個比較函數:這就是回調。

2 如何在C語言中實現回調

下面是排序函數 qsort 的聲明:
      void qsort(void* field, size_t nElements, size_t sizeOfAnElement,
     
       int(_USERENTRY *cmpFunc)(const void*, const void*));

field 指向需要被排序的field的第一個成員,nElements 是field中需要被排序的items的數量,sizeOfAnElement 是以bytes為單位的需要排序的items的大小,cmpFunc是指向比較函數的指標,比較函數輸入兩個void類型的參數,返回一個int類型的值. 回調就像調用一個普通函數一樣的被執行:你只需要使用函數指標的名字而不是函數名。


void qsort( ... , int(_USERENTRY *cmpFunc)(const void*, const void*))
{
/* sort algorithm - note: item1 and item2 are void-pointers */
int bigger=cmpFunc(item1, item2); // make callback
/* use the result */
}

3 使用排序函數 qsort 的範例代碼

在下面的例子中一組 floats 類型的items被排序.

//-----------------------------------------------------------------------------------------
// 3 How to make a callback in C by the means of the sort function qsort

#include <stdlib.h> // due to: qsort
#include <time.h> // randomize
#include <stdio.h> // printf

// comparison-function for the sort-algorithm
// two items are taken by void-pointer, converted and compared
int CmpFunc(const void* _a, const void* _b)
{
// you've got to explicitly cast to the correct type
const float* a = (const float*) _a;
const float* b = (const float*) _b;

if(*a > *b) return 1; // first item is bigger than the second one -> return 1
else
if
(*a == *b) return 0; // equality -> return 0
else return -1; // second item is bigger than the first one -> return -1
}

// example for the use of qsort()
void QSortExample()
{
float field[100];

::randomize(); // initialize random-number-generator
for(int c=0;c<100;c++) // randomize all elements of the field
field[c]=random(99);

// sort using qsort()
qsort((void*) field, /*number of items*/ 100, /*size of an item*/ sizeof(field[0]),
/*comparison-function*/ CmpFunc);

// display first ten elements of the sorted field
printf("The first ten elements of the sorted field are .../n");
for(int c=0;c<10;c++)
printf("element #%d contains %.0f/n", c+1, field[c]);
printf("/n");
}

4 如何在一個C++靜態成員函數中實現回調

和在C函數中實現回調是一樣的。靜態成員函數不需要對象來引用,因此實現起來和C相同。

5 如何在一個C++非靜態成員函數中實現回調

Wrapper 方式

指向非靜態成員函數的指標和通常的C函數指標是不一樣的,因為它們需要類的this 指標才能夠被傳遞。如果你只是需要對一個特定類的成員函數實現回調,那麼你只需要把通常的C回調編碼方式改成類成員函數的編碼形式。但是,如果需要對任意一個類的非靜態成員函數實現回調怎麼辦?這有一點點複雜。你需要寫一個靜態成員函數作為wrapper.靜態成員函數和C函數的形式是一樣的。然後你把指向包含有你希望調用的成員函數的對象的指標指向 void* 並把它作為一個附加參數或者全域變數傳遞給 wrapper。如果你使用全域變數方式,你得確認它必須指向正確的對象。當然你也必須把呼叫的參數傳遞給成員函數。wrapper 把void指標指向對應類的一個執行個體並且調用成員函數。下面我們有兩個例子:

Example A: 指向類執行個體的指標作為附加參數被傳遞

函數 DoItA 對於類TClassA的對象實現回調。因此一個指向類TClassA的一個成員的指標以及一個指向靜態wrapper函數 TClassA::Wrapper_To_Call_Display 的指標被傳遞給 DoItA。這個wrapper就是回呼函數。


//-----------------------------------------------------------------------------------------
// 5 Example A: Callback to member function using an additional argument
// Task: The function 'DoItA' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper-function
// 'Wrapper_To_Call_Display is used.

#include <iostream.h> // due to: cout

class TClassA
{
public:

void Display(const char* text) { cout << text << endl; };
static void Wrapper_To_Call_Display(void* pt2Object, char* text);

/* more of TClassA */
};

// static wrapper-function to be able to callback the member function Display()
void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
{
// explicitly cast to a pointer to TClassA
TClassA* mySelf = (TClassA*) pt2Object;

// call member
mySelf->Display(string);
}

// function does something which implies a callback
// note: of course this function can also be a member function
void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text))
{
/* do something */

pt2Function(pt2Object, "hi, i'm calling back using a argument ;-)"); // make callback
}

// execute example code
void Callback_Using_Argument()
{
// 1. instantiate object of TClassA
TClassA objA;

// 2. call 'DoItA' for <objA>
DoItA((void*) &objA, TClassA::Wrapper_To_Call_Display);
}

Example B: 指向類執行個體的指標被儲存在一個全域變數中

函數 DoItB 對於類TClassB的對象實現回調。一個指向靜態函數 TClassB::Wrapper_To_Call_Display 的指標被傳遞給 DoItB. 這個wrapper被稱作回呼函數,它使用全域變數 void* pt2Object 並將其映射到 TClassB的一個執行個體上。非常重要的是:總是初始化一個全域變數並將其指向正確的類執行個體


//-----------------------------------------------------------------------------------------
// 5 Example B: Callback to member function using a global variable
// Task: The function 'DoItB' makes something which implies a callback to
// the member function 'Display'. Therefore the wrapper-function
// 'Wrapper_To_Call_Display is used.

#include <iostream.h> // due to: cout

void* pt2Object; // global variable which points to an arbitrary object

class TClassB
{
public:

void Display(const char* text) { cout << text << endl; };
static void Wrapper_To_Call_Display(char* text);

/* more of TClassB */
};

// static wrapper-function to be able to callback the member function Display()
void TClassB::Wrapper_To_Call_Display(char* string)
{
// explicitly cast global variable <pt2Object> to a pointer to TClassB
// warning: <pt2Object> MUST point to an appropriate object!
TClassB* mySelf = (TClassB*) pt2Object;

// call member
mySelf->Display(string);
}

// function does something which implies a callback
// note: of course this function can also be a member function
void DoItB(void (*pt2Function)(char* text))
{
/* do something */

pt2Function("hi, i'm calling back using a global ;-)"); // make callback
}

// execute example code
void Callback_Using_Global()
{
// 1. instantiate object of TClassB
TClassB objB;

// 2. assign global variable which is used in the static wrapper function
// important: never forget to do this!!
pt2Object = (void*) &objB;

// 3. call 'DoItB' for <objB>
DoItB(TClassB::Wrapper_To_Call_Display);
}

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.