ฉันควรใส่รวมไว้ในไฟล์ส่วนหัวหรือไฟล์ต้นฉบับหรือไม่ ถ้าไฟล์ส่วนหัวมีคำสั่ง include ถ้าฉันรวมไฟล์ส่วนหัวนั้นไว้ในซอร์สไฟล์ซอร์สของฉันจะมีไฟล์ทั้งหมดที่อยู่ในส่วนหัวของฉันหรือไม่ หรือฉันควรรวมไว้ในไฟล์ต้นฉบับเท่านั้น?
ฉันควรใส่รวมไว้ในไฟล์ส่วนหัวหรือไฟล์ต้นฉบับหรือไม่ ถ้าไฟล์ส่วนหัวมีคำสั่ง include ถ้าฉันรวมไฟล์ส่วนหัวนั้นไว้ในซอร์สไฟล์ซอร์สของฉันจะมีไฟล์ทั้งหมดที่อยู่ในส่วนหัวของฉันหรือไม่ หรือฉันควรรวมไว้ในไฟล์ต้นฉบับเท่านั้น?
คำตอบ:
ใส่รวมไว้ในส่วนหัวก็ต่อเมื่อส่วนหัวนั้นต้องการเท่านั้น
ตัวอย่าง:
size_t
ประเภทผลตอบแทนการทำงานของคุณ จากนั้น#include <stddef.h>
ในไฟล์ส่วนหัวstrlen
การใช้งานฟังก์ชั่นของคุณ จากนั้น#include <string.h>
ในไฟล์ต้นฉบับsize_t
?
size_t
หรือstd::string
?
มีความไม่เห็นด้วยเล็กน้อยเกี่ยวกับเรื่องนี้ในช่วงหลายปีที่ผ่านมา ครั้งหนึ่งเป็นเรื่องธรรมดาที่ส่วนหัวจะประกาศเฉพาะสิ่งที่อยู่ในโมดูลใดก็ตามที่เกี่ยวข้องกับส่วนหัวจำนวนมากจึงมีข้อกำหนดเฉพาะที่คุณ#include
กำหนดส่วนหัว (ตามลำดับที่ระบุ) โปรแกรมเมอร์ C แบบดั้งเดิมบางคนยังคงทำตามแบบจำลองนี้ (ในทางศาสนาอย่างน้อยก็ในบางกรณี)
เมื่อเร็ว ๆ นี้มีการเคลื่อนไหวเพื่อสร้างส่วนหัวส่วนใหญ่แบบสแตนด์อโลน หากส่วนหัวนั้นต้องการอย่างอื่นส่วนหัวจะจัดการกับสิ่งนั้นเองเพื่อให้แน่ใจว่าสิ่งที่ต้องการนั้นรวมอยู่ด้วย (ในลำดับที่ถูกต้องหากมีปัญหาในการสั่งซื้อ) โดยส่วนตัวแล้วฉันชอบสิ่งนี้ - โดยเฉพาะอย่างยิ่งเมื่อลำดับของส่วนหัวมีความสำคัญการแก้ปัญหาครั้งเดียวแทนที่จะต้องให้ทุกคนที่ใช้มันแก้ปัญหาอีกครั้ง
โปรดทราบว่าส่วนหัวส่วนใหญ่ควรมีเฉพาะการประกาศเท่านั้น ซึ่งหมายความว่าการเพิ่มส่วนหัวที่ไม่จำเป็นไม่ควร (ตามปกติ) มีผลต่อการเรียกใช้งานสุดท้าย สิ่งที่เลวร้ายที่สุดที่เกิดขึ้นคือการคอมไพล์ช้าลงเล็กน้อย
ไฟล์ของคุณ#include
ควรเป็นไฟล์ส่วนหัวและแต่ละไฟล์ (แหล่งที่มาหรือส่วนหัว) ควร#include
เป็นไฟล์ส่วนหัวที่ต้องการ ไฟล์ส่วนหัวควร#include
เป็นไฟล์ส่วนหัวขั้นต่ำที่จำเป็นและไฟล์ต้นฉบับก็ควรเช่นกันแม้ว่าไฟล์ต้นฉบับจะไม่สำคัญเท่า
ซอร์สไฟล์จะมีส่วนหัว#include
และส่วนหัว#include
และอื่น ๆ จนถึงความลึกของการซ้อนสูงสุด นี่คือเหตุผลที่คุณไม่ต้องการฟุ่มเฟือย#include
มีไฟล์ส่วนหัวที่อาจทำให้ไฟล์ต้นฉบับรวมไฟล์ส่วนหัวจำนวนมากที่อาจไม่ต้องการได้ทำให้การคอมไพล์ช้าลง
ซึ่งหมายความว่าอาจเป็นไปได้ทั้งหมดที่ไฟล์ส่วนหัวอาจรวมสองครั้งและอาจเป็นปัญหาได้ วิธีการดั้งเดิมคือการใส่ "รวมยาม" ในไฟล์ส่วนหัวเช่นนี้สำหรับไฟล์ foo.h:
#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
แนวทางที่ฉันพัฒนามากว่ายี่สิบปีก็คือ
พิจารณาห้องสมุด
มีไฟล์ C หลายไฟล์ไฟล์ H ภายในหนึ่งไฟล์และไฟล์ H ภายนอกหนึ่งไฟล์ ไฟล์ C รวมถึงไฟล์ H ภายใน ไฟล์ H ภายในมีไฟล์ H ภายนอก
คุณจะเห็นว่าจากคอมไพเลอร์ POV เมื่อคอมไพเลอร์ไฟล์ C มีลำดับชั้น
ภายนอก -> ภายใน -> รหัส C
นี่คือลำดับที่ถูกต้องเนื่องจากสิ่งที่อยู่ภายนอกคือทุกสิ่งที่บุคคลที่สามจำเป็นต้องใช้ไลบรารี สิ่งที่อยู่ภายในจำเป็นสำหรับการคอมไพล์โค้ด C
หากไฟล์ส่วนหัว A #includes
ไฟล์ส่วนหัว B และ C ดังนั้นไฟล์ต้นฉบับทุกไฟล์ที่#includes
A จะได้รับ B และ C #included
ด้วย ตัวประมวลผลล่วงหน้าเพียงแค่ทำการแทนที่ข้อความ: ทุกที่ที่พบข้อความที่ระบุว่า#include <foo.h>
แทนที่ด้วยข้อความของfoo.h
ไฟล์
มีความคิดเห็นที่แตกต่างกันว่าคุณควรใส่#includes
ส่วนหัวหรือไฟล์ต้นฉบับ โดยส่วนตัวแล้วฉันชอบที่จะใส่ทั้งหมด#includes
ในไฟล์ต้นฉบับโดยค่าเริ่มต้น แต่ไฟล์ส่วนหัวใด ๆ ที่ไม่สามารถคอมไพล์โดยไม่มีส่วนหัวที่จำเป็นต้องมีอื่น ๆ ควรเป็น#include
ส่วนหัวเหล่านั้นเอง
และไฟล์ส่วนหัวทุกไฟล์ควรมี include guard เพื่อป้องกันไม่ให้รวมหลายครั้ง
สร้างไฟล์ทั้งหมดของคุณเพื่อให้สามารถสร้างได้โดยใช้เฉพาะสิ่งที่มีอยู่ หากคุณไม่ต้องการการรวมในส่วนหัวของคุณให้ลบออก ในโปรเจ็กต์ขนาดใหญ่หากคุณไม่รักษาระเบียบวินัยนี้คุณจะปล่อยให้ตัวเองเปิดกว้างที่จะทำลายโครงสร้างทั้งหมดเมื่อมีคนลบการรวมออกจากไฟล์ส่วนหัวที่ผู้บริโภคใช้ไฟล์นั้นและไม่แม้แต่ส่วนหัว
ในบางสภาพแวดล้อมการคอมไพล์จะเร็วที่สุดหากมีเพียงไฟล์ส่วนหัวที่ต้องการ ในสภาพแวดล้อมอื่นการคอมไพล์จะได้รับการปรับให้เหมาะสมหากไฟล์ต้นฉบับทั้งหมดสามารถใช้คอลเลกชันหลักของส่วนหัวเดียวกันได้ (ไฟล์บางไฟล์อาจมีส่วนหัวเพิ่มเติมนอกเหนือจากชุดย่อยทั่วไป) ตามหลักการแล้วควรสร้างส่วนหัวเพื่อให้การดำเนินการ #include หลายรายการไม่มีผล อาจเป็นการดีที่จะล้อมรอบคำสั่ง #include ด้วยการตรวจสอบ include-guard ของไฟล์ที่จะรวมแม้ว่าจะสร้างการอ้างอิงตามรูปแบบของยามนั้น นอกจากนี้ขึ้นอยู่กับพฤติกรรมการแคชไฟล์ของระบบ #include ที่ไม่จำเป็นซึ่งเป้าหมายจะกลายเป็น # ifdef'ed โดยสมบูรณ์อาจใช้เวลาไม่นาน
สิ่งที่ควรพิจารณาอีกประการหนึ่งคือถ้าฟังก์ชันนำตัวชี้ไปที่โครงสร้างก็สามารถเขียนต้นแบบเป็น
เป็นโมฆะ foo (โครงสร้าง BAR_s * bar);
ไม่มีคำจำกัดความสำหรับ BAR_s ต้องอยู่ในขอบเขต แนวทางที่มีประโยชน์มากในการหลีกเลี่ยงสิ่งที่ไม่จำเป็น ได้แก่
PS - ในหลายโครงการของฉันจะมีไฟล์ซึ่งคาดว่าทุกโมดูลจะ # รวมถึงสิ่งต่างๆเช่น typedef สำหรับขนาดจำนวนเต็มและโครงสร้างและสหภาพทั่วไปบางส่วน [เช่น
typedef union { ยาวไม่ได้ลงนาม l; lw สั้นที่ไม่ได้ลงนาม [2]; ถ่านปอนด์ที่ไม่ได้ลงนาม [4]; } U_QUAD;
(ใช่ฉันรู้ว่าฉันจะมีปัญหาถ้าฉันย้ายไปใช้สถาปัตยกรรม big-endian แต่เนื่องจากคอมไพเลอร์ของฉันไม่อนุญาตให้มีโครงสร้างที่ไม่ระบุชื่อในสหภาพการใช้ตัวระบุที่มีชื่อสำหรับไบต์ภายในสหภาพจึงจำเป็นต้องมีการเข้าถึงเป็น theUnion.b.b1 เป็นต้นซึ่งดูค่อนข้างน่ารำคาญ
ไฟล์ต้นฉบับของคุณจะมีคำสั่ง include หากคุณใส่ไว้ในส่วนหัว อย่างไรก็ตามในบางกรณีควรใส่ไว้ในไฟล์ต้นฉบับจะดีกว่า
โปรดจำไว้ว่าหากคุณรวมส่วนหัวนั้นไว้ในแหล่งที่มาอื่นพวกเขาจะได้รับการรวมจากส่วนหัวด้วยและนั่นก็ไม่เป็นที่ต้องการเสมอไป คุณควรรวมเฉพาะสิ่งของที่ใช้เท่านั้น
คุณควรรวมเฉพาะไฟล์ในส่วนหัวที่คุณต้องประกาศค่าคงที่และการประกาศฟังก์ชัน ในทางเทคนิคแล้วสิ่งเหล่านี้จะรวมอยู่ในไฟล์ต้นฉบับของคุณด้วย แต่เพื่อความชัดเจนคุณควรรวมเฉพาะไฟล์ที่คุณจำเป็นต้องใช้ในแต่ละไฟล์เท่านั้น คุณควรปกป้องพวกเขาในส่วนหัวของคุณจากการรวมหลายรายการด้วยเหตุนี้:
#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H
...definition of header file...
#endif
ซึ่งจะป้องกันไม่ให้รวมส่วนหัวหลายครั้งส่งผลให้คอมไพเลอร์มีข้อผิดพลาด