ไฟล์ส่วนหัวดีจริงหรือ [ปิด]


20

ฉันพบว่าไฟล์ส่วนหัวมีประโยชน์เมื่อเรียกดูไฟล์ต้นฉบับ C ++ เพราะพวกเขาให้ "บทสรุป" ของฟังก์ชั่นและสมาชิกข้อมูลทั้งหมดในชั้นเรียน ทำไมภาษาอื่น ๆ อีกมากมาย (เช่น Ruby, Python, Java และอื่น ๆ ) ไม่มีคุณสมบัติเช่นนี้ นี่เป็นพื้นที่ที่การใช้คำฟุ่มเฟือยของ C ++ มีประโยชน์ไหม?


ฉันไม่คุ้นเคยกับเครื่องมือใน C และ C ++ แต่ตัวแก้ไข IDE / รหัสส่วนใหญ่ที่ฉันเคยทำงานมีมุมมอง "โครงสร้าง" หรือ "โครงร่าง"
Michael K

1
ไฟล์ส่วนหัวไม่ดี - จำเป็น! - ดูคำตอบนี้ได้ที่ SO: stackoverflow.com/a/1319570/158483
Pete

คำตอบ:


31

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

C ++ นั้นเข้ากันได้กับ C ที่จำเป็นในการเก็บไฟล์ส่วนหัวไว้ แต่เพิ่มจำนวนมากไว้ด้านบนซึ่งทำให้การออกแบบค่อนข้างมีปัญหา อื่น ๆ ในคำถามที่พบบ่อยๆ

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

ไม่เลย ไฟล์ส่วนหัวไม่ดี พวกเขาเป็นตอนที่เรายังต้องมีคอมไพเลอร์และลิงเกอร์บนฟล้อปปี้ที่แยกต่างหากและการรวบรวมใช้เวลา 30 นาที ทุกวันนี้พวกเขาเข้าไปถึงแล้วและเป็นสัญลักษณ์ของการออกแบบที่ไม่ดี


8
ฉันเห็นด้วยกับสิ่งที่คุณเขียนส่วนใหญ่ แต่ย่อหน้าสุดท้ายทำให้สิ่งต่าง ๆ เป็นเรื่องง่าย ไฟล์ส่วนหัวยังคงมีวัตถุประสงค์ที่เป็นประโยชน์ในการกำหนดส่วนต่อประสานสาธารณะ
Benjamin Bannier

3
@ honk นั่นคือสิ่งที่ฉันได้รับกับคำถามของฉัน แต่ฉันคิดว่า IDEs ที่ทันสมัย ​​(ตามที่แนะนำโดย ElYusubov) ปฏิเสธสิ่งนี้
Matt Fichman

5
คุณสามารถเพิ่มได้ว่าคณะกรรมการ C ++ กำลังทำงานกับโมดูลซึ่งจะทำให้ C และ C ++ สามารถทำงานได้โดยไม่ต้องมีส่วนหัวหรือส่วนหัวที่ใช้ในวิธีที่มีประสิทธิภาพมากขึ้น นั่นจะทำให้คำตอบนี้ยุติธรรมมากขึ้น
Klaim

2
@MattFichman: การสร้างไฟล์ส่วนหัวบางอย่างเป็นสิ่งหนึ่ง (ซึ่งบรรณาธิการ / โปรแกรมเมอร์ส่วนใหญ่สามารถทำโดยอัตโนมัติในทางใดทางหนึ่ง) แต่จุดเกี่ยวกับ API สาธารณะคือว่ามันจำเป็นต้องมีการกำหนดและออกแบบโดยมนุษย์จริง
Benjamin Bannier

2
@honk ใช่ แต่ไม่มีเหตุผลสำหรับสิ่งนี้ที่จะถูกกำหนดในไฟล์แยกกัน มันอาจเป็นรหัสเดียวกันกับการใช้งาน เช่นเดียวกับ C # ที่กำลังทำอยู่
ร่าเริง

17

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

C ได้รับการออกแบบเพื่อให้การรวบรวมแต่ละครั้งสร้างโมดูลเดียว แต่ละไฟล์ต้นฉบับถูกคอมไพล์ในการรันแยกกันของคอมไพเลอร์ ในทางกลับกันไฟล์ส่วนหัวจะถูกแทรกลงในขั้นตอนการรวบรวมสำหรับไฟล์ต้นฉบับแต่ละไฟล์ที่อ้างอิงถึง

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

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

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

ไม่ว่าในกรณีใดคุณสามารถ "เรียกดู" ข้อมูลที่สร้างโดยขั้นตอนนี้โดยใช้เครื่องมือของภาษา โดยทั่วไปแล้ว IDE จะมีคลาสเบราว์เซอร์บางประเภท และหากภาษานั้นมี REPL ก็สามารถใช้ในการสร้างเอกสารสรุปของวัตถุที่โหลดได้


5
ไม่จริงอย่างเคร่งครัด - ส่วนใหญ่ถ้าไม่ได้ทั้งหมดคอมไพเลอร์ก่อนรวบรวมหัวเมื่อพวกเขาเห็นพวกเขาดังนั้นรวมถึงพวกเขาในการรวบรวมหน่วยต่อไปไม่เลวร้ายยิ่งกว่าการหาบล็อกของรหัสรวบรวมไว้ล่วงหน้า มันไม่ต่างไปจากการพูดรหัส. NET ซ้ำ ๆ 'building' system.data.types สำหรับไฟล์ C # ทุกไฟล์ที่คุณคอมไพล์ เหตุผลที่โปรแกรม C / C ++ ใช้เวลานานมากก็เพราะว่ามันถูกคอมไพล์แล้ว (และปรับให้เหมาะสม) ความต้องการทั้งหมดของโปรแกรม. NET คือการแยกวิเคราะห์ลงใน IL รันไทม์ทำการรวบรวมจริงผ่าน JIT ที่กล่าวว่าไฟล์ต้นฉบับ 300 ไฟล์ในรหัส C # ต้องใช้เวลาค่อนข้างนานในการรวบรวมเช่นกัน
gbjbaanb

7

ไม่ชัดเจนในสิ่งที่คุณหมายถึงโดยการเรียกดูไฟล์สำหรับฟังก์ชั่นและข้อมูลสมาชิก อย่างไรก็ตาม IDE ส่วนใหญ่มีเครื่องมือในการเรียกดูคลาสและดูข้อมูลสมาชิกคลาส

ตัวอย่างเช่น: Visual Studio มีClass ViewและObject browserให้การทำงานที่คุณร้องขออย่างดี ชอบในภาพหน้าจอต่อไปนี้

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่


4

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

ประกอบกับระบบมาโครของ C (สืบทอดโดย C ++) ทำให้เกิดข้อผิดพลาดและสถานการณ์ที่สับสน

เพื่อแสดงให้เห็นว่าหากส่วนหัวหนึ่งกำหนดสัญลักษณ์บางอย่างโดยใช้มาโครสำหรับการใช้งานและส่วนหัวอื่นใช้สัญลักษณ์ในรูปแบบอื่นเช่นชื่อฟังก์ชั่นดังนั้นลำดับของการรวมจะมีผลอย่างมากต่อผลลัพธ์สุดท้าย มีตัวอย่างมากมาย


2

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

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

ดังนั้น C / C ++ จึงเป็นคำสั่งที่เทียบเท่า (ของแปลก ๆ ) ของอินเตอร์เฟสในไฟล์ส่วนหัวเป็นสิ่งที่ดี

ฉันรู้ว่ามีผู้สนับสนุนของระบบอื่น ๆ ที่ไม่ชอบพวกเขาด้วยเหตุผลรวมถึง 'มันยากกว่าที่จะแฮ็คโค้ดออกมาถ้าคุณต้องใส่สิ่งต่าง ๆ ในไฟล์ 2 ไฟล์' แต่ทัศนคติของฉันก็คือ และเมื่อคุณเริ่มเขียน / ออกแบบโค้ดด้วยความคิดที่มากขึ้นคุณจะต้องกำหนดส่วนหัว / ส่วนต่อประสานเป็นหลัก


ส่วนต่อประสานและการนำไปใช้งานในไฟล์ต่างๆ? หากคุณต้องการสิ่งนี้โดยการออกแบบสิ่งนี้ยังคงเป็นเรื่องธรรมดามากที่จะกำหนดอินเทอร์เฟซ (หรือคลาสนามธรรม) ในภาษาเช่น Java / C # ในไฟล์เดียวและซ่อนการนำไปใช้ในไฟล์อื่น ๆ สิ่งนี้ไม่ต้องการไฟล์ส่วนหัวที่เก่าและยุ่งยาก
Petter Nordlander

ใช่มั้ย? ไม่ใช่ที่ฉันพูดคุณกำหนดอินเทอร์เฟซและการใช้งานคลาสใน 2 ไฟล์
gbjbaanb

2
ที่ไม่ต้องใช้ไฟล์ส่วนหัวและไม่ทำให้ไฟล์ส่วนหัวดีขึ้น ใช่พวกเขามีความจำเป็นในภาษาที่มีมรดกตกทอด แต่เป็นแนวคิดที่พวกเขาไม่จำเป็นต้องแบ่งส่วนต่อประสานงาน / การนำไปใช้งานในไฟล์ต่าง ๆ (อันที่จริงแล้วมันทำให้แย่ลง)
Petter Nordlander

@ จดหมายข่าวฉันคิดว่าคุณเข้าใจผิดคิดว่าพวกเขาจะไม่แตกต่างกัน - ส่วนหัวมีอยู่เพื่อให้อินเตอร์เฟซในภาษา C และแม้ว่าคุณจะถูกต้อง - คุณสามารถรวม 2 (เป็นจำนวนมากทำด้วยรหัส C เฉพาะส่วนหัว) ไม่ใช่ แนวปฏิบัติที่ดีที่สุดที่ฉันเคยเห็นมาในหลาย ๆ ที่ C # devs ทั้งหมดที่ฉันเห็นแยกอินเทอร์เฟซออกจากคลาสตัวอย่างเช่น เป็นวิธีปฏิบัติที่ดีในการทำเช่นนี้จากนั้นคุณสามารถรวมไฟล์อินเทอร์เฟซในไฟล์อื่น ๆ หลายไฟล์โดยไม่มีมลพิษ ตอนนี้ถ้าคุณคิดว่าการแบ่งนี้เป็นแนวปฏิบัติที่ดีที่สุด (เป็น) วิธีที่จะทำให้สำเร็จใน C คือการใช้ไฟล์ส่วนหัว
gbjbaanb

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

1

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

ฉันเชื่อว่าฉันตั้งใจจะให้โค้ดใช้ไลบรารีวัตถุอื่น ๆ โดยไม่ต้องนำเข้าเนื้อหาทั้งหมดของสคริปต์ เพียงคุณมีส่วนหัวในการรวบรวมและเชื่อมโยง มันช่วยประหยัดเวลาและรอบที่ ในตัวอย่างนั้นเป็นความคิดที่ดี แต่เป็นเพียงวิธีหนึ่งในการแก้ไขปัญหาเหล่านี้

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

ทำไมภาษาอื่นถึงไม่ใช้คุณสมบัติเดียวกัน? เพราะภาษาอื่นมาจากคนอื่นและนักออกแบบ / ผู้สร้างเหล่านั้นมีวิสัยทัศน์ที่แตกต่างกันว่าควรทำงานอย่างไร

คำตอบที่ดีที่สุดคือการทำงานที่คุณต้องทำและทำให้คุณมีความสุข


0

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.