ฟังก์ชันเรียกกลับเป็นฟังก์ชัน ซึ่งเป็นอาร์กิวเมนต์ ไม่ใช่พารามิเตอร์ ในฟังก์ชันอื่น ฟังก์ชันอื่นเรียกว่าฟังก์ชันหลัก ดังนั้นสองฟังก์ชันที่เกี่ยวข้อง: ฟังก์ชันหลักและฟังก์ชันเรียกกลับเอง ในรายการพารามิเตอร์ของฟังก์ชันหลัก มีการประกาศฟังก์ชันเรียกกลับโดยไม่มีคำจำกัดความ เช่นเดียวกับการประกาศอ็อบเจ็กต์ที่ไม่มีการมอบหมาย ฟังก์ชันหลักถูกเรียกด้วยอาร์กิวเมนต์ (ใน 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() ให้ใช้ (อ่านและแสดง) ที่นั่นแล้วจึงใช้