青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

桃源谷

心靈的旅行

人生就是一場旅行,不在乎旅行的目的地,在乎的是沿途的風(fēng)景和看風(fēng)景的心情 !
posts - 32, comments - 42, trackbacks - 0, articles - 0
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

函數(shù)指針教程

Posted on 2009-02-12 16:08 lymons 閱讀(2906) 評論(5)  編輯 收藏 引用 所屬分類: C++C
From 2008精選

 

函數(shù)指針教程 原版:http://www.newty.de/fpt/index.html
譯者:
Lymons Lau

  導(dǎo)引

  1. 函數(shù)指針簡介

  2.  C C++ 函數(shù)指針語法

  3. C C++里怎么實(shí)現(xiàn)回調(diào)函數(shù) ?

  4.  封裝C C++函數(shù)指針的仿函數(shù)

  5. 相關(guān)鏈接

 

   1.1 什么是函數(shù)指針 ?

   1.2  開場例子或者怎么替換Switch語句

函數(shù)指針提供了一些極其有趣,有效和絕妙的編程技術(shù)。你能用它代替switch/if語句來實(shí)現(xiàn)你自己的晚綁定(late-binding)或者作為回調(diào)(callback)來使用。不幸的是可能由于它的語法比較復(fù)雜幾乎所有的電腦書籍和文檔上都講解的不多。即便如此,它們也只是做了相當(dāng)簡單和膚淺的說明。而對于函數(shù)指針你只需要明白它是什么以及它的語法,因?yàn)樗鸵话愕闹羔槺绕饋韽膩聿挥藐P(guān)心內(nèi)存的分配和釋放,所以它被使用的時(shí)候是不易產(chǎn)生錯(cuò)誤的。但你要注意的是: 要時(shí)常問自己是否真的需要函數(shù)指針。因?yàn)殡m然用它來實(shí)現(xiàn)晚綁定也很漂亮,但用既存的C++數(shù)據(jù)結(jié)構(gòu)的話會使代碼更可讀和更簡潔。另外,晚綁定的一方面實(shí)際上就是運(yùn)行期(runtime: 如果你調(diào)用了一個(gè)虛擬函數(shù),你的程序會根據(jù)一個(gè)存儲所有函數(shù)的虛擬表(V-Table)自己來確定到底真正調(diào)用的是哪一個(gè)。這就要花費(fèi)一些時(shí)間而用函數(shù)指針代替虛擬函數(shù)的話有可能會節(jié)省一些時(shí)間。BTW: 現(xiàn)代的編譯器在這方面都做得非常好!就那我的Borland編譯器來說這個(gè)時(shí)間就比調(diào)用一次虛擬函數(shù)能節(jié)省2%

注:(late binding)可能來自c++的術(shù)語,也為動態(tài)(dynamic binding),它主要是來實(shí)現(xiàn)態(tài)機(jī)制。既是一在運(yùn)行時(shí)動態(tài)確定語義的機(jī)制。

1.1 什么是函數(shù)指針?

函數(shù)指針就是一個(gè)指針,也就是一個(gè)指向函數(shù)地址的變量。你必須注意的是,一個(gè)正在運(yùn)行的程序在主內(nèi)存內(nèi)獲得一段固定的空間,并且編譯出來的可執(zhí)行程序代碼和代碼中的變量都駐留在這段內(nèi)存里。而在這個(gè)程序代碼里的一個(gè)函數(shù)無非就是一個(gè)地址。重要的是你只要知道或者說你的編譯器/處理器,怎么來解釋一個(gè)指針指向的那段內(nèi)存中的內(nèi)容。

1.2 開場例子和怎么來代替一個(gè)Switch-語句

當(dāng)你想要在程序中的某一個(gè)地方調(diào)用函數(shù)DoIt()的時(shí)候, 你只需要在源代碼的這個(gè)地方放上函數(shù)DoIt()的調(diào)用即可.那么,在編譯完這段代碼后當(dāng)你的程序執(zhí)行到這個(gè)地方時(shí)這個(gè)DoIt()函數(shù)就會被調(diào)用.好像看上來一切都ok.但是,如果你不知道在代碼的構(gòu)建時(shí)期(build-time)哪一個(gè)函數(shù)將要被調(diào)用的話你能做什么呢?在運(yùn)行期你要是決定哪一個(gè)函數(shù)要被調(diào)用的話那你需要做什么呢?這時(shí)你可能會使用一個(gè)回調(diào)函數(shù)(Callback-Function)或者你想在一堆函數(shù)列表中選擇其中的一個(gè)。然而,你也能使用switch語句,在想要調(diào)用函數(shù)的地方使用不同的分支來解決這個(gè)問題。但是,這里我們講述的是另一個(gè)方式:就是使用函數(shù)指針!

在下面的例子中,我們能看到它的主要處理就是執(zhí)行一個(gè)算術(shù)操作(共有4個(gè)算術(shù)操作)。首先是使用了switch語句來實(shí)現(xiàn),另外還使用了函數(shù)指針處理這個(gè)調(diào)用。這僅僅是個(gè)處理簡單的例子,我想可能正常情況下沒有人會使用函數(shù)指針來這么作吧;-)

 
//------------------------------------------------------------------------------------
// 1.2 開場例子和怎么替代一個(gè)Switch-語句
// 任務(wù):通過字符'+', '-', '*' 和 '/'
//       選擇執(zhí)行一個(gè)基本的算術(shù)操作. 

// 四個(gè)算術(shù)操作  使用swicth或者一個(gè)函數(shù)指針
// 在運(yùn)行期選擇這些函數(shù)中的一個(gè)
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) { return a/b; }


// switch-語句的解決方案 - <opCode> 要選擇那一個(gè)函數(shù)的操作碼
void Switch(float a, float b, char opCode)
{
   
float result;

   
// 執(zhí)行操作
   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;         // 顯示結(jié)果
}
 

// 函數(shù)指針的解決方案 - <pt2Func> 是一個(gè)指向帶有兩個(gè)float型參數(shù)
// 和float型返回值的函數(shù). 這個(gè)函數(shù)指針“指定”了那一個(gè)函數(shù)將被執(zhí)行.
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(floatfloat))
{
   
float result = pt2Func(a, b);    // 調(diào)用函數(shù)指針

   cout 
<< "Switch replaced by function pointer: 2-5=";  // 顯示結(jié)果
   cout << result << endl;
}


// 執(zhí)行樣例代碼
void Replace_A_Switch()
{
   cout 
<< endl << "Executing function 'Replace_A_Switch'" << endl;

   Switch(
25/* '+' 指定了 'Plus'函數(shù)將被執(zhí)行 */ '+');
   Switch_With_Function_Pointer(
25/* 指向'Minus'函數(shù)的指針 */ &Minus);
}

提示:一個(gè)函數(shù)指針總是使用一個(gè)特定的標(biāo)識來指向一個(gè)函數(shù)!然而,一旦你想要用一個(gè)函數(shù)指針指向很多函數(shù),那的保證這些函數(shù)擁有相同的參數(shù)和返回值。

 

  導(dǎo)引

  1. 函數(shù)指針簡介

  2.  C C++ 函數(shù)指針語法

  3. C C++里怎么實(shí)現(xiàn)回調(diào)函數(shù) ?

  4.  封裝C C++函數(shù)指針的仿函數(shù)

  5. 相關(guān)鏈接

 

2.  C C++ 函數(shù)指針語法

   2.1  定義函數(shù)指針

   2.2  調(diào)用規(guī)約

   2.3  給函數(shù)指針賦值

   2.4  比較函數(shù)指針

   2.5  通過函數(shù)指針調(diào)用函數(shù)

   2.6  怎么把函數(shù)指針作為參數(shù)進(jìn)行傳遞 ?

   2.7  怎么返回一個(gè)函數(shù)指針?

   2.8  怎么使用函數(shù)指針數(shù)組?

 

2.1  定義函數(shù)指針

在語法上, 函數(shù)指針有兩種不同的類型: 一種是指向普通函數(shù)或靜態(tài)C++成員函數(shù)的指針. 另一種是指向非靜態(tài)C++成員函數(shù). 它們之間基本的區(qū)別就是所有指向非靜態(tài)成員函數(shù)的函數(shù)指針需要一個(gè)隱含參數(shù): 成員函數(shù)所屬類的實(shí)例. 經(jīng)常要注意的是: 這兩種類型之間的函數(shù)指針是相互不兼容的.

因?yàn)楹瘮?shù)指針無非就是一個(gè)變量, 所以它的定義也跟正常變量一樣定義. 在下面的例子里,我們定義了3個(gè)函數(shù)指針,分別是pt2Function, pt2Member pt2ConstMember. 它們指向的函數(shù)的參數(shù)(一個(gè)float和兩個(gè)char)和返回值都相同. C++ 的例子里,指向的函數(shù)都是必須是類TMyClass的成員函數(shù).

// 2.1 定義一個(gè)函數(shù)指針并初始化為NULL
int (*pt2Function)(floatcharchar= NULL;                        // C
int (TMyClass::*pt2Member)(floatcharchar= NULL;                // C++
int (TMyClass::*pt2ConstMember)(floatcharcharconst = NULL;     // C++

2.2  調(diào)用規(guī)約

一般的情況下你不用考慮一個(gè)函數(shù)的調(diào)用規(guī)約(calling convention): 如果你沒有指定另外的規(guī)約的話編譯器是把__cdecl 作為默認(rèn)的規(guī)約. 如果你想要了解的更多的話, 那就繼續(xù)往后讀吧... 我們說的這個(gè)調(diào)用規(guī)約就是告訴編譯器做一些時(shí)而,比如怎么去傳遞參數(shù)或者怎么去生成函數(shù)的名字. 某些例子里還有其它的調(diào)用規(guī)約如__stdcall, __pascal __fastcall. 這些調(diào)用規(guī)約實(shí)際上是屬于函數(shù)的標(biāo)識(signature): 那么函數(shù)與和自己有些不同調(diào)用規(guī)約的函數(shù)指針之間是相互不兼容的! 對于Borland Microsoft 的編譯器規(guī)定你要指定的調(diào)用規(guī)約應(yīng)該放在返回值和函數(shù)或函數(shù)名之間. 而對于GNU GCC 的編譯器是使用 __attribute__ 關(guān)鍵字: 也就是通過關(guān)鍵字__attribute__把調(diào)用規(guī)約寫在函數(shù)定義的后面并用雙括號把它括上. 如果有誰還知道更多的調(diào)用規(guī)約的話: 請讓我知道;-) 另外,你想要知道函數(shù)調(diào)用在編譯規(guī)約下是怎么工作的,請閱讀Paul Carter PC Assembly Tutorial Subprograms 這一章節(jié).

// 2.2 定義一個(gè)調(diào)用規(guī)約
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  給函數(shù)指針賦值

把一個(gè)函數(shù)的地址賦給一個(gè)函數(shù)指針是非常容易. 你只要知道函數(shù)和成員函數(shù)的名字. 盡管大多數(shù)編譯器需要在函數(shù)名的前面加上一個(gè)地址符& 但為了代碼的可移植行你應(yīng)該這么做. 當(dāng)指向成員函數(shù)的時(shí)候你還需要在該函數(shù)前面加上類名和域操作符(::). 你還要保證, 在你賦值的地方已經(jīng)被允許訪問該函數(shù).

// 2.3 給函數(shù)指針賦值
//     注意: 盡管你能刪掉這個(gè)地址符也能在大多數(shù)的編譯器編譯通過
//     但是為了提高程序的移植性你應(yīng)該使用這個(gè)正確的方法. 

// 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;      // 短格式
pt2Function = &DoMore;   // 使用地址符的正確賦值方法 

// 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; //使用地址符的正確賦值方法
pt2Member = &TMyClass::DoIt; // 注意: <pt2Member> 也可以指向 &DoMore函數(shù)

2.4  比較函數(shù)指針

對于函數(shù)指針你也能像正常寫法一樣使用比較操作符(==, !=). 在下面的例子里,檢查pt2Function pt2Member 是否是真的等于函數(shù)DoIt TMyClass::DoMore 的地址. 相當(dāng)?shù)臅r(shí)候輸出一段字符串.

// 2.4 比較函數(shù)指針 

// C
if(pt2Function >0){                           // 檢查是否被初始化
   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  通過函數(shù)指針調(diào)用函數(shù)

C語言里你能顯示地使用解引用操作符*來調(diào)用一個(gè)函數(shù). 還可以直接使用函數(shù)指針來代替你要調(diào)用函數(shù)的名字. C++ 里面.* ->* 這兩個(gè)操作符可以分別與類事例在一起使用來調(diào)用這些類的成員函數(shù)(非靜態(tài)). 如果這個(gè)調(diào)用發(fā)生這些類的成員函數(shù)里則可以使用this-指針.

// 2.5 使用函數(shù)指針調(diào)用函數(shù)
int result1 = pt2Function    (12'a''b');          // C偷懶格式
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++ 如果this指針能被使用 

TMyClass
* instance2 = new TMyClass;
int result4 = (instance2->*pt2Member)(12'a''b');  // C++, instance2 是一個(gè)指針
delete instance2;

2.6  怎么把一個(gè)函數(shù)指針作為參數(shù)進(jìn)行傳遞?

你能把一個(gè)函數(shù)指針作為一個(gè)函數(shù)的調(diào)用參數(shù). 下列代碼顯示了怎么傳遞一個(gè)函數(shù)指針(返回值為int型,第一個(gè)參數(shù)為float型,第二,三個(gè)參數(shù)都為char型):

//------------------------------------------------------------------------------------
// 2.6 怎么傳遞一個(gè)函數(shù)指針 

// <pt2Func> 是一個(gè)指向帶有一個(gè)float型和兩個(gè)int型參數(shù)以及返回值是int型的函數(shù)
void PassPtr(int (*pt2Func)(floatcharchar))
{
   
int result = (*pt2Func)(12'a''b');     // 調(diào)用函數(shù)指針
   cout << result << endl;


// 執(zhí)行樣例代碼 - 'DoIt' 是在上面2.1-4定義個(gè)函數(shù) 
void Pass_A_Function_Pointer()
{
   cout 
<< endl << "Executing 'Pass_A_Function_Pointer'" << endl;
   PassPtr(
&DoIt);
}

2.7  怎么返回函數(shù)指針 ?

把一個(gè)函數(shù)指針作為返回值需要一個(gè)小技巧. 就像下面的例子一樣有兩種方法來返回一個(gè)帶有兩個(gè)參數(shù)和返回值的函數(shù)指針. 如果你想要返回一個(gè)指向成員函數(shù)的指針你只需改一下函數(shù)指針的定義/聲明.

//---------------------------------------------------------------------------
// 2.7 怎么返回一個(gè)函數(shù)指針
//     'Plus' 和'Minus'參看前面的定義. 它們都返回一個(gè)float 和 帶有兩個(gè)float參數(shù) 

// 直接方案: 定義了一個(gè)帶有char型參數(shù)并且返回一個(gè)指向帶有兩個(gè)float型和返回值為float
// 型的函數(shù). <opCode>則是決定哪個(gè)函數(shù)被返回
float (*GetPtr1(const char opCode))(floatfloat)
{
   
if(opCode == '+')
      
return &Plus;
   
else
      
return &Minus; // 如果傳遞的參數(shù)為無效時(shí),是缺省函數(shù)
}

// 使用typedef的方案: 定義一個(gè)指向帶有兩個(gè)floats型和返回值是float型的函數(shù)
typedef float(*pt2Func)(floatfloat); 

// 定義帶有一個(gè)char型參數(shù)和返回一個(gè)上面定一個(gè)的函數(shù)指針的函數(shù)
// <opCode> 決定那一個(gè)函數(shù)被返回
pt2Func GetPtr2(const char opCode)
{
   
if(opCode == '+')
      
return &Plus;
   
else
      
return &Minus; //如果傳遞的參數(shù)為無效時(shí),是缺省函數(shù)


// 執(zhí)行樣例代碼
void Return_A_Function_Pointer()
{
   cout 
<< endl << "Executing 'Return_A_Function_Pointer'" << endl; 

   
// 定義函數(shù)指針并初始化為NULL
   float (*pt2Function)(floatfloat= NULL;

   pt2Function
=GetPtr1('+');   // 通過函數(shù)指針'GetPtr1'得到調(diào)用函數(shù)
   cout << (*pt2Function)(24<< endl;   // 調(diào)用該函數(shù) 

   pt2Function
=GetPtr2('-');   //通過函數(shù)指針'GetPtr2'得到調(diào)用函數(shù)
   cout << (*pt2Function)(24<< endl;   //調(diào)用該函數(shù)
}

2.8  怎么使用函數(shù)指針數(shù)組?

操作函數(shù)指針數(shù)組是非常有意思的事情. 這使得用一個(gè)索引來選擇一個(gè)函數(shù)指針變得可能. 這個(gè)語法表示起來較困難,常常導(dǎo)致混淆. 下面有兩種方法可以在C C++里定義并使用一個(gè)函數(shù)指針數(shù)組. 第一個(gè)方法是使用typedef, 第二個(gè)方法是直接定一個(gè)數(shù)組. 愿意使用那種方法完全取決于你.

//--------------------------------------------------------------------------- 2.8 怎么使用函數(shù)指針數(shù)組 

// C -----------------------------------------------------------------------
// 類型定義: 'pt2Function' 現(xiàn)在能被作為一個(gè)類型來使用
typedef int (*pt2Function)(floatcharchar);

// 闡述一個(gè)函數(shù)指針數(shù)組是怎么工作的
void Array_Of_Function_Pointers()
{
   printf(
"\nExecuting 'Array_Of_Function_Pointers'\n");

   
// 定義一個(gè)數(shù)組并初始化每一個(gè)元素為NULL, <funcArr1> 和 <funcArr2> 是帶有
   
// 10個(gè)函數(shù)指針的數(shù)組

   
// 第一個(gè)方法是使用 typedef
   pt2Function funcArr1[10= {NULL};

   
// 第二個(gè)方法是直接定義這個(gè)數(shù)組
   int (*funcArr2[10])(floatcharchar= {NULL};

   
// 賦于函數(shù)的地址 - 'DoIt' 和 'DoMore' 的定義請參照上面2.1-4
   funcArr1[0= funcArr2[1= &DoIt;
   funcArr1[
1= funcArr2[0= &DoMore;

   
/* 更多的賦值 */

   
// 使用一個(gè)索引來調(diào)用這些函數(shù)指針
   printf("%d\n", funcArr1[1](12'a''b'));         //  偷懶格式
   printf("%d\n", (*funcArr1[0])(12'a''b'));      // "正確" 的調(diào)用方式
   printf("%d\n", (*funcArr2[1])(56'a''b'));
   printf(
"%d\n", (*funcArr2[0])(34'a''b'));
}

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

// 類型定義: 'pt2Member' 現(xiàn)在能被作為類型來使用
typedef int (TMyClass::*pt2Member)(floatcharchar);

// 闡述成員函數(shù)指針是怎么工作的
void Array_Of_Member_Function_Pointers()
{
   cout 
<< endl << "Executing 'Array_Of_Member_Function_Pointers'" << endl;

   
// 定義一個(gè)數(shù)組并初始化每一個(gè)元素為NULL, <funcArr1> 和 <funcArr2> 是帶有
   
// 10個(gè)函數(shù)指針的數(shù)組

   
// 第一個(gè)方法是使用 typedef
   pt2Member funcArr1[10= {NULL};

   
// 第二個(gè)方法是直接定義這個(gè)數(shù)組
   int (TMyClass::*funcArr2[10])(floatcharchar= {NULL};

   
// 賦于函數(shù)的地址 - 'DoIt' 和 'DoMore' 的定義請參照上面2.1-4
   funcArr1[0= funcArr2[1= &TMyClass::DoIt;
   funcArr1[
1= funcArr2[0= &TMyClass::DoMore;
   
/* 更多的賦值 */

   
// 使用一個(gè)索引來調(diào)用這些成員函數(shù)指針
   
// 注意: 要調(diào)用成員函數(shù)則需要一個(gè)TMyClass類的實(shí)例
   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;
}


  導(dǎo)引

  1. 函數(shù)指針簡介

  2.  C C++ 函數(shù)指針語法

  3. C C++里怎么實(shí)現(xiàn)回調(diào)函數(shù) ?

  4.  封裝C C++函數(shù)指針的仿函數(shù)

  5. 相關(guān)鏈接

 

3.  怎么在 C C++中實(shí)現(xiàn)回調(diào)

   3.1  回調(diào)函數(shù)的概念

   3.2  怎么在C 中實(shí)現(xiàn)回調(diào)?

   3.3  使用qsort的例子

   3.4  怎么在C++ 中實(shí)現(xiàn)靜態(tài)成員函數(shù)的回調(diào)?

   3.5 怎么在C++ 中實(shí)現(xiàn)靜態(tài)成員函數(shù)的回調(diào)?

      A: 把類實(shí)例的指針作為附加參數(shù)進(jìn)行傳遞

      B: 把類實(shí)例的指針存儲在全局變量里

3.1  回調(diào)函數(shù)的概念

在回調(diào)函數(shù)的概念中當(dāng)然少不了函數(shù)指針這東西. 如果你不知道怎么使用函數(shù)指針的話你可以去看一下函數(shù)指針簡介 這一章. 我將使用著名的排序函數(shù)qsort來給大家解釋回調(diào)函數(shù)的概念. 這個(gè)函數(shù)可以根據(jù)用戶指定的排序方式來排列一個(gè)區(qū)域中的很多項(xiàng)目之間的順序. 這個(gè)區(qū)域中包含的項(xiàng)目可以是任何類型; 它是通過void-類型的指針被傳遞到這個(gè)排序函數(shù)里. 另外該類型項(xiàng)目的大小和這個(gè)區(qū)域中項(xiàng)目的數(shù)量也要被傳遞到排序函數(shù)里.現(xiàn)在的問題是: 這個(gè)排序函數(shù)在不知道項(xiàng)目類型的任何信息的情況下怎么實(shí)現(xiàn)排序功能的呢? 這個(gè)答案非常簡單: 就是這個(gè)函數(shù)要接收一個(gè)指向帶有兩個(gè)參數(shù)是void型項(xiàng)目指針的比較函數(shù)的函數(shù)指針, 這個(gè)比較函數(shù)則是來估算兩個(gè)項(xiàng)目之間的順序并返回一個(gè)int型的結(jié)果代碼. 因此每一次這個(gè)排序算法需要決定兩個(gè)項(xiàng)目之間的順序時(shí)則僅僅是通過函數(shù)指針來調(diào)用這個(gè)比較函數(shù)即可: 這就是回調(diào)!

3.2  怎么在C里實(shí)現(xiàn)回調(diào)?

下面是函數(shù) qsort 的聲明:

   void qsort(void* field, size_t nElements, size_t sizeOfAnElement,
                    int(_USERENTRY *cmpFunc)(const void*, const void*));

field 指向要被排序的那個(gè)域中的第一個(gè)元素, nElements 是這個(gè)域里的項(xiàng)目數(shù)量, sizeOfAnElement 則是用字節(jié)表示的一個(gè)項(xiàng)目的大小并且 cmpFunc 指向比較函數(shù)的函數(shù)指針. 這個(gè)比較函數(shù)帶有兩個(gè)void-型指針的參數(shù)并返回一個(gè)型的返回值int. 在函數(shù)的定義里怎么把函數(shù)指針作為一個(gè)參數(shù)來使用的語法看起來有一些陌生. 只要看看, 怎么定義一個(gè)函數(shù)指針這一章你就能發(fā)現(xiàn)它完全就是相同的. 執(zhí)行一個(gè) 回調(diào)就像普通函數(shù)被調(diào)用那樣簡單: 你只需要使用函數(shù)指針的名字而不是那個(gè)函數(shù)的名字. 就像下面顯示的那樣. 注意: 下面的代碼中除了函數(shù)指針?biāo)械膮?shù)都被省略了因?yàn)槲覀冎皇顷P(guān)注跟函數(shù)指針相關(guān)的事情.

 
   void qsort(  , int(_USERENTRY *cmpFunc)(const void*const void*))
   {
      
/* 排序算法  - 注意: item1 和 item2 是 void-型的指針 */ 

      
int bigger=cmpFunc(item1, item2);  // 做一個(gè)回調(diào) 

      
/* 使用上面的結(jié)果 */
   }

3.3  qsort的使用例子

在下面的例子里排序 floats 型的項(xiàng)目.

 
 //-----------------------------------------------------------------------------------------
   
// 3.3 通過qsort排序函數(shù)的方法在C 中實(shí)現(xiàn)回調(diào)

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

   
// 排序算法的比較函數(shù)
   
// 兩個(gè)被比較的項(xiàng)目的類型都是 void-指針, 先作轉(zhuǎn)換后作比較
   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(*> *b) return 1;              // first item is bigger than the second one -> return 1
      else
         
if(*== *b) return  0;         // equality -> return 0
         else         return -1;         // second item is bigger than the first one -> return -1
   }

   
// 使用qsort()的例子
   void QSortExample()
   {
      
float field[100];

      ::randomize();                     
// 初始化隨機(jī)數(shù)生成器
      for(int c=0;c<100;c++)             // 給域中的每個(gè)元素設(shè)定隨機(jī)值
         field[c]=random(99);

      
// 用 qsort()進(jìn)行排序
      qsort((void*) field, /*項(xiàng)目的數(shù)量*/ 100/*一個(gè)項(xiàng)目的大小*/ sizeof(field[0]),
            
/*比較函數(shù)*/ CmpFunc);

      
// 排完序后顯示前10個(gè)元素
      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.4  實(shí)現(xiàn) C++ 靜態(tài)成員函數(shù)的回調(diào)?

這跟在C里的實(shí)現(xiàn)是完全一樣. 靜態(tài)成員函數(shù)不需要調(diào)用類對象并就像C函數(shù)一樣擁有相同標(biāo)識,相同的調(diào)用規(guī)約,調(diào)用參數(shù)以及返回值.

3.5 實(shí)現(xiàn) C++ 靜態(tài)成員函數(shù)的回調(diào)?

封裝方法

指向非靜態(tài)成員的指針和普通的C指針是不一樣的,因?yàn)樗鼈冃枰獋鬟f一個(gè)類對象的this-指針. 而且普通的函數(shù)指針和非靜態(tài)成員函數(shù)有些不同并存在不兼容的標(biāo)識(signatures)! 如果你想要回調(diào)一個(gè)指定類的成員函數(shù)那你得把你的代碼從普通的函數(shù)指針改成一個(gè)指向成員函數(shù)的指針. 但是如果你想要回調(diào)一個(gè)任意類的非靜態(tài)成員函數(shù)那你能怎么做呢? 這有一點(diǎn)兒困難. 你需要寫一個(gè)靜態(tài)的成員函數(shù)作為包裝函數(shù). 一個(gè)靜態(tài)成員函數(shù)擁有和C函數(shù)一樣的標(biāo)識! 然后你要把要調(diào)用的成員函數(shù)的類對象指針強(qiáng)轉(zhuǎn)為void* 并作為附加參數(shù)或者全局變量傳遞到包裝函數(shù)里. 有一點(diǎn)比較重要,如果你使用全局變量時(shí)你必須得保證這個(gè)指針總是指向一個(gè)正確的類對象! 當(dāng)然你也得為成員函數(shù)傳遞那些調(diào)用參數(shù). 這個(gè)包裝函數(shù)把void指針強(qiáng)轉(zhuǎn)為對應(yīng)類的實(shí)例的指針并調(diào)用這個(gè)成員函數(shù). 在后面你能看到兩個(gè)例子.

A: 作為附加參數(shù)來傳遞類實(shí)例的指針

函數(shù) DoItA 利用TClassA類(包含有一個(gè)回調(diào))的對象作一些事情. 因此指向靜態(tài)包裝函數(shù)TClassA::Wrapper_To_Call_Display的指針要被傳遞到函數(shù)DoItA. 這個(gè)包裝函數(shù)是一個(gè)回調(diào)函數(shù). 你也能隨便寫一個(gè)類似于TClassA的類并在函數(shù)DoItA中使用當(dāng)然前提是那些類立提供了包裝函數(shù). 注意:利用回調(diào)函數(shù)作為接口的設(shè)計(jì)方案會比下面使用全局變量的那個(gè)方案要有用的多.

   //-----------------------------------------------------------------------------------------
   
// 3.5 例A: 使用附加參數(shù)來回調(diào)成員函數(shù)
   
// 任務(wù): 函數(shù) 'DoItA'中回調(diào)成員函數(shù)'Display'. 因此要使用包裝函數(shù)
   
//       'Wrapper_To_Call_Display'.

   #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 */
   };

   
// 靜態(tài)成員函數(shù)能回調(diào)成員函數(shù)Display()
   void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
   {
       
// 顯示地強(qiáng)轉(zhuǎn)為TClassA類的指針
       TClassA* mySelf = (TClassA*) pt2Object;

       
// 調(diào)用成員函數(shù)
       mySelf->Display(string);
   }


   
// 隱含地做回調(diào)
   
// 注意: 當(dāng)然這個(gè)函數(shù)也能作為成員函數(shù)
   void DoItA(void* pt2Object, void (*pt2Function)(void* pt2Object, char* text))
   {
      
/* do something */

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


   
// 執(zhí)行例程
   void Callback_Using_Argument()
   {
      
// 1. TClassA類對象的初始化
      TClassA objA;

      
// 2. 為對象<objA>調(diào)用函數(shù)'DoItA' 
      DoItA((void*&objA, TClassA::Wrapper_To_Call_Display);
   }

B: 把類實(shí)例的指針作為全局變量

函數(shù)DoItB利用TClassA類(包含有一個(gè)回調(diào))的對象作一些事情. 因此指向靜態(tài)包裝函數(shù)TClassB::Wrapper_To_Call_Display的指針要被傳遞到函數(shù)DoItA. 這個(gè)包裝函數(shù)是一個(gè)回調(diào)函數(shù). 你也能隨便寫一個(gè)類似于TClassA的類并在函數(shù)DoItA中使用當(dāng)然前提是那些類立提供了包裝函數(shù). 注意: 這個(gè)方案在已經(jīng)存在的回調(diào)接口不會被改變的前提下才會比較有用,但因?yàn)槭褂萌肿兞靠赡軙浅NkU(xiǎn)而且還可能到使嚴(yán)重錯(cuò)誤所以這并不是一個(gè)很好的方案.

 
  //-----------------------------------------------------------------------------------------
   
// 3.5 例B: 使用全局變量回調(diào)成員函數(shù)
   
// 任務(wù): 函數(shù) 'DoItB'中回調(diào)成員函數(shù)'Display'. 因此要使用包裝函數(shù)
   
//       'Wrapper_To_Call_Display'..

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

   
void* pt2Object;        // 一個(gè)可以指向任意類的全局變量

   
class TClassB
   {
   
public:

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

      
/* more of TClassB */
   };


   
// 靜態(tài)成員函數(shù)能回調(diào)成員函數(shù) Display()
   void TClassB::Wrapper_To_Call_Display(char* string)
   {
       
// 顯示地將全局變量 <pt2Object> 強(qiáng)轉(zhuǎn)成類TClassB的指針
       
// 警告: <pt2Object> 必須指向一個(gè)有效的類對象!
       TClassB* mySelf = (TClassB*) pt2Object;
 

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


   
// 隱含地做回調(diào)
   
// 注意: 當(dāng)然這個(gè)函數(shù)也能作為成員函數(shù)
   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.  TClassB類對象的初始化
      TClassB objB;


      
// 2. 對在靜態(tài)包裝函數(shù)中要使用的全局變量進(jìn)行賦值
      
// 重要: 一定不要忘記作這一步
      pt2Object = (void*&objB;


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

  導(dǎo)引

  1. 函數(shù)指針簡介

  2.  C C++ 函數(shù)指針語法

  3. C C++里怎么實(shí)現(xiàn)回調(diào)函數(shù) ?

   4.  封裝C C++函數(shù)指針的仿函數(shù)

  5. 相關(guān)鏈接

4. 封裝C C++函數(shù)指針的仿函數(shù)

   4.1  什么是仿函數(shù) ?

   4.2  怎么實(shí)現(xiàn)仿函數(shù) ?

   4.3  使用仿函數(shù)的例子

4.1  什么是仿函數(shù) ?

仿函數(shù)是一個(gè)具有狀態(tài)的函數(shù). C++ 你能看到它一個(gè)用一到個(gè)私有成員來儲這狀態(tài)類來現(xiàn),并利用重的操作符()來執(zhí)行本身的操作. 仿函數(shù)是利用模版(templates)和多態(tài)(polymorphism)的概念C C++ 的函數(shù). 你能構(gòu)建一個(gè)任意的成數(shù)的指列表并通相同的接口來調(diào)用它,但不干擾它或者一個(gè)實(shí)例的指的需求. 僅僅是要求所有的函數(shù)要保持相同的返回型和調(diào)參數(shù). 時(shí)候仿函數(shù)通常稱為閉(closures). 你也能用仿函數(shù)來實(shí)現(xiàn)調(diào).

4.2  怎么實(shí)現(xiàn)仿函數(shù) ?

首先你需要一個(gè)提供了一個(gè)叫Call的虛函數(shù)的基類TFunctor或者一個(gè)能調(diào)用成員函數(shù)的虛擬重載操作符 ().是選擇重載操作符還是函數(shù)Call當(dāng)然是隨便你自己的喜好了. 從這個(gè)基類你能派生一個(gè)模版 TSpecificFunctor ,在構(gòu)造函數(shù)里用一個(gè)對象的指針和一個(gè)函數(shù)指針來初始化.這個(gè)派生類重載基類中的函數(shù)Call/或操作符(): 在這個(gè)重載的版本里,你能利用存儲的函數(shù)指針和類對象的指針來調(diào)用成員函數(shù). 如果你忘了怎么去使用函數(shù)指針你可以參考章節(jié)函數(shù)指針簡介.

 
  //-----------------------------------------------------------------------------------------
   
// 4.2 怎么實(shí)現(xiàn)仿函數(shù)

   
// 抽象基類
   class TFunctor
   {
   
public:

      
// 兩個(gè)用來調(diào)用成員函數(shù)的虛函數(shù)
      
// 派生類將使用函數(shù)指針和類對象指針來實(shí)現(xiàn)函數(shù)調(diào)用
      virtual void operator()(const char* string)=0;  // call using operator
      virtual void Call(const char* string)=0;        // call using function
   };

   
// 派生模版類
   template <class TClass> class TSpecificFunctor : public TFunctor
   {
   
private:
      
void (TClass::*fpt)(const char*);   // pointer to member function
      TClass* pt2Object;                  // pointer to object

   
public:

      
// 構(gòu)造函數(shù)- 把類對象指針和函數(shù)指針
      
// 存儲在兩個(gè)私有變量中
      TSpecificFunctor(TClass* _pt2Object, void(TClass::*_fpt)(const char*))
         { pt2Object 
= _pt2Object;  fpt=_fpt; };

      
// 重載操作符 "()"
      virtual void operator()(const char* string)
       { (
*pt2Object.*fpt)(string);};              // execute member function

      
// 重載函數(shù) "Call"
      virtual void Call(const char* string)
        { (
*pt2Object.*fpt)(string);};             // execute member function
   };

4.3  仿函數(shù)的使用列

在下面的例子里我們有兩個(gè)類,包括返回值是(void)和參數(shù)為(const char*)的函數(shù)Display的一個(gè)簡單實(shí)現(xiàn).這里我們創(chuàng)建兩個(gè)TFunctor類的指針數(shù)組并且用封裝了類對象指針和TClassA TClassB的成員函數(shù)指針的TSpecificFunctor類來初始化這個(gè)數(shù)組中的元素. 而后我們使用仿函數(shù)數(shù)組來分別調(diào)用這兩個(gè)成員函數(shù). 類對象并不直接調(diào)用函數(shù)但你得保證你的操作不能干擾這兩個(gè)類的操作!

   //-----------------------------------------------------------------------------------------
   
// 4.3 仿函數(shù)的使用例子

   
// 類A
   class TClassA{
   
public:

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

      
/* more of TClassA */
   };

   
// 類 B
   class TClassB{
   
public:

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

      
/* more of TClassB */
   };

   
// 主程序
   int main(int /*argc*/char* /*argv[]*/)
   {
      
// 1. TClassA 和TClassB的實(shí)例
      TClassA objA;
      TClassB objB;

      
// 2. 實(shí)例化TSpecificFunctor 對象 
      
//    a ) 封裝指向TClassA類成員的函數(shù)指針的仿函數(shù)
      TSpecificFunctor<TClassA> specFuncA(&objA, &TClassA::Display);

      
//    b) 封裝指向TClassB類成員的函數(shù)指針的仿函數(shù)
      TSpecificFunctor<TClassB> specFuncB(&objB, &TClassB::Display);
 

      
// 3. 聲明基類TFunctor指針的數(shù)組, 并初始化
      TFunctor* vTable[] = { &specFuncA, &specFuncB };


      
// 4. 不需要類對象就可以調(diào)用成員函數(shù)
      vTable[0]->Call("TClassA::Display called!");        // via function "Call"
      (*vTable[1])   ("TClassB::Display called!");        // via operator "()"


      
// hit enter to terminate
      cout << endl << "Hit Enter to terminate!" << endl;
      cin.
get();

      
return 0;
   }


  導(dǎo)引

  1. 函數(shù)指針簡介

  2.  C C++ 函數(shù)指針語法

  3. C C++里怎么實(shí)現(xiàn)回調(diào)函數(shù) ?

   4.  封裝C C++函數(shù)指針的仿函數(shù)

  5. 相關(guān)鏈接

5.相關(guān)鏈接

   5.1  介紹函數(shù)指針

   5.2  回調(diào)和仿函數(shù)

   5.3  其他項(xiàng)

5.1  介紹函數(shù)指針

  • Using Member Function Pointers  一個(gè)borland社區(qū)的詳細(xì)文章. 原本是為了給產(chǎn)品BC3.1提供支持但實(shí)際上它是平臺獨(dú)立的并且一直在維護(hù)更新.  [文章]
  • Classes having Pointers to Members C++注解》的第15章作者Frank Brokken.  [教程]
  • C++ FAQ-Lite  新聞組news.comp.lang.c++FAQ. 該鏈接指向的是成員函數(shù)指針那部分.  [FAQ]
  • Pointing to Class Members  一個(gè)較短但很有用的文章,闡述了怎么使用成員函數(shù)指針.  [教程]
  • Declaring Function Pointers and Implementing Callbacks  關(guān)于C的函數(shù)指針的比較短小的文章. 里面也描述了函數(shù)標(biāo)識的調(diào)用規(guī)約.  [教程]
  • Notes on Function Pointers   為了闡明通用編程(generic programming)的思想的一篇簡要教程: 分割算法和數(shù)據(jù). 容器類的使用函數(shù)指針的例子, 例如. 調(diào)用用戶指定的函數(shù)為每一個(gè)容器元素. [教程]

5.2  回調(diào)和仿函數(shù)

5.2  其他項(xiàng)

  • PC Assembly Tutorial   關(guān)于匯編的一篇很好的教程. 這里你能了解到一個(gè)函數(shù)調(diào)用是怎么工作的. 另外還有調(diào)用規(guī)約以及匯編和C代碼的之間的交互. [教程]
  • The Virtual Function Mechanism  關(guān)于使用Microsoft's Visual C++匯編器的虛函數(shù)機(jī)制一篇簡要介紹 [文章]

 

Feedback

# re: 函數(shù)指針教程  回復(fù)  更多評論   

2010-07-24 17:31 by MaureenWiley20
If you want to buy real estate, you would have to get the <a href="http://bestfinance-blog.com/topics/home-loans">home loans</a>. Moreover, my father always utilizes a commercial loan, which supposes to be really firm.

# re: 函數(shù)指針教程  回復(fù)  更多評論   

2010-08-05 21:01 by course work
Looking for extra high quality and the best? You an opportunity to order coursework online at trustworthy custom writing firm.

# re: 函數(shù)指針教程  回復(fù)  更多評論   

2010-08-25 11:07 by ringtones
We do opine that the bollywood ringtones would be acquirable at get ringtones services and this is truth.

# re: 函數(shù)指針教程  回復(fù)  更多評論   

2010-09-16 14:17 by wars essay
Thare’s no other good way to have excellent mark than to make the research papers about this good topic and that is, also, good to order the United States essays paper in the essay papers writing service.
我的個(gè)人簡歷第一頁 我的個(gè)人簡歷第二頁
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美亚洲一级片| 老巨人导航500精品| 欧美一区二区三区视频免费| 亚洲精品日韩欧美| 日韩一二在线观看| 99精品黄色片免费大全| 日韩一级免费| 亚洲一区二区三区高清 | 国产精品系列在线播放| 国产精品久久国产三级国电话系列| 国产精品久久久91| 国模私拍一区二区三区| 伊人婷婷欧美激情| 亚洲精品久久久蜜桃| 亚洲一区免费| 裸体一区二区| 亚洲美女精品久久| 欧美在线精品一区| 欧美精品麻豆| 韩国成人福利片在线播放| 亚洲人成毛片在线播放| 亚洲综合色激情五月| 老司机精品导航| 一区二区精品国产| 理论片一区二区在线| 国产精品福利久久久| 精品1区2区| 午夜精品一区二区三区电影天堂| 蜜乳av另类精品一区二区| 这里只有视频精品| 蜜桃伊人久久| 国产一区二区三区四区在线观看 | 夜夜嗨av色一区二区不卡| 亚洲免费在线视频一区 二区| 免费观看在线综合| 欧美一区二区在线| 亚洲第一精品福利| 亚洲午夜激情网页| 欧美国产在线观看| 影音先锋另类| 欧美在线一二三区| 日韩天堂av| 免费永久网站黄欧美| 国产精品一区二区久久久| 日韩视频国产视频| 欧美黄色小视频| 久久成人综合网| 国产精品一区二区三区久久久| 日韩午夜激情av| 亚洲第一精品夜夜躁人人躁| 久久国产主播精品| 国产一级久久| 久久精品2019中文字幕| 亚洲自拍偷拍麻豆| 国产精品成人免费精品自在线观看| 亚洲日本无吗高清不卡| 欧美成人国产va精品日本一级| 午夜精品免费在线| 国产欧美69| 久久动漫亚洲| 欧美在线免费视屏| 国产在线成人| 久久精品视频在线看| 亚洲欧美成人精品| 国产日韩欧美| 久久蜜臀精品av| 性色av一区二区三区红粉影视| 国产精品综合av一区二区国产馆| 午夜国产精品影院在线观看| 亚洲一区精品视频| 国产亚洲精品久久飘花| 久久人人爽人人爽爽久久| 久久精品国产第一区二区三区最新章节 | 日韩一级片网址| 欧美日韩精品不卡| 亚洲网在线观看| 亚洲免费视频在线观看| 国产精品美女久久| 久久精品一二三区| 久久亚洲私人国产精品va| 亚洲毛片一区| 亚洲图片在线| 伊人色综合久久天天| 亚洲国产欧美日韩精品| 欧美视频一区| 久久国产欧美| 男女激情久久| 午夜宅男欧美| 久久综合给合| 亚洲永久精品国产| 久久免费视频在线| 亚洲私人影吧| 久久精品亚洲乱码伦伦中文 | 一区二区三区日韩欧美| 亚洲欧美国产日韩中文字幕| 在线看视频不卡| 一个人看的www久久| 国产亚洲精品v| 亚洲精品日产精品乱码不卡| 国产精品夫妻自拍| 亚洲电影在线播放| 国产精品视频免费一区| 亚洲第一精品影视| 国产亚洲精品bt天堂精选| 亚洲欧洲美洲综合色网| 国产一区 二区 三区一级| 亚洲精品一区二区在线| 国产一区二区三区奇米久涩| 亚洲麻豆一区| 亚洲激情第一区| 久久se精品一区精品二区| 99re热这里只有精品视频 | 欧美日韩少妇| 老司机亚洲精品| 国产精品扒开腿爽爽爽视频| 美女主播视频一区| 国产精品一区二区三区四区| 亚洲片在线观看| 亚洲黄色有码视频| 久久精品亚洲一区| 欧美一区二区私人影院日本 | 亚洲理伦在线| 久久综合色88| 久久综合999| 国产伦精品免费视频| 99在线观看免费视频精品观看| 亚洲欧洲日韩综合二区| 欧美影院精品一区| 欧美一区二区三区日韩| 国产精品videosex极品| 日韩一二在线观看| 一区二区av在线| 欧美激情亚洲视频| 亚洲黄色免费电影| 亚洲国产精品久久久久秋霞蜜臀 | 欧美一区精品| 久久婷婷蜜乳一本欲蜜臀| 国产亚洲精品aa| 亚洲欧美在线网| 久久国产精品99久久久久久老狼| 国产精品久久久久久久久久尿 | 久久aⅴ乱码一区二区三区| 欧美一区成人| 国产一级久久| 久久久久久噜噜噜久久久精品| 久久精品99无色码中文字幕| 国产欧美精品在线观看| 久久爱另类一区二区小说| 伊人久久大香线蕉av超碰演员| 亚洲欧美在线磁力| 久久亚洲精品一区| 亚洲第一二三四五区| 免费欧美网站| 99视频一区二区| 欧美亚洲日本网站| 黑人操亚洲美女惩罚| 麻豆精品视频在线观看| 亚洲国产精品福利| 亚洲一区二区三区久久 | 暖暖成人免费视频| 亚洲精品免费一区二区三区| 亚洲无毛电影| 国产综合自拍| 欧美成人蜜桃| 一区二区三区黄色| 久久中文在线| 一区二区三区四区五区视频| 国产精品无码专区在线观看| 久久久www免费人成黑人精品| 欧美不卡视频一区发布| 亚洲一区二区三区成人在线视频精品 | 在线一区二区视频| 国产精品手机视频| 裸体歌舞表演一区二区| 一本久道久久综合婷婷鲸鱼| 久久久蜜桃精品| 亚洲最新中文字幕| 国产酒店精品激情| 欧美激情女人20p| 欧美亚洲视频一区二区| 亚洲福利一区| 久热爱精品视频线路一| 国产精品99久久久久久久vr| 国内外成人在线视频| 欧美日韩在线播放三区四区| 久久精品国产一区二区三区| 亚洲精品美女91| 久久综合九色综合久99| 亚洲欧美激情四射在线日| 亚洲高清不卡一区| 国产精品日韩在线播放| 欧美激情五月| 美女诱惑黄网站一区| 欧美中文字幕在线| 中文一区二区| 亚洲精品乱码久久久久久日本蜜臀| 欧美在线啊v| 国产精品99久久久久久人| 亚洲人成网在线播放| 加勒比av一区二区|