ฟังก์ชันโทรกลับใน C++

Callback Function C



ฟังก์ชันเรียกกลับเป็นฟังก์ชัน ซึ่งเป็นอาร์กิวเมนต์ ไม่ใช่พารามิเตอร์ ในฟังก์ชันอื่น ฟังก์ชันอื่นเรียกว่าฟังก์ชันหลัก ดังนั้นสองฟังก์ชันที่เกี่ยวข้อง: ฟังก์ชันหลักและฟังก์ชันเรียกกลับเอง ในรายการพารามิเตอร์ของฟังก์ชันหลัก มีการประกาศฟังก์ชันเรียกกลับโดยไม่มีคำจำกัดความ เช่นเดียวกับการประกาศอ็อบเจ็กต์ที่ไม่มีการมอบหมาย ฟังก์ชันหลักถูกเรียกด้วยอาร์กิวเมนต์ (ใน main()) หนึ่งในอาร์กิวเมนต์ในการเรียกฟังก์ชันหลักคือนิยามที่มีประสิทธิภาพของฟังก์ชันเรียกกลับ ใน C ++ อาร์กิวเมนต์นี้เป็นการอ้างอิงถึงคำจำกัดความของฟังก์ชันเรียกกลับ ไม่ใช่คำจำกัดความที่แท้จริง จริง ๆ แล้วฟังก์ชันเรียกกลับนั้นถูกเรียกภายในคำจำกัดความของฟังก์ชันหลัก

ฟังก์ชันเรียกกลับพื้นฐานใน C++ ไม่รับประกันการทำงานแบบอะซิงโครนัสในโปรแกรม พฤติกรรมแบบอะซิงโครนัสเป็นประโยชน์ที่แท้จริงของรูปแบบฟังก์ชันการโทรกลับ ในรูปแบบฟังก์ชันการเรียกกลับแบบอะซิงโครนัส ควรได้รับผลลัพธ์ของฟังก์ชันหลักสำหรับโปรแกรมก่อนที่จะได้รับผลลัพธ์ของฟังก์ชันเรียกกลับ สามารถทำได้ใน C++; อย่างไรก็ตาม C ++ มีไลบรารีที่เรียกว่าอนาคตเพื่อรับประกันการทำงานของรูปแบบฟังก์ชันการโทรกลับแบบอะซิงโครนัส







บทความนี้จะอธิบายเกี่ยวกับโครงร่างฟังก์ชันการโทรกลับพื้นฐาน ส่วนมากจะใช้ C++ ล้วนๆ เท่าที่เกี่ยวข้องกับการเรียกกลับ พฤติกรรมพื้นฐานของไลบรารีในอนาคตจะได้รับการอธิบายด้วย ความรู้พื้นฐานเกี่ยวกับ C ++ และตัวชี้เป็นสิ่งจำเป็นสำหรับการทำความเข้าใจบทความนี้



เนื้อหาบทความ

แผนฟังก์ชันการโทรกลับพื้นฐาน

โครงร่างฟังก์ชันเรียกกลับต้องการฟังก์ชันหลัก และฟังก์ชันเรียกกลับเอง การประกาศฟังก์ชันเรียกกลับเป็นส่วนหนึ่งของรายการพารามิเตอร์ของฟังก์ชันหลัก คำจำกัดความของฟังก์ชันเรียกกลับถูกระบุในการเรียกฟังก์ชันของฟังก์ชันหลัก เรียกฟังก์ชันเรียกกลับจริงภายในคำจำกัดความของฟังก์ชันหลัก โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:



#รวม

โดยใช้ เนมสเปซชั่วโมง;



intหลักFn(charch[],int (*ptr)(int))

{

intid1= 1;

intid2= 2;

intโดยปกติ= (*ptr)(id2);

ค่าใช้จ่าย<<'หน้าที่หลัก:'<<id1<<''<<ch<<''<<โดยปกติ<<'NS';

กลับid1;

}


intcb(intไอดี)

{

ค่าใช้จ่าย<<'ฟังก์ชันเรียกกลับ'<<'NS';

กลับไอดี;

}


intหลัก()

{

int (*ptr)(int) = &cb;

charไม่[] = 'และ';

หลักFn(พ่อ cb);



กลับ 0;

}

ผลลัพธ์คือ:





ฟังก์ชั่นโทรกลับ

หน้าที่หลัก: 1 และ 2

ฟังก์ชันหลักถูกระบุโดย principalFn() ฟังก์ชั่นการโทรกลับถูกระบุโดย cb() ฟังก์ชันเรียกกลับถูกกำหนดไว้ภายนอกฟังก์ชันหลัก แต่เรียกจริงภายในฟังก์ชันหลัก

สังเกตการประกาศฟังก์ชันเรียกกลับเป็นพารามิเตอร์ในรายการพารามิเตอร์ของการประกาศฟังก์ชันหลัก การประกาศฟังก์ชันเรียกกลับเป็น int (*ptr)(int) โปรดสังเกตนิพจน์ของฟังก์ชันเรียกกลับ เช่น การเรียกฟังก์ชัน ในคำจำกัดความของฟังก์ชันหลัก อาร์กิวเมนต์สำหรับการเรียกใช้ฟังก์ชันเรียกกลับถูกส่งผ่านที่นั่น คำสั่งสำหรับการเรียกใช้ฟังก์ชันนี้คือ:



intโดยปกติ= (*ptr)(id2);

โดยที่ id2 เป็นอาร์กิวเมนต์ ptr เป็นส่วนหนึ่งของพารามิเตอร์ ซึ่งเป็นตัวชี้ ซึ่งจะเชื่อมโยงกับการอ้างอิงของฟังก์ชันเรียกกลับในฟังก์ชัน main()

สังเกตนิพจน์:

int (*ptr)(int) = &cb;

ในฟังก์ชัน main() ซึ่งเชื่อมโยงการประกาศ (โดยไม่มีคำจำกัดความ) ของฟังก์ชันการเรียกกลับกับชื่อของคำจำกัดความของฟังก์ชันการเรียกกลับเดียวกัน

ฟังก์ชันหลักถูกเรียกในฟังก์ชัน main() เป็น:

หลักFn(พ่อ cb);

โดยที่ cha เป็นสตริงและ cb เป็นชื่อของฟังก์ชันการโทรกลับโดยไม่มีอาร์กิวเมนต์

พฤติกรรมแบบซิงโครนัสของฟังก์ชันเรียกกลับ

พิจารณาโปรแกรมต่อไปนี้:

#รวม

โดยใช้ เนมสเปซชั่วโมง;



โมฆะหลักFn(โมฆะ (*ptr)())

{

ค่าใช้จ่าย<<'หน้าที่หลัก'<<'NS';

(*ptr)();

}


โมฆะcb()

{

ค่าใช้จ่าย<<'ฟังก์ชันเรียกกลับ'<<'NS';

}


โมฆะfn()

{

ค่าใช้จ่าย<<'เห็น'<<'NS';

}


intหลัก()

{

โมฆะ (*ptr)() = &cb;

หลักFn(cb);

fn();



กลับ 0;

}

ผลลัพธ์คือ:

หน้าที่หลัก

ฟังก์ชั่นโทรกลับ

เห็น

มีฟังก์ชั่นใหม่ที่นี่ ฟังก์ชั่นใหม่ทั้งหมดทำคือแสดงผลลัพธ์ที่เห็น ในฟังก์ชัน main() ฟังก์ชันหลักจะถูกเรียก จากนั้นฟังก์ชันใหม่จะเรียก fn() ผลลัพธ์แสดงว่าโค้ดสำหรับฟังก์ชันหลักถูกรัน จากนั้นสำหรับฟังก์ชันเรียกกลับถูกรัน และสุดท้ายสำหรับฟังก์ชัน fn() ถูกรัน นี่เป็นพฤติกรรมแบบซิงโครนัส (เธรดเดียว)

หากเป็นพฤติกรรมแบบอะซิงโครนัส เมื่อมีการเรียกส่วนรหัสสามส่วนตามลำดับ ส่วนรหัสแรกอาจถูกดำเนินการ ตามด้วยการดำเนินการของส่วนรหัสที่สาม ก่อนดำเนินการส่วนรหัสที่สอง

ฟังก์ชัน fn() สามารถเรียกได้จากในนิยามของฟังก์ชันหลัก แทนที่จะเรียกจากภายในฟังก์ชัน main() ดังนี้

#รวม

โดยใช้ เนมสเปซชั่วโมง;



โมฆะfn()

{

ค่าใช้จ่าย<<'เห็น'<<'NS';

}


โมฆะหลักFn(โมฆะ (*ptr)())

{

ค่าใช้จ่าย<<'หน้าที่หลัก'<<'NS';

fn();

(*ptr)();

}


โมฆะcb()

{

ค่าใช้จ่าย<<'ฟังก์ชันเรียกกลับ'<<'NS';

}


intหลัก()

{

โมฆะ (*ptr)() = &cb;

หลักFn(cb);



กลับ 0;

}

ผลลัพธ์คือ:

หน้าที่หลัก

เห็น

ฟังก์ชั่นโทรกลับ

นี่เป็นการเลียนแบบพฤติกรรมแบบอะซิงโครนัส ไม่ใช่พฤติกรรมแบบอะซิงโครนัส มันยังคงเป็นพฤติกรรมแบบซิงโครนัส

นอกจากนี้ ลำดับการดำเนินการของส่วนรหัสของฟังก์ชันหลักและส่วนรหัสของฟังก์ชันเรียกกลับสามารถสลับกันได้ในคำจำกัดความของฟังก์ชันหลัก โปรแกรมต่อไปนี้แสดงให้เห็นสิ่งนี้:

#รวม

โดยใช้ เนมสเปซชั่วโมง;



โมฆะหลักFn(โมฆะ (*ptr)())

{

(*ptr)();

ค่าใช้จ่าย<<'หน้าที่หลัก'<<'NS';

}


โมฆะcb()

{

ค่าใช้จ่าย<<'ฟังก์ชันเรียกกลับ'<<'NS';

}


โมฆะfn()

{

ค่าใช้จ่าย<<'เห็น'<<'NS';

}


intหลัก()

{

โมฆะ (*ptr)() = &cb;

หลักFn(cb);

fn();



กลับ 0;

}

ผลผลิตตอนนี้คือ

ฟังก์ชั่นโทรกลับ

หน้าที่หลัก

เห็น

นี่เป็นการเลียนแบบพฤติกรรมแบบอะซิงโครนัส ไม่ใช่พฤติกรรมแบบอะซิงโครนัส มันยังคงเป็นพฤติกรรมแบบซิงโครนัส พฤติกรรมแบบอะซิงโครนัสที่แท้จริงสามารถรับได้ดังที่อธิบายไว้ในหัวข้อถัดไปหรือกับไลบรารีในอนาคต

ลักษณะการทำงานแบบอะซิงโครนัสพร้อมฟังก์ชันเรียกกลับ

รหัสเทียมสำหรับรูปแบบฟังก์ชันการโทรกลับแบบอะซิงโครนัสพื้นฐานคือ:

พิมพ์เอาต์พุต;

พิมพ์ cb(พิมพ์เอาต์พุต)

{

//คำสั่ง

}


พิมพ์หลักFn(พิมพ์อินพุต พิมพ์ cb(พิมพ์เอาต์พุต))

{

//คำสั่ง

}

สังเกตตำแหน่งของข้อมูลอินพุตและเอาต์พุตในตำแหน่งต่างๆ ของรหัสหลอก อินพุตของฟังก์ชันโทรกลับคือเอาต์พุต พารามิเตอร์ของฟังก์ชันหลักคือพารามิเตอร์อินพุตสำหรับโค้ดทั่วไปและพารามิเตอร์สำหรับฟังก์ชันเรียกกลับ ด้วยรูปแบบนี้ ฟังก์ชันที่สามสามารถดำเนินการ (เรียกว่า) ในฟังก์ชัน main() ก่อนที่จะอ่านเอาต์พุตของฟังก์ชันเรียกกลับ (ยังคงอยู่ในฟังก์ชัน main()) รหัสต่อไปนี้แสดงให้เห็นสิ่งนี้:

#รวม

โดยใช้ เนมสเปซชั่วโมง;

char *ผลผลิต;


โมฆะcb(charออก[])

{

ผลผลิต=ออก;

}



โมฆะหลักFn(charป้อนข้อมูล[],โมฆะ (*ptr)(char[ห้าสิบ]))

{

(*ptr)(ป้อนข้อมูล);

ค่าใช้จ่าย<<'หน้าที่หลัก'<<'NS';

}


โมฆะfn()

{

ค่าใช้จ่าย<<'เห็น'<<'NS';

}


intหลัก()

{

charป้อนข้อมูล[] = 'ฟังก์ชันเรียกกลับ';

โมฆะ (*ptr)(char[]) = &cb;

หลักFn(อินพุต cb);

fn();

ค่าใช้จ่าย<<ผลผลิต<<'NS';



กลับ 0;

}

ผลลัพธ์ของโปรแกรมคือ:

หน้าที่หลัก

เห็น

ฟังก์ชั่นโทรกลับ

ในโค้ดเฉพาะนี้ Datum เอาต์พุตและอินพุตจะเป็น Datum เดียวกัน ผลลัพธ์ของการเรียกใช้ฟังก์ชันที่สามในฟังก์ชัน main() ถูกแสดงก่อนผลลัพธ์ของฟังก์ชันเรียกกลับ ฟังก์ชันเรียกกลับดำเนินการ เสร็จสิ้น และกำหนดผลลัพธ์ (ค่า) ให้กับตัวแปร เอาต์พุต ทำให้โปรแกรมสามารถดำเนินการต่อได้โดยไม่มีการรบกวน ในฟังก์ชัน main() เอาต์พุตของฟังก์ชันเรียกกลับถูกใช้ (อ่านและแสดง) เมื่อจำเป็น ซึ่งนำไปสู่พฤติกรรมแบบอะซิงโครนัสสำหรับโครงร่างทั้งหมด

นี่เป็นวิธีแบบเธรดเดียวในการรับการทำงานแบบอะซิงโครนัสของฟังก์ชันเรียกกลับแบบอะซิงโครนัสด้วย C++ บริสุทธิ์

การใช้งานพื้นฐานของห้องสมุดในอนาคต

แนวคิดของรูปแบบฟังก์ชันการเรียกกลับแบบอะซิงโครนัสคือฟังก์ชันหลักจะส่งกลับก่อนที่ฟังก์ชันเรียกกลับจะส่งคืน สิ่งนี้ทำโดยทางอ้อมอย่างมีประสิทธิภาพในโค้ดด้านบน

สังเกตจากโค้ดด้านบนว่าฟังก์ชันเรียกกลับได้รับอินพุตหลักสำหรับโค้ด และสร้างเอาต์พุตหลักสำหรับโค้ด ไลบรารี C++ ในอนาคตมีฟังก์ชันที่เรียกว่า sync() อาร์กิวเมนต์แรกของฟังก์ชันนี้คือการอ้างอิงฟังก์ชันเรียกกลับ อาร์กิวเมนต์ที่สองคืออินพุตของฟังก์ชันเรียกกลับ ฟังก์ชัน sync() ส่งคืนโดยไม่ต้องรอให้ฟังก์ชันการเรียกกลับทำงานจนเสร็จ แต่อนุญาตให้ฟังก์ชันเรียกกลับทำงานจนเสร็จ สิ่งนี้แสดงพฤติกรรมแบบอะซิงโครนัส ในขณะที่ฟังก์ชันเรียกกลับยังคงดำเนินการอยู่ เนื่องจากฟังก์ชัน sync() ได้ส่งคืนไปแล้ว คำสั่งด้านล่างจะทำงานต่อไป นี่เป็นเหมือนพฤติกรรมอะซิงโครนัสในอุดมคติ

โปรแกรมด้านบนถูกเขียนใหม่ด้านล่าง โดยคำนึงถึงไลบรารีในอนาคตและฟังก์ชัน sync()

#รวม

#รวม

#รวม

โดยใช้ เนมสเปซชั่วโมง;

อนาคต<สตริง>ผลผลิต;

สตริง cb(สตริงสตริ)

{

กลับสตริ;

}



โมฆะหลักFn(อินพุตสตริง)

{

ผลผลิต=async(cb, อินพุต);

ค่าใช้จ่าย<<'หน้าที่หลัก'<<'NS';

}


โมฆะfn()

{

ค่าใช้จ่าย<<'เห็น'<<'NS';

}


intหลัก()

{

อินพุตสตริง=สตริง('ฟังก์ชันเรียกกลับ');

หลักFn(ป้อนข้อมูล);

fn();

สตริง ret=เอาท์พุทรับ(); //รอการติดต่อกลับหากจำเป็น

ค่าใช้จ่าย<<ขวา<<'NS';



กลับ 0;

}

ในที่สุด ฟังก์ชัน sync() จะเก็บเอาต์พุตของฟังก์ชันเรียกกลับไว้ในอ็อบเจ็กต์ในอนาคต ผลลัพธ์ที่คาดหวังสามารถรับได้ในฟังก์ชัน main() โดยใช้ฟังก์ชันสมาชิก get() ของอ็อบเจ็กต์ในอนาคต

บทสรุป

ฟังก์ชันเรียกกลับเป็นฟังก์ชัน ซึ่งเป็นอาร์กิวเมนต์ ไม่ใช่พารามิเตอร์ ในฟังก์ชันอื่น โครงร่างฟังก์ชันเรียกกลับต้องการฟังก์ชันหลัก และฟังก์ชันเรียกกลับเอง การประกาศฟังก์ชันเรียกกลับเป็นส่วนหนึ่งของรายการพารามิเตอร์ของฟังก์ชันหลัก คำจำกัดความของฟังก์ชันเรียกกลับถูกระบุในการเรียกฟังก์ชันของฟังก์ชันหลัก (ใน main()) เรียกฟังก์ชันเรียกกลับจริงภายในคำจำกัดความของฟังก์ชันหลัก

รูปแบบฟังก์ชันการโทรกลับไม่จำเป็นต้องเป็นแบบอะซิงโครนัส เพื่อให้แน่ใจว่ารูปแบบฟังก์ชันการโทรกลับเป็นแบบอะซิงโครนัส ให้ป้อนข้อมูลหลักไปยังรหัส ป้อนข้อมูลไปยังฟังก์ชันเรียกกลับ ทำให้ผลลัพธ์หลักของรหัส, ผลลัพธ์ของฟังก์ชันการโทรกลับ; เก็บผลลัพธ์ของฟังก์ชันเรียกกลับในตัวแปรหรือโครงสร้างข้อมูล ในฟังก์ชัน main() หลังจากเรียกใช้ฟังก์ชันหลักแล้ว ให้ดำเนินการคำสั่งอื่นๆ ของแอปพลิเคชัน เมื่อต้องการเอาท์พุตของฟังก์ชันเรียกกลับ ในฟังก์ชัน main() ให้ใช้ (อ่านและแสดง) ที่นั่นแล้วจึงใช้