อะไรคือประโยชน์ของไลบรารีเฉพาะส่วนหัวและเหตุใดคุณจึงเขียนแบบนั้นเพื่อต่อต้านการใส่การใช้งานลงในไฟล์แยก
อะไรคือประโยชน์ของไลบรารีเฉพาะส่วนหัวและเหตุใดคุณจึงเขียนแบบนั้นเพื่อต่อต้านการใส่การใช้งานลงในไฟล์แยก
คำตอบ:
มีสถานการณ์ที่ไลบรารีเฉพาะส่วนหัวเป็นตัวเลือกเดียวตัวอย่างเช่นเมื่อจัดการกับเทมเพลต
การมีไลบรารีเฉพาะส่วนหัวทำให้คุณไม่ต้องกังวลเกี่ยวกับแพลตฟอร์มต่างๆที่อาจใช้ไลบรารี เมื่อคุณแยกการนำไปใช้งานคุณมักจะทำเช่นนั้นเพื่อซ่อนรายละเอียดการใช้งานและแจกจ่ายไลบรารีเป็นชุดส่วนหัวและไลบรารี ( lib
, dll
หรือ.so
ไฟล์) แน่นอนว่าต้องรวบรวมสำหรับระบบปฏิบัติการ / เวอร์ชันต่างๆที่คุณให้การสนับสนุน
คุณยังสามารถแจกจ่ายไฟล์การนำไปใช้งานได้ แต่นั่นหมายถึงขั้นตอนพิเศษสำหรับผู้ใช้นั่นคือการรวบรวมไลบรารีของคุณก่อนใช้งาน
แน่นอนว่าสิ่งนี้บังคับใช้เป็นกรณีๆ ไป ตัวอย่างเช่นบางครั้งไลบรารีส่วนหัวเท่านั้นจะเพิ่มขึ้นขนาดรหัส & เวลารวบรวม
ประโยชน์ของไลบรารีส่วนหัวเท่านั้น:
ข้อเสียของไลบรารีส่วนหัวเท่านั้น:
ไฟล์ออบเจ็กต์ที่ใหญ่ขึ้น ทุกเมธอดแบบอินไลน์จากไลบรารีที่ใช้ในซอร์สไฟล์บางไฟล์จะได้รับสัญลักษณ์ที่ไม่ชัดเจนคำนิยามนอกบรรทัดในไฟล์อ็อบเจ็กต์ที่คอมไพล์แล้วสำหรับไฟล์ต้นฉบับนั้น ซึ่งจะทำให้คอมไพเลอร์ช้าลงและยังทำให้ตัวเชื่อมโยงช้าลงด้วย คอมไพเลอร์ต้องสร้างการขยายตัวทั้งหมดจากนั้นตัวเชื่อมโยงจะต้องกรองออก
การรวบรวมอีกต่อไป นอกเหนือจากปัญหาการขยายตัวที่กล่าวถึงข้างต้นการคอมไพล์จะใช้เวลานานขึ้นเนื่องจากส่วนหัวมีขนาดใหญ่กว่าไลบรารีส่วนหัวเท่านั้นมากกว่าไลบรารีที่คอมไพล์ ส่วนหัวขนาดใหญ่เหล่านี้จะต้องมีการแยกวิเคราะห์สำหรับไฟล์ต้นฉบับแต่ละไฟล์ที่ใช้ไลบรารี ปัจจัยอีกประการหนึ่งคือไฟล์ส่วนหัวเหล่านั้นในไลบรารีส่วนหัวเท่านั้นต้องเป็น#include
ส่วนหัวที่จำเป็นสำหรับคำจำกัดความแบบอินไลน์เช่นเดียวกับส่วนหัวที่จำเป็นเมื่อสร้างไลบรารีเป็นไลบรารีที่คอมไพล์แล้ว
การรวบรวมที่ยุ่งเหยิงมากขึ้น คุณได้รับการอ้างอิงมากขึ้นด้วยไลบรารีส่วนหัวเท่านั้นเนื่องจาก#include
จำเป็นต้องมีไลบรารีส่วนหัวเท่านั้น เปลี่ยนการใช้งานฟังก์ชันหลักบางอย่างในไลบรารีและคุณอาจต้องทำการคอมไพล์โครงการใหม่ทั้งหมด ทำการเปลี่ยนแปลงนั้นในซอร์สไฟล์สำหรับไลบรารีที่คอมไพล์และสิ่งที่คุณต้องทำคือคอมไพล์ซอร์สไฟล์ไลบรารีหนึ่งไฟล์ใหม่อัพเดตไลบรารีที่คอมไพล์ด้วยไฟล์. o ใหม่และเชื่อมโยงแอ็พพลิเคชันอีกครั้ง
ยากขึ้นสำหรับมนุษย์ที่จะอ่าน แม้จะมีเอกสารที่ดีที่สุด แต่ผู้ใช้ห้องสมุดก็มักจะต้องหันไปอ่านส่วนหัวของห้องสมุด ส่วนหัวในไลบรารีส่วนหัวเท่านั้นจะเต็มไปด้วยรายละเอียดการใช้งานที่ช่วยในการทำความเข้าใจอินเทอร์เฟซ ด้วยไลบรารีที่คอมไพล์แล้วสิ่งที่คุณเห็นคืออินเทอร์เฟซและคำอธิบายสั้น ๆ เกี่ยวกับสิ่งที่การใช้งานทำและนั่นคือทั้งหมดที่คุณต้องการ นั่นคือทั้งหมดที่คุณควรต้องการ คุณไม่ควรรู้รายละเอียดการใช้งานเพื่อทราบวิธีใช้ไลบรารี
detail
ที่เรียกว่า
ฉันรู้ว่านี่เป็นกระทู้เก่า แต่ไม่มีใครพูดถึงอินเทอร์เฟซ ABI หรือปัญหาเฉพาะของคอมไพเลอร์ ฉันก็เลยคิดว่าฉันจะ
โดยพื้นฐานแล้วขึ้นอยู่กับแนวคิดของคุณไม่ว่าจะเป็นการเขียนไลบรารีที่มีส่วนหัวเพื่อแจกจ่ายให้กับผู้คนหรือใช้ซ้ำกับตัวเองเทียบกับการมีทุกอย่างในส่วนหัว หากคุณกำลังคิดที่จะนำไฟล์ส่วนหัวและไฟล์ต้นฉบับมาใช้ซ้ำและคอมไพล์ใหม่ในทุกโปรเจ็กต์สิ่งนี้ไม่ได้มีผลบังคับใช้
โดยทั่วไปถ้าคุณคอมไพล์โค้ด C ++ ของคุณและสร้างไลบรารีด้วยคอมไพเลอร์หนึ่งคอมไพเลอร์ผู้ใช้พยายามใช้ไลบรารีนั้นกับคอมไพเลอร์อื่นหรือคอมไพเลอร์รุ่นอื่นคุณอาจได้รับข้อผิดพลาดตัวเชื่อมโยงหรือพฤติกรรมรันไทม์แปลก ๆ เนื่องจากความไม่ลงรอยกันของไบนารี
ตัวอย่างเช่นผู้จำหน่ายคอมไพเลอร์มักเปลี่ยนการนำ STL ไปใช้ระหว่างเวอร์ชัน หากคุณมีฟังก์ชันในไลบรารีที่ยอมรับ std :: vector ก็คาดว่าไบต์ในคลาสนั้นจะถูกจัดเรียงตามวิธีการจัดเรียงเมื่อไลบรารีถูกคอมไพล์ หากในคอมไพเลอร์เวอร์ชันใหม่ผู้ขายได้ทำการปรับปรุงประสิทธิภาพให้กับ std :: vector แล้วโค้ดของผู้ใช้จะเห็นคลาสใหม่ซึ่งอาจมีโครงสร้างที่แตกต่างกันและส่งผ่านโครงสร้างใหม่นั้นไปยังไลบรารีของคุณ ทุกอย่างลงเนินจากที่นั่น ... นี่คือเหตุผลที่ไม่แนะนำให้ส่งวัตถุ STL ข้ามขอบเขตไลบรารี เช่นเดียวกับประเภท C Run-Time (CRT)
ในขณะที่พูดถึง CRT โดยทั่วไปแล้วไลบรารีของคุณและซอร์สโค้ดของผู้ใช้จะต้องเชื่อมโยงกับ CRT เดียวกัน ด้วย Visual Studio หากคุณสร้างไลบรารีของคุณโดยใช้ CRT แบบมัลติเธรด แต่ผู้ใช้เชื่อมโยงกับ Multithreaded Debug CRT คุณจะมีปัญหาในการเชื่อมโยงเนื่องจากไลบรารีของคุณอาจไม่พบสัญลักษณ์ที่ต้องการ ฉันจำไม่ได้ว่าเป็นฟังก์ชันใด แต่สำหรับ Visual Studio 2015 Microsoft ได้สร้างฟังก์ชัน CRT แบบอินไลน์หนึ่งฟังก์ชัน ทันใดนั้นมันอยู่ในส่วนหัวไม่ใช่ไลบรารี CRT ดังนั้นไลบรารีที่คาดว่าจะพบในเวลาลิงก์ไม่สามารถทำได้อีกต่อไปและเกิดข้อผิดพลาดในการเชื่อมโยง ผลลัพธ์ก็คือไลบรารีเหล่านี้จำเป็นต้องทำการคอมไพล์ใหม่ด้วย Visual Studio 2015
คุณยังสามารถได้รับข้อผิดพลาดในการเชื่อมโยงหรือพฤติกรรมแปลก ๆ หากคุณใช้ Windows API แต่คุณสร้างด้วยการตั้งค่า Unicode ที่แตกต่างกันสำหรับผู้ใช้ไลบรารี เนื่องจาก Windows API มีฟังก์ชันที่ใช้สตริง Unicode หรือ ASCII และมาโคร / กำหนดว่าจะใช้ประเภทใดที่ถูกต้องโดยอัตโนมัติตามการตั้งค่า Unicode ของโครงการ หากคุณส่งสตริงข้ามขอบเขตไลบรารีซึ่งเป็นประเภทที่ไม่ถูกต้องสิ่งต่าง ๆ จะพังเมื่อรันไทม์ หรือคุณอาจพบว่าโปรแกรมไม่ได้เชื่อมโยงในตอนแรก
สิ่งเหล่านี้ยังเป็นจริงสำหรับการส่งผ่านวัตถุ / ประเภทข้ามขอบเขตไลบรารีจากไลบรารีบุคคลที่สามอื่น ๆ (เช่นเวกเตอร์ Eigen หรือเมทริกซ์ GSL) หากไลบรารีของบุคคลที่สามเปลี่ยนส่วนหัวระหว่างคุณคอมไพล์ไลบรารีและผู้ใช้ของคุณคอมไพล์โค้ดของพวกเขาสิ่งต่างๆจะพัง
โดยพื้นฐานแล้วเพื่อความปลอดภัยสิ่งเดียวที่คุณสามารถข้ามผ่านขอบเขตไลบรารีได้ถูกสร้างขึ้นในประเภทและข้อมูลเก่าธรรมดา (POD) ตามหลักการแล้ว POD ใด ๆ ควรอยู่ในโครงสร้างที่กำหนดไว้ในส่วนหัวของคุณเองและอย่าพึ่งพาส่วนหัวของบุคคลที่สาม
หากคุณจัดเตรียมไลบรารีส่วนหัวเท่านั้นโค้ดทั้งหมดจะถูกคอมไพล์ด้วยการตั้งค่าคอมไพเลอร์เดียวกันและเทียบกับส่วนหัวเดียวกันดังนั้นปัญหาเหล่านี้จึงหมดไป (การให้ไลบรารีส่วนที่สามเวอร์ชันที่คุณและผู้ใช้ของคุณใช้นั้นเข้ากันได้กับ API)
อย่างไรก็ตามมีข้อเสียที่ได้กล่าวไว้ข้างต้นเช่นเวลาในการรวบรวมข้อมูลที่เพิ่มขึ้น นอกจากนี้คุณอาจดำเนินธุรกิจดังนั้นคุณอาจไม่ต้องการส่งรายละเอียดการติดตั้งซอร์สโค้ดทั้งหมดของคุณให้กับผู้ใช้ของคุณในกรณีที่หนึ่งในนั้นขโมยไป
"ประโยชน์" หลักคือคุณต้องส่งซอร์สโค้ดดังนั้นคุณจะพบกับรายงานข้อผิดพลาดในเครื่องและคอมไพเลอร์ที่คุณไม่เคยได้ยินมาก่อน เมื่อไลบรารีเป็นเทมเพลตทั้งหมดคุณไม่มีทางเลือกมากนัก แต่เมื่อคุณมีทางเลือกส่วนหัวเท่านั้นมักเป็นตัวเลือกด้านวิศวกรรมที่ไม่ดี (ในทางกลับกันส่วนหัวหมายความว่าคุณไม่จำเป็นต้องบันทึกขั้นตอนการรวมใด ๆ )
การซับในสามารถทำได้โดย Link Time Optimization (LTO)
ฉันต้องการเน้นสิ่งนี้เนื่องจากจะลดค่าของหนึ่งในสองข้อได้เปรียบหลักของไลบรารีเฉพาะส่วนหัว: "คุณต้องการคำจำกัดความที่ส่วนหัวเป็นแบบอินไลน์"
ตัวอย่างที่เป็นรูปธรรมเล็กน้อยนี้แสดงไว้ที่: การเพิ่มประสิทธิภาพเวลาลิงก์และแบบอินไลน์
ดังนั้นคุณเพียงแค่ส่งแฟล็กและการอินไลน์ในไฟล์อ็อบเจ็กต์สามารถทำได้โดยไม่ต้องปรับโครงสร้างใด ๆ เลยไม่จำเป็นต้องเก็บคำจำกัดความไว้ในส่วนหัวอีกต่อไป
อย่างไรก็ตาม LTO อาจมีข้อเสียของตัวเองเช่นกัน: มีเหตุผลอะไรที่ไม่ใช้การเพิ่มประสิทธิภาพเวลาเชื่อมโยง (LTO)?