ฟังก์ชันอินไลน์แตกต่างจากมาโครตัวประมวลผลล่วงหน้าอย่างไร
ฟังก์ชันอินไลน์แตกต่างจากมาโครตัวประมวลผลล่วงหน้าอย่างไร
คำตอบ:
มาโครตัวประมวลผลล่วงหน้าเป็นเพียงรูปแบบการทดแทนที่ใช้กับโค้ดของคุณ สามารถใช้ได้เกือบทุกที่ในโค้ดของคุณเนื่องจากถูกแทนที่ด้วยส่วนขยายก่อนที่การคอมไพล์จะเริ่มต้น
ฟังก์ชันอินไลน์คือฟังก์ชันจริงที่ร่างกายถูกฉีดเข้าไปในไซต์การโทรโดยตรง สามารถใช้ได้เฉพาะในกรณีที่การเรียกใช้ฟังก์ชันเหมาะสมเท่านั้น
ตอนนี้สำหรับการใช้มาโครเทียบกับฟังก์ชันอินไลน์ในบริบทที่เหมือนฟังก์ชันโปรดทราบว่า:
ขั้นแรกมาโครตัวประมวลผลก่อนเป็นเพียง "คัดลอกวาง" ในโค้ดก่อนการคอมไพล์ ดังนั้นจึงไม่มีการตรวจสอบประเภทและผลข้างเคียงบางอย่างอาจปรากฏขึ้น
ตัวอย่างเช่นหากคุณต้องการเปรียบเทียบ 2 ค่า:
#define max(a,b) ((a<b)?b:a)
ผลข้างเคียงจะปรากฏขึ้นหากคุณใช้max(a++,b++)
เช่น ( a
หรือb
จะเพิ่มขึ้นสองครั้ง) ให้ใช้ (ตัวอย่าง) แทน
inline int max( int a, int b) { return ((a<b)?b:a); }
max(fibonacci(100), factorial(10000))
ที่ใหญ่กว่าจะได้รับการคำนวณสองครั้ง :(
ฟังก์ชันอินไลน์ถูกขยายโดยคอมไพลเลอร์โดยที่มาโครถูกขยายโดยตัวประมวลผลล่วงหน้าซึ่งเป็นเพียงการทดแทนข้อความดังนั้น
ไม่มีการตรวจสอบประเภทระหว่างการเรียกใช้มาโครในขณะที่การตรวจสอบประเภทเสร็จสิ้นระหว่างการเรียกใช้ฟังก์ชัน
ผลลัพธ์ที่ไม่ต้องการและความไม่มีประสิทธิภาพอาจเกิดขึ้นระหว่างการขยายแมโครเนื่องจากการประเมินค่าอาร์กิวเมนต์และลำดับการดำเนินการใหม่ ตัวอย่างเช่น
#define MAX(a,b) ((a)>(b) ? (a) : (b))
int i = 5, j = MAX(i++, 0);
จะส่งผลให้
int i = 5, j = ((i++)>(0) ? (i++) : (0));
อาร์กิวเมนต์แมโครไม่ได้รับการประเมินก่อนการขยายแมโคร
#define MUL(a, b) a*b
int main()
{
// The macro is expended as 2 + 3 * 3 + 5, not as 5*8
printf("%d", MUL(2+3, 3+5));
return 0;
}
// Output: 16`
ไม่สามารถใช้คีย์เวิร์ด return ในมาโครเพื่อส่งคืนค่าเช่นเดียวกับในกรณีของฟังก์ชัน
ฟังก์ชันอินไลน์สามารถทำงานได้มากเกินไป
โทเค็นที่ส่งผ่านไปยังมาโครสามารถเชื่อมต่อกันได้โดยใช้ตัวดำเนินการ ## ที่เรียกว่าตัวดำเนินการวางโทเค็น
โดยทั่วไปแล้วมาโครจะใช้สำหรับการใช้โค้ดซ้ำโดยที่ฟังก์ชันอินไลน์ถูกใช้เพื่อกำจัดเวลาเหนือศีรษะ (เวลาส่วนเกิน) ระหว่างการเรียกใช้ฟังก์ชัน (หลีกเลี่ยงการข้ามไปยังรูทีนย่อย)
ความแตกต่างที่สำคัญคือการตรวจสอบประเภท คอมไพเลอร์จะตรวจสอบว่าสิ่งที่คุณส่งผ่านเป็นค่าอินพุตเป็นประเภทที่สามารถส่งผ่านไปยังฟังก์ชันได้หรือไม่ ไม่เป็นความจริงกับมาโครตัวประมวลผลก่อน - พวกมันถูกขยายก่อนการตรวจสอบประเภทใด ๆ และอาจทำให้เกิดข้อบกพร่องที่รุนแรงและยากที่จะตรวจพบ
ต่อไปนี้เป็นประเด็นที่ไม่ชัดเจนอื่น ๆ อีกมากมายที่ระบุไว้
หากต้องการเพิ่มความแตกต่างให้กับสิ่งที่ให้ไปแล้ว: คุณไม่สามารถก้าวผ่านตัว#define
แก้ไขข้อบกพร่อง แต่คุณสามารถก้าวผ่านฟังก์ชันอินไลน์ได้
มาโครกำลังละเว้นเนมสเปซ และนั่นทำให้พวกเขาชั่วร้าย
ฟังก์ชันแบบอินไลน์จะคล้ายกับมาโคร (เนื่องจากโค้ดฟังก์ชันถูกขยายที่จุดของการโทรในเวลาคอมไพล์) ฟังก์ชันอินไลน์จะถูกแยกวิเคราะห์โดยคอมไพเลอร์ในขณะที่มาโครจะขยายโดยตัวประมวลผลล่วงหน้า เป็นผลให้มีความแตกต่างที่สำคัญหลายประการ:
ในบางกรณีนิพจน์ที่ส่งเป็นอาร์กิวเมนต์ไปยังมาโครสามารถประเมินได้มากกว่าหนึ่งครั้ง http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx
มาโครจะถูกขยายในเวลาก่อนการคอมไพล์คุณไม่สามารถใช้สำหรับการดีบัก แต่คุณสามารถใช้ฟังก์ชันแบบอินไลน์ได้
- บทความดีๆ : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1
;
ฟังก์ชันอินไลน์จะรักษาความหมายของค่าไว้ในขณะที่มาโครตัวประมวลผลล่วงหน้าจะคัดลอกไวยากรณ์เท่านั้น คุณจะได้รับบั๊กที่ละเอียดมากด้วยมาโครตัวประมวลผลล่วงหน้าหากคุณใช้อาร์กิวเมนต์หลายครั้งตัวอย่างเช่นหากอาร์กิวเมนต์มีการกลายพันธุ์เช่น "i ++" ที่มีการดำเนินการสองครั้งนั้นค่อนข้างน่าแปลกใจ ฟังก์ชันอินไลน์จะไม่มีปัญหานี้
ฟังก์ชั่นแบบอินไลน์จะทำงานในรูปแบบวากยสัมพันธ์เช่นเดียวกับฟังก์ชันปกติโดยให้ความปลอดภัยของชนิดและขอบเขตของฟังก์ชันตัวแปรโลคัลและการเข้าถึงสมาชิกคลาสหากเป็นวิธีการ นอกจากนี้เมื่อเรียกใช้วิธีการแบบอินไลน์คุณต้องปฏิบัติตามข้อ จำกัด ส่วนตัว / ที่มีการป้องกัน
หากต้องการทราบความแตกต่างระหว่างมาโครและฟังก์ชันอินไลน์อันดับแรกเราควรทราบว่ามันคืออะไรและควรใช้เมื่อใด
ฟังก์ชั่น :
int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
การเรียกใช้ฟังก์ชันมีค่าใช้จ่ายที่เกี่ยวข้องเนื่องจากหลังจากฟังก์ชันเสร็จสิ้นการเรียกใช้งานจะต้องทราบว่าต้องส่งคืนที่ใดและต้องเก็บค่าไว้ในหน่วยความจำสแต็ก
สำหรับแอปพลิเคชั่นขนาดเล็กจะไม่มีปัญหา แต่เรามาดูตัวอย่างของแอปพลิเคชันทางการเงินที่มีธุรกรรมนับพันรายการเกิดขึ้นทุกวินาทีเราไม่สามารถใช้การเรียกฟังก์ชันได้
แมโคร:
# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
ผลลัพธ์ int = กำลังสอง (x * x)
แต่มาโครมีจุดบกพร่องที่เกี่ยวข้อง
#define Square(x) x*x
int main() {
int val = 5;
int result = Square(val + 1);
cout << result << endl;
return 0;
}
นี่ออกเป็น11 ไม่ได้ 36
ฟังก์ชั่นออนไลน์ :
inline int Square(int x) {
return x * x;
}
int main() {
using namespace std;
int val = 5;
int result = Square(val + 1);
cout << result << endl;
return 0;
}
เอาต์พุต36
คีย์เวิร์ดแบบอินไลน์ขอให้คอมไพลเลอร์แทนที่การเรียกใช้ฟังก์ชันด้วยเนื้อหาของฟังก์ชันที่นี่ผลลัพธ์จะถูกต้องเนื่องจากจะประเมินนิพจน์ก่อนแล้วจึงส่งผ่านไปซึ่งจะลดค่าใช้จ่ายในการเรียกฟังก์ชันเนื่องจากไม่จำเป็นต้องเก็บที่อยู่ส่งคืนและสแต็ก ไม่จำเป็นต้องใช้หน่วยความจำสำหรับอาร์กิวเมนต์ของฟังก์ชัน
การเปรียบเทียบระหว่างมาโครและฟังก์ชันอินไลน์:
สรุป:
บางครั้งฟังก์ชันแบบอินไลน์มีประโยชน์มากกว่ามาโครเนื่องจากช่วยเพิ่มประสิทธิภาพและปลอดภัยในการใช้งานและลดค่าใช้จ่ายในการเรียกฟังก์ชันด้วย เป็นเพียงการร้องขอไปยังคอมไพเลอร์ฟังก์ชันบางอย่างจะไม่อยู่ในบรรทัดเช่น:
ซึ่งเป็นสิ่งที่ดีเพราะนั่นคือเมื่อใดก็ตามที่คอมไพเลอร์คิดว่าดีที่สุดที่จะทำสิ่งอื่นด้วยวิธีอื่น
ใน GCC (ฉันไม่แน่ใจเกี่ยวกับคนอื่น ๆ ) การประกาศฟังก์ชันแบบอินไลน์เป็นเพียงคำใบ้ให้กับคอมไพเลอร์ ยังคงขึ้นอยู่กับคอมไพเลอร์ในตอนท้ายของวันที่จะตัดสินใจว่าจะรวมเนื้อหาของฟังก์ชันเมื่อใดก็ตามที่มีการเรียกใช้หรือไม่
ความแตกต่างระหว่างฟังก์ชันในบรรทัดและมาโครตัวประมวลผลล่วงหน้ามีค่อนข้างมาก มาโครตัวประมวลผลล่วงหน้าเป็นเพียงการแทนที่ข้อความในตอนท้ายของวัน คุณให้ความสามารถอย่างมากสำหรับคอมไพลเลอร์ในการตรวจสอบประเภทการตรวจสอบอาร์กิวเมนต์และประเภทการส่งคืน การประเมินอาร์กิวเมนต์นั้นแตกต่างกันมาก (หากนิพจน์ที่คุณส่งเข้าไปในฟังก์ชันมีผลข้างเคียงคุณจะมีช่วงเวลาที่สนุกมากในการแก้ไขข้อบกพร่อง) มีความแตกต่างเล็กน้อยเกี่ยวกับตำแหน่งที่สามารถใช้ฟังก์ชันและมาโครได้ ตัวอย่างเช่นถ้าฉันมี:
#define MACRO_FUNC(X) ...
โดยที่ MACRO_FUNC กำหนดเนื้อความของฟังก์ชันอย่างชัดเจน ต้องใช้ความระมัดระวังเป็นพิเศษเพื่อให้ทำงานได้อย่างถูกต้องในทุกกรณีที่สามารถใช้ฟังก์ชันได้ตัวอย่างเช่น MACRO_FUNC ที่เขียนไม่ดีอาจทำให้เกิดข้อผิดพลาดใน
if(MACRO_FUNC(y)) {
...body
}
สามารถใช้ฟังก์ชันปกติได้โดยไม่มีปัญหา
จากมุมมองของการเข้ารหัสฟังก์ชันอินไลน์ก็เหมือนกับฟังก์ชัน ดังนั้นความแตกต่างระหว่างฟังก์ชันแบบอินไลน์และมาโครจึงเหมือนกับความแตกต่างระหว่างฟังก์ชันและมาโคร
จากมุมมองของการคอมไพล์ฟังก์ชันอินไลน์จะคล้ายกับมาโคร มันถูกฉีดเข้าไปในรหัสโดยตรงไม่ได้ถูกเรียก
โดยทั่วไปคุณควรพิจารณาว่าฟังก์ชันอินไลน์เป็นฟังก์ชันปกติที่มีการเพิ่มประสิทธิภาพเล็กน้อยผสมอยู่และเช่นเดียวกับการปรับให้เหมาะสมส่วนใหญ่คอมไพเลอร์จะตัดสินใจว่าต้องการใช้งานจริงหรือไม่ บ่อยครั้งที่คอมไพเลอร์ยินดีที่จะเพิกเฉยต่อความพยายามใด ๆ ของโปรแกรมเมอร์ในการแทรกฟังก์ชันด้วยเหตุผลหลายประการ
ฟังก์ชันแบบอินไลน์จะทำงานเป็นการเรียกใช้ฟังก์ชันหากมีคำสั่งวนซ้ำหรือวนซ้ำอยู่ในนั้นเพื่อป้องกันการดำเนินการตามคำสั่งซ้ำ ค่อนข้างมีประโยชน์ในการบันทึกหน่วยความจำโดยรวมของโปรแกรมของคุณ
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{
return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases.
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
return a*a*a;
}
int main()
{
cout<<NUMBER<<endl<<number()<<endl;
cout<<CUBE(1+3); //Unexpected output 10
cout<<endl<<cube(1+3);// As expected 64
return 0;
}
โดยทั่วไปมาโครจะเร็วกว่าฟังก์ชันเนื่องจากไม่เกี่ยวข้องกับค่าใช้จ่ายในการเรียกฟังก์ชันจริง
ข้อเสียบางประการของมาโคร: ไม่มีการตรวจสอบประเภทยากที่จะดีบักเนื่องจากทำให้เกิดการเปลี่ยนอย่างง่ายมาโครไม่มีเนมสเปซดังนั้นมาโครในส่วนหนึ่งของโค้ดอาจส่งผลต่อส่วนอื่น ๆ มาโครอาจทำให้เกิดผลข้างเคียงดังที่แสดงในตัวอย่าง CUBE () ด้านบน
มาโครมักจะเป็นซับเดียว อย่างไรก็ตามอาจประกอบด้วยมากกว่าหนึ่งบรรทัดฟังก์ชันไม่มีข้อ จำกัด ดังกล่าว
#define TWO_N(n) 2 << n
นั้นcout << CUBE(TWO_N(3 + 1)) << endl;
? (เป็นการดีกว่าที่จะสิ้นสุดบรรทัดเอาต์พุตด้วยendl
ดีกว่าที่จะเริ่มต้นด้วย)