con tro ham c++

13
Bài viết về function pointer được trích dịch từ tài liệu này: http://www.newty.de/fpt/index.html 1. 1 Giới thiệu 1. 1.1 Function Pointer là gì? 2. 1.2 Thay thế câu lệnh Switch như thế nào? 2. 2 Syntax của C và C++ function pointer 1. 2.1 Define một function pointer 2. 2.2 Calling Convention 3. 2.3 Gán một địa chỉ vào function pointer 4. 2.4 So sánh các function pointer 5. 2.5 Gọi một hàm sử dụng function pointer 6. 2.6 Truyền function pointer như là một tham số 7. 2.7 Trả về một function pointer 8. 2.8 Sử dụng một mảng các function pointer 3. 3 Implement Callback Functions in C and C++ 1. 3.1 Khái niệm về callback function 2. 3.2 Implement a callback function in C 3. 3.3 Implement a Callback to a static C++ Member Function 4. 3.4 Implement a Callback to a non-static C++ Member Function 1 Giới thiệu Function Pointer cung cấp một kỹ thuật lập trình cực kỳ thú vị, hiệu quả và “đầy màu sắc”. Chúng ta có thể sử dụng nó để thay thế câu lệnh switch/if, xây dựng quá trình late-binding hoặc implement hàm callback. Tiếc thay, có thể vì sự phức tạp của nó mà nó được đề cập rất ít trong hầu hết sách và tài liệu. Nếu có thì nó chỉ được trình bày một cách rất tóm tắt và sơ sài. Thực ra thì nó ít gây ra lỗi hơn so với pointer bình thường bởi vì chúng ta không bao giờ phải allocate hoặc de-allocate bộ nhớ cả. Tất cả việc chúng ta cần làm là hiểu nó làm gì và học cú pháp của nó. Nhưng hãy luôn tâm niệm rằng: hãy tự hỏi bạn có thực sự cần đến function pointer hay không? Rất tuyệt để thể hiện cách thức late-binding, thế nhưng sử dụng cấu trúc hiện tại của C++ làm cho đoạn mã trở nên dễ đọc và rõ ràng hơn. Một khía cạnh khác của late- binding là runtime: nếu bạn gọi một virtual function, chương trình sẽ xác định hàm

Upload: tien-quang-phan

Post on 28-Nov-2014

438 views

Category:

Documents


0 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Con tro ham c++

Bài viết về function pointer được trích dịch từ tài liệu này:

http://www.newty.de/fpt/index.html

1. 1 Giới thiệu

1. 1.1 Function Pointer là gì?

2. 1.2 Thay thế câu lệnh Switch như thế nào?

2. 2 Syntax của C và C++ function pointer

1. 2.1 Define một function pointer

2. 2.2 Calling Convention

3. 2.3 Gán một địa chỉ vào function pointer

4. 2.4 So sánh các function pointer

5. 2.5 Gọi một hàm sử dụng function pointer

6. 2.6 Truyền function pointer như là một tham số

7. 2.7 Trả về một function pointer

8. 2.8 Sử dụng một mảng các function pointer

3. 3 Implement Callback Functions in C and C++

1. 3.1 Khái niệm về callback function

2. 3.2 Implement a callback function in C

3. 3.3 Implement a Callback to a static C++ Member Function

4. 3.4 Implement a Callback to a non-static C++ Member Function

1 Giới thiệu Function Pointer cung cấp một kỹ thuật lập trình cực kỳ thú vị, hiệu quả và “đầy màu

sắc”. Chúng ta có thể sử dụng nó để thay thế câu lệnh switch/if, xây dựng quá trình

late-binding hoặc implement hàm callback. Tiếc thay, có thể vì sự phức tạp của nó

mà nó được đề cập rất ít trong hầu hết sách và tài liệu. Nếu có thì nó chỉ được trình

bày một cách rất tóm tắt và sơ sài. Thực ra thì nó ít gây ra lỗi hơn so với pointer bình

thường bởi vì chúng ta không bao giờ phải allocate hoặc de-allocate bộ nhớ cả. Tất

cả việc chúng ta cần làm là hiểu nó làm gì và học cú pháp của nó. Nhưng hãy luôn

tâm niệm rằng: hãy tự hỏi bạn có thực sự cần đến function pointer hay không? Rất

tuyệt để thể hiện cách thức late-binding, thế nhưng sử dụng cấu trúc hiện tại của

C++ làm cho đoạn mã trở nên dễ đọc và rõ ràng hơn. Một khía cạnh khác của late-

binding là runtime: nếu bạn gọi một virtual function, chương trình sẽ xác định hàm

Page 2: Con tro ham c++

nào được gọi. Nó làm điều đó bằng cách sử dụng V-Table mà chứa tất cả những hàm

có thể gọi. Điều đó có vẻ hơi lãng phí mỗi lần gọi, và có thể bạn sẽ tiết kiệm một

chút nếu sử dụng function pointer thay vì virtual function. Cũng có thể không … 1.1 Function Pointer là gì? Function pointer là một pointer mà nó chỉ đến địa chỉ của một hàm. Bạn phải luôn

giữ trong đầu rằng một chương trình chạy sẽ chiếm một không gian bộ nhớ xác định

trong bộ nhớ chính. Cả đoạn chương trình thực thi đã được dịch từ mã mà bạn viết

và các biến sử dụng đều được đưa vào trong không gian bộ nhớ này. Vì vậy một

function trong chương trình của bạn không có gì khác hơn là một địa chỉ trong bộ

nhớ. 1.2 Thay thế câu lệnh Switch như thế nào? Khi chúng ta muốn gọi một hàm DoIt() ở một label xác định trong chương trình,

chúng ta phải để lời gọi tới hàm DoIt() tại label đó. Sau đó biên dịch và mỗi khi

chương trình chạy tới label đó thì hàm DoIt() sẽ được gọi. Mọi thứ đều ok, nhưng sẽ

làm gì nếu giả sử chúng ta không biết tại thời điểm build-time (thời gian dịch) hàm

nào sẽ được gọi? Nghĩa là chỉ đến lúc chạy ta mới biết ở label đó thì nên chạy DoIt()

hay một hàm nào khác. Đó chính là lúc chúng ta muốn sử dụng đến callback-function

hoặc là sử dụng kỹ thuật lấy ra từ một “pool” chứa các possible function. Tuy nhiên

thì chúng ta có thể giải quyết vấn đề này bằng cách sử dụng lệnh switch, và lựa chọn

lời gọi đến hàm thích hợp ở những nhánh khác nhau tùy theo giá trị biểu thức của

switch. Nhưng vẫn có một cách khác là sử dụng function pointer. Trong ví dụ sau đây

chúng ta thực hiện nhiệm vụ của bốn toán tử toán học cơ bản (+, -, *, /). Cách đầu

tiên sử dụng switch và cách thứ hai sử dụng function pointer. //-----------------------------------------------------------------------------

-

// 1.2 Introductory Example or How to Replace a Switch-Statement

// Task: Perform one of the four basic arithmetic operations specified by the

// characters '+', '-', '*' or '/'.

// The four arithmetic operations ... one of these functions is selected

// at runtime with a swicth or a function pointer

float Plus (float a, float b) {

return a+b;

}

float Minus (float a, float b) {

return a-b;

}

float Multiply(float a, float b) {

return a*b;

}

float Divide (float a, float b) {

Page 3: Con tro ham c++

return a/b;

}

// Solution with a switch-statement - <opCode> specifies which operation to

execute

void Switch(float a, float b, char opCode) {

float result;

// execute operation

switch(opCode) {

case '+' :

result = Plus (a, b);

break;

case '-' :

result = Minus (a, b);

break;

case '*' :

result = Multiply (a, b);

break;

case '/' :

result = Divide (a, b);

break;

}

cout << "Switch: 2+5=" << result << endl; // display result

}

// Solution with a function pointer - <pt2Func> is a function pointer and

points to

// a function which takes two floats and returns a float. The function pointer

// "specifies" which operation shall be executed.

void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float,

float)) {

float result = pt2Func(a, b); // call using function pointer

cout << "Switch replaced by function pointer: 2-5="; // display result

cout << result << endl;

}

// Execute example code

void Replace_A_Switch() {

cout << endl << "Executing function 'Replace_A_Switch'" << endl;

Switch(2, 5, /* '+' specifies function 'Plus' to be executed */ '+');

Switch_With_Function_Pointer(2, 5, /* pointer to function 'Minus' */

&Minus);

}

Chú ý: Một function pointer luôn trỏ đến một function đặc biệt nên tất cả những

function mà chúng ta muốn sử dụng với cùng một function pointer thì phải có cùng

tham số và giá trị trả về. Nói một cách khác là cùng prototype. 2 Syntax của C và C++ function pointer Dựa vào cú pháp thì có hai loại function pointer khác nhau: một là những function

pointer trỏ đến C function hoặc static C++ member function, một là những function

pointer tới non-static C++ member function. Sự khác biệt cơ bản là tất cả pointer

đến non-static member function cần một tham số ẩn: con trỏ this tới instance của

class. Vậy chỉ cần nhớ rằng có hai loại function pointer không tương thích với nhau. 2.1 Define một function pointer Vì function pointer không khác gì hơn một biến nên nó phải được define giống như

thông thương. Ví dụ dưới đây chúng ta khai báo các function pointer tên là

Page 4: Con tro ham c++

pt2Function, pt2Member và pt2ConstMember. Chúng trở đến function và lấy một

biến float và hai biến char và trả về một số int. Ở ví dụ C++ chúng ta giả sử rằng

function mà function pointer trỏ đến là non-static member function của 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.2 Calling Convention Thông thường chúng ta không phải nghĩ về calling convention của một function.

Trình biên dịch giả định rằng cdecl là convention mặc định nếu chúng ta không sử

dụng một convention khác. Nếu bạn muốn tìm hiểu kỹ hơn hãy tìm đọc. Calling

convention nói cho trình biên dịch biết cách truyền tham số và cách tạo ra một

function. Ví dụ về những calling convention khác là stdcall, pascal, fastcall. Nếu

function và function pointer khác calling convention thì chúng cũng không tương

thích với nhau và không thể thực hiện phép gán function pointer vào địa chỉ của

function kia. Đối với trình biên dịch của Borland và Microsoft thì cần khai báo calling

convention ở giữa kiểu trả về và tên hàm hay tên function pointer. Đối với GNU GCC

thì sử dụng từ khóa __attribute__: viết khai báo hàm theo sau bởi từ khóa

__attribute__ và sau đó là trạng thái của calling convention ở trong1. // 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

2.3 Gán một địa chỉ vào function pointer Rất dễ dàng để gán một địa chỉ của một function vào function pointer. Đơn giản chỉ

cần lấy tên của function hoặc member function thích hợp. Mặc dù hầu hết các

compiler support việc đó nhưng tốt hơn hết là chúng ta sử dụng toán tử địa chỉ & và

đặt trước các function name để viết những đoạn mã portable. Chúng ta cũng phải sử

dụng tên đầy đủ của member function bao gồm tên lớp và toán tử scope (::). Chúng

ta cũng phải đảm bảo rằng chúng ta được quyền truy nhập vào function ở bên trong

scope đó. // 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");

Page 5: Con tro ham c++

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

2.4 So sánh các function pointer Chúng ta có thể sử dụng các toán tử so sánh (==, !=) như bình thường. Trong ví dụ

dưới đây nó được kiểm tra xem pt2Function và pt2Member có thực sự chứa địa chỉ

của hàm DoIt() và 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;

2.5 Gọi một hàm sử dụng function pointer Trong C, chúng ta gọi một hàm sử dụng function pointer bằng cách explicitly

dereferencing nó bằng toán tử *. Một lựa chọn khác là sử dụng function pointer thay

vì function name. Trong C++ hai toán tử .* và ->* được sử dụng cùng với instance

của một class để gọi một (non-static) member function. Nếu lời gọi diễn ra bên trong

một member function khác, chúng ta có thể sử dụng con trỏ 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;

2.6 Truyền function pointer như là một tham số

Page 6: Con tro ham c++

Chúng ta có thể truyền function pointer như một tham số của một function được gọi

khác. Điều đó rất cần thiết nếu chúng ta muốn truyền một con trỏ tới một callback

function. Đoạn mã dưới đây chỉ cách truyền một pointer tới một function mà trả về

một số nguyên và lấy một số float và 2 char làm tham số: //-----------------------------------------------------------------------------

-------

// 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);

}

2.7 Trả về một function pointer Hơi mẹo một chút nhưng một function pointer có thể là giá trị trả về của một

function. Trong ví dụ sau đây có hai giải pháp cho việc trả về một function pointer.

Nếu muốn trả về một pointer vào một member function, chúng ta phải thay đổi

definitions và declarations của tất cả các function pointer. //-----------------------------------------------------------------------------

-------

// 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'

Page 7: Con tro ham c++

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

}

2.8 Sử dụng một mảng các function pointer Sử dụng một mảng các function pointer khá thú vị. Nó cho phép khả năng lựa chọn

một function sử dụng chỉ số (index). Cú pháp thì khá phức tạp và thường xuyên dẫn

đến sự nhầm lẫn. Đoạn mã dưới đây bạn sẽ tìm thấy hai cách sử dụng một mảng các

function pointer trong C và C++. Cách thứ nhất sử dụng typedef và cách thứ hai sử

dụng trức tiếp cách khai bao mảng. Nó tùy thuộc vào bạn thích cách nào hơn. //-----------------------------------------------------------------------------

-------

// 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

Page 8: Con tro ham c++

};

// 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

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;

}

3 Implement Callback Functions in C and C++ 3.1 Khái niệm về callback function Function pointer cung cấp khái niệm về callback function. Trong bài viết này sẽ giới

thiệu về callback function thông qua một hàm về giải thuật sắp xếp nổi tiếng là

qsort. Hàm này sẽ sắp xếp các phần tử theo một tiêu chuẩn sắp xếp mà user định

nghĩa. Kích thước của một item và số item cần sắp xếp sẽ được truyền vào hàm này.

Câu hỏi đặt ra là: làm thế nào để hàm qsort có thể sắp xếp các phần tử mà không có

một thông tin gì về kiểu của phần tử sắp xếp? Câu trả lời rất đơn giản: hàm qsort sẽ

nhận một function pointer tới một hàm so sánh như một tham số. Hàm so sánh sẽ

nhận hai tham số kiểu con trỏ void tới hai phần tử và sẽ đánh giá thứ tự của hai

phần tử đó và trả về một số int. Do đó mỗi lần giải thuật sắp xếp cần đánh giá ví trí

của hai phần tử thì đơn giản chỉ là gọi tới hàm so sánh thông qua function pointer. 3.2 Implement a callback function in C Hãy tham khảo khai báo của hàm qsort dưới đây: void qsort(void* field, size_t nElements, size_t sizeOfAnElement,

int(_USERENTRY *cmpFunc)(const void *, const void*));

field trỏ tới phần tử đầu tiên của mảng cần sắp xếp, nElements là số phần tử cần sắp

xếp, sizeOfAnElement là kích thước của một phần tử tính theo byte, và cmpFunc là

function pointer tới hàm so sánh. Hàm so sánh này lấy hai phần tử const void* và trả

về một số nguyên. 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 */

}

Ví dụ mẫu sử dụng qsort

Page 9: Con tro ham c++

//-----------------------------------------------------------------------------

------------

// 3.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");

}

3.3 Implement a Callback to a static C++ Member Function Implement callback tới static C++ member function cũng giống như implement

callback tới hàm C. Static member function không cần một object để thực thi vì vậy

nó phải có cùng một prototype như là hàm C với cùng một calling convention, tham

số và giá trị trả về. 3.4 Implement a Callback to a non-static C++ Member Function Function pointer tới non-static member thì khác với là một con trỏ hàm C vì nó cần

phải truyền một con trỏ this của object. Nếu bạn chỉ muốn gọi hàm callback tới một

member function của một class cụ thể thì chỉ cần chuyển đoạn mã từ một function

pointer bình thường tới con trỏ tới member function. Nhưng sẽ làm thế nào nếu

muốn gọi hàm callback tới một non-static member của một class chưa rõ nào đó (có

thể ở các class khác nhau). Nó khó hơn một chút. Chúng ta cần phải viết một static

member function như là một wrapper. Static member function thì được coi như là

một function bình thường. Sau đó thì chúng ta sẽ ép kiểu con trỏ tới object mà chúng

Page 10: Con tro ham c++

ta muốn nó thực hiện member function, thành void* và truyền nó tới wrapper như là

một tham số phụ thêm hoặc là thông qua một biến toàn cục. Tất nhiên chúng ta

cũng truyền các tham số gọi tới member function. Wrapper sẽ ép kiểu con trỏ void

thành con trỏ tới object của class tương ứng của nó và gọi member function (kỹ

thuật này giống với kỹ thuật chúng ta xây dựng class Thread). Dưới đây là hai ví dụ:

Ví dụ A: Con trỏ tới object được truyền như là một tham số phụ thêm: Hàm DoItA()

thực hiện với object của class TClassA để gọi hàm callback. Vì vậy con trỏ tới object

của class TClassA và con trỏ tới static wrapper function

TClassA::Wrapper_To_Call_Display được truyền tới DoItA(). Hàm wrapper này là

callback function. Chúng ta có thể viết những class khác giống như TClassA và sử

dụng chung với DoItA miễn là những class này cũng cung cấp các function cần thiết.

Chú ý là giải pháp này có thể hữu dụng nếu chúng ta tự thiết kế giao diện của hàm

callback. Nó tốt hơn rất nhiều so với giải pháp thứ hai sử dụng biến toàn cục. //-----------------------------------------------------------------------------

------------

// 3.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);

}

Page 11: Con tro ham c++

Ví dụ B: Con trỏ tới object được lưu trữ trong một biến toàn cục. Hàm DoItB sẽ thực

hiện với đối tượng của class B để gọi hàm callback. Một con trỏ tới static wrapper

function TClassB::Wrapper_To_Call_Display được truyền vào DoItB. Hàm wrapper

này là callback function. Wrapper sử dụng biến toàn cục void* pt2Object và phép ép

kiểu thành một đối tượng của TClassB. Phải luôn chú ý khởi tạo biến toàn cục để chỉ

đến đối tượng của class chính xác. Chúng ta có thể viết những class khác như

TClassB và sử dụng chúng với DoItB miễn là những class đó cung cấp các function

cần thiết. Cách này không phải là một giải pháp tốt bởi vì sử dụng biến toàn cục rất

nguy hiểm và có thể gây ra những lỗi nghiêm trọng. //-----------------------------------------------------------------------------

-

// 3.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);

}

1. [↩]

5 Comments »

Page 12: Con tro ham c++

kiennguyen said:

Sao lại có fpt ở đây nhỉ

- 26 November 2007 at 5:40 pm

[email protected] said:

Thông hiểu được cái này thì sẽ hiểu ngay cơ chế làm việc của thư viện liên kết động (.dll trên Windows và

.so trên Linux) nhỉ. Bài viết quá hay ! Một tràng pháo tay cho bạn Hoàng nào .

- 26 April 2008 at 3:43 pm

thachdaigia47 said:

thank you

- 7 January 2009 at 3:06 pm

van viet said:

thanks tat ca!

dien dan nay tuyet wa

- 20 October 2009 at 11:17 pm

LongVNIT » Function Pointer said:

[...] Thông thường chúng ta không phải nghĩ về calling convention của một function. Trình biên dịch giả định

rằng cdecl là convention mặc định nếu chúng ta không sử dụng một convention khác. Nếu bạn muốn tìm

hiểu kỹ hơn hãy tìm đọc. Calling convention nói cho trình biên dịch biết cách truyền tham số và cách tạo ra

một function. Ví dụ về những calling convention khác là stdcall, pascal, fastcall. Nếu function và function

pointer khác calling convention thì chúng cũng không tương thích với nhau và không thể thực hiện phép

gán function pointer vào địa chỉ của function kia. Đối với trình biên dịch của Borland và Microsoft thì cần

khai báo calling convention ở giữa kiểu trả về và tên hàm hay tên function pointer. Đối với GNU GCC thì sử

Page 13: Con tro ham c++

dụng từ khóa __attribute__: viết khai báo hàm theo sau bởi từ khóa __attribute__ và sau đó là trạng thái

của calling convention ở trong1. [...]