จะใส่ข้อความส่วนหัวหรือแหล่งที่มาได้ที่ไหน?


107

ฉันควรใส่รวมไว้ในไฟล์ส่วนหัวหรือไฟล์ต้นฉบับหรือไม่ ถ้าไฟล์ส่วนหัวมีคำสั่ง include ถ้าฉันรวมไฟล์ส่วนหัวนั้นไว้ในซอร์สไฟล์ซอร์สของฉันจะมีไฟล์ทั้งหมดที่อยู่ในส่วนหัวของฉันหรือไม่ หรือฉันควรรวมไว้ในไฟล์ต้นฉบับเท่านั้น?


2
รายการที่ซ้ำกันหลายรายการใน SO เช่นควรใส่ "รวม" ไว้ที่ไหนใน C ++
Paul R

คำตอบ:


142

ใส่รวมไว้ในส่วนหัวก็ต่อเมื่อส่วนหัวนั้นต้องการเท่านั้น

ตัวอย่าง:

  • size_tประเภทผลตอบแทนการทำงานของคุณ จากนั้น#include <stddef.h>ในไฟล์ส่วนหัว
  • strlenการใช้งานฟังก์ชั่นของคุณ จากนั้น#include <string.h>ในไฟล์ต้นฉบับ

2
จะเกิดอะไรขึ้นถ้าฟังก์ชันของฉันใช้อาร์กิวเมนต์ประเภทsize_t?
andrybak

คำถามเดียวกันที่ขยายไปยัง c ++: จะเกิดอะไรขึ้นถ้าโครงสร้าง / คลาสของฉันมีฟิลด์ / สมาชิกประเภทsize_tหรือstd::string?
andrybak

10
เหตุผลคืออะไร?
Patrizio Bertoni

ฉันมีสถานการณ์แบบใช้สายคลาส C ++ A มีอ็อบเจ็กต์ของคลาส B อื่นและฉันไม่สามารถใช้การประกาศ B และ end-up ไปข้างหน้ารวมถึงส่วนหัว B ภายในส่วนหัว A (การใช้ตัวชี้ไม่มีปัญหานี้)
shuva

@andrybak ไฟล์ซอร์สของคุณควรมีไฟล์ส่วนหัวของคุณดังนั้นการรวมส่วนหัวของคุณจะได้รับแหล่งที่มาของคุณด้วย
Jeremy Trifilo

27

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

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

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


2
หากส่วนหัวทั้งหมดเขียนในลักษณะที่สองไม่ควรมีปัญหาในการสั่งซื้อเลย การมีปัญหาในการสั่งซื้อในส่วนหัวมักหมายความว่าคุณไม่ได้รวมทุกสิ่งที่คุณต้องการไว้ในส่วนหัว
Goodbye SE

12

ไฟล์ของคุณ#includeควรเป็นไฟล์ส่วนหัวและแต่ละไฟล์ (แหล่งที่มาหรือส่วนหัว) ควร#includeเป็นไฟล์ส่วนหัวที่ต้องการ ไฟล์ส่วนหัวควร#includeเป็นไฟล์ส่วนหัวขั้นต่ำที่จำเป็นและไฟล์ต้นฉบับก็ควรเช่นกันแม้ว่าไฟล์ต้นฉบับจะไม่สำคัญเท่า

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

ซึ่งหมายความว่าอาจเป็นไปได้ทั้งหมดที่ไฟล์ส่วนหัวอาจรวมสองครั้งและอาจเป็นปัญหาได้ วิธีการดั้งเดิมคือการใส่ "รวมยาม" ในไฟล์ส่วนหัวเช่นนี้สำหรับไฟล์ foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif

ฉันรู้ว่าคำตอบนี้เก่ามาก แต่ตั้งแต่นั้นมาพวกเขาก็เพิ่ม #pragma หนึ่งครั้งดังนั้นคุณจะไม่ต้องใส่ #ifndef เมื่อประกาศ # รวมถึงฉันโพสต์เพราะเนื่องจากเธรดที่เก่ากว่านี้ แต่ได้รับความนิยม / โหวตมากขึ้นมักจะอยู่ที่ด้านบนของการค้นหาของ Google
Dogunbound hounds

6

แนวทางที่ฉันพัฒนามากว่ายี่สิบปีก็คือ

พิจารณาห้องสมุด

มีไฟล์ C หลายไฟล์ไฟล์ H ภายในหนึ่งไฟล์และไฟล์ H ภายนอกหนึ่งไฟล์ ไฟล์ C รวมถึงไฟล์ H ภายใน ไฟล์ H ภายในมีไฟล์ H ภายนอก

คุณจะเห็นว่าจากคอมไพเลอร์ POV เมื่อคอมไพเลอร์ไฟล์ C มีลำดับชั้น

ภายนอก -> ภายใน -> รหัส C

นี่คือลำดับที่ถูกต้องเนื่องจากสิ่งที่อยู่ภายนอกคือทุกสิ่งที่บุคคลที่สามจำเป็นต้องใช้ไลบรารี สิ่งที่อยู่ภายในจำเป็นสำหรับการคอมไพล์โค้ด C


4

หากไฟล์ส่วนหัว A #includesไฟล์ส่วนหัว B และ C ดังนั้นไฟล์ต้นฉบับทุกไฟล์ที่#includesA จะได้รับ B และ C #includedด้วย ตัวประมวลผลล่วงหน้าเพียงแค่ทำการแทนที่ข้อความ: ทุกที่ที่พบข้อความที่ระบุว่า#include <foo.h>แทนที่ด้วยข้อความของfoo.hไฟล์

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

และไฟล์ส่วนหัวทุกไฟล์ควรมี include guard เพื่อป้องกันไม่ให้รวมหลายครั้ง


4

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


4

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


1

ไฟล์ต้นฉบับของคุณจะมีคำสั่ง include หากคุณใส่ไว้ในส่วนหัว อย่างไรก็ตามในบางกรณีควรใส่ไว้ในไฟล์ต้นฉบับจะดีกว่า

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


1

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

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

ซึ่งจะป้องกันไม่ให้รวมส่วนหัวหลายครั้งส่งผลให้คอมไพเลอร์มีข้อผิดพลาด

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