เหตุใดเราต้องรวม. h ขณะที่ทุกอย่างทำงานเมื่อรวมเฉพาะไฟล์. cpp


18

ทำไมเราต้องรวมทั้งไฟล์.hและ.cppในขณะที่เราสามารถทำให้มันทำงานได้โดยรวม.cppไฟล์

ตัวอย่างเช่นการสร้างfile.hที่มีการประกาศแล้วการสร้างคำนิยามที่มีและรวมทั้งในfile.cppmain.cpp

อีกทางหนึ่ง: การสร้างการfile.cppประกาศ / คำจำกัดความที่มี (ไม่มีต้นแบบ) รวมถึงสิ่งmain.cppต่อไปนี้

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


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

ฉันขอแนะนำให้ดูคำถามที่เกี่ยวข้องที่นี่ทางด้านข้าง programmers.stackexchange.com/questions/56215/…และprogrammers.stackexchange.com/questions/115368/…เป็นเครื่องอ่านที่ดี

ทำไมจึงเป็นเช่นนี้กับโปรแกรมเมอร์และไม่ใช่ SO
การแข่งขัน Lightness กับโมนิก้า

@LightnessRacesInOrbit เนื่องจากเป็นคำถามของรูปแบบการเข้ารหัสไม่ใช่คำถาม "วิธีการ"
CashCow

@CashCow: ดูเหมือนว่าคุณเข้าใจผิดดังนั้นซึ่งไม่ใช่ไซต์ "วิธีการ" นอกจากนี้ยังดูเหมือนว่าคุณเข้าใจผิดคำถามนี้ซึ่งไม่ใช่คำถามของรูปแบบการเข้ารหัส
การแข่งขัน Lightness กับโมนิก้า

คำตอบ:


29

ในขณะที่คุณสามารถรวม.cppไฟล์ได้ตามที่คุณกล่าวถึงนี่เป็นความคิดที่ไม่ดี

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

สิ่งที่ควรเกิดขึ้นคือแต่ละ.cppไฟล์มีคำจำกัดความสำหรับชุดย่อยของโปรแกรมเช่นคลาสกลุ่มฟังก์ชันที่มีการจัดระบบแบบโลจิคัลตัวแปรสแตติกโกลบอล (ใช้เท่าที่จำเป็น)

แต่ละหน่วยการรวบรวม ( .cppไฟล์) จากนั้นรวมถึงการประกาศใด ๆ ที่จำเป็นต้องรวบรวมคำจำกัดความที่มีอยู่ มันติดตามฟังก์ชั่นและการเรียนมันอ้างอิง แต่ไม่ได้มีดังนั้น linker สามารถแก้ไขได้ในภายหลังเมื่อมันรวมรหัสวัตถุลงในปฏิบัติการหรือห้องสมุด

ตัวอย่าง

  • Foo.h -> มีการประกาศ (อินเทอร์เฟซ) สำหรับคลาส Foo
  • Foo.cpp -> มีคำจำกัดความ (การนำไปใช้) สำหรับคลาส Foo
  • Main.cpp-> มีวิธีการหลักจุดเข้าใช้โปรแกรม รหัสนี้จะยกตัวอย่าง Foo และใช้มัน

ทั้งสองFoo.cppและจำเป็นจะต้องรวมMain.cpp จำเป็นต้องใช้เพราะมีการกำหนดรหัสที่ด้านหลังอินเทอร์เฟซของคลาสดังนั้นจึงจำเป็นต้องทราบว่าอินเทอร์เฟซนั้นคืออะไร ต้องการมันเพราะมันกำลังสร้าง Foo และเรียกใช้พฤติกรรมดังนั้นจึงต้องรู้ว่าพฤติกรรมนั้นคืออะไรขนาดของ Foo ในหน่วยความจำและวิธีการค้นหาฟังก์ชั่น ฯลฯ แต่ยังไม่จำเป็นต้องมีการใช้งานจริงFoo.hFoo.cppMain.cpp

คอมไพเลอร์จะสร้างFoo.oจากFoo.cppที่มีรหัสคลาส Foo ทั้งหมดในรูปแบบที่รวบรวม นอกจากนี้ยังสร้างMain.oซึ่งรวมถึงวิธีการหลักและการอ้างอิงที่ยังไม่ได้แก้ไขกับคลาสฟู

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

หากคุณรวมFoo.cppไฟล์ไว้ในMain.cppนั้นจะมีคำจำกัดความของคลาสฟูสองคำจำกัดความ ตัวเชื่อมโยงจะเห็นสิ่งนี้และพูดว่า "ฉันไม่รู้ว่าจะเลือกอันไหนดังนั้นนี่เป็นข้อผิดพลาด" ขั้นตอนการรวบรวมจะสำเร็จ แต่การเชื่อมโยงจะไม่ (เว้นแต่คุณจะไม่ได้รวบรวมFoo.cppแต่ทำไมมันเป็น.cppไฟล์แยกต่างหาก)

สุดท้ายแนวคิดของไฟล์ประเภทต่าง ๆ นั้นไม่เกี่ยวข้องกับคอมไพเลอร์ C / C ++ มันรวบรวม "ไฟล์ข้อความ" ซึ่งหวังว่าจะมีรหัสที่ถูกต้องสำหรับภาษาที่ต้องการ บางครั้งมันอาจสามารถบอกภาษาตามนามสกุลไฟล์ ตัวอย่างเช่นคอมไพล์.cไฟล์ที่ไม่มีตัวเลือกคอมไพเลอร์และมันจะถือว่า C ในขณะที่ส่วนขยาย.ccหรือ.cppส่วนขยายจะบอกให้ถือว่า C ++ อย่างไรก็ตามฉันสามารถบอกคอมไพเลอร์ให้รวบรวมไฟล์ได้อย่างง่ายดาย.hหรือแม้แต่.docxเป็น C ++ และมันจะปล่อย.oไฟล์object ( ) ถ้ามันมีรหัส C ++ ที่ถูกต้องในรูปแบบข้อความธรรมดา ส่วนขยายเหล่านี้เพิ่มเติมเพื่อประโยชน์ของโปรแกรมเมอร์ ถ้าฉันเห็นFoo.hและFoo.cppฉันทันทีถือว่าแรกที่มีการประกาศของชั้นเรียนและที่สองมีคำจำกัดความ


ฉันไม่เข้าใจอาร์กิวเมนต์ที่คุณทำที่นี่ หากคุณเพียงแค่รวมFoo.cppไว้ในMain.cppคุณไม่จำเป็นต้องรวม.hไฟล์คุณมีไฟล์น้อยกว่าหนึ่งไฟล์คุณยังคงชนะที่แยกรหัสเป็นไฟล์แยกต่างหากเพื่อให้สามารถอ่านได้และคำสั่งการคอมไพล์ของคุณนั้นง่ายกว่า ในขณะที่ฉันเข้าใจถึงความสำคัญของไฟล์ส่วนหัวฉันไม่คิดว่ามันจะเป็นเช่นนั้นหรอก
Benjamin Gruenbaum

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

2
If you had included the Foo.cpp file in Main.cpp, there would be two definitions of class Foo.ประโยคที่สำคัญที่สุดเกี่ยวกับคำถาม
marczellm

@Snowman Right ซึ่งเป็นสิ่งที่ฉันคิดว่าคุณควรมุ่งเน้น - หน่วยรวบรวมหลาย การผสานโค้ดที่มี / ไม่มีไฟล์ส่วนหัวเพื่อแยกรหัสจนถึงชิ้นเล็ก ๆ นั้นมีประโยชน์และตั้งฉากกับวัตถุประสงค์ของไฟล์ส่วนหัว หากสิ่งที่คุณต้องการทำคือแยกมันเป็นไฟล์ส่วนหัวของไฟล์ไม่ต้องซื้ออะไรเลยเมื่อเราต้องการการเชื่อมโยงแบบไดนามิกการเชื่อมโยงแบบมีเงื่อนไขและการสร้างล่วงหน้ามากขึ้นพวกเขาก็มีประโยชน์อย่างมากในทันที
Benjamin Gruenbaum

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

9

อ่านเพิ่มเติมเกี่ยวกับบทบาทของตัวประมวลผลล่วงหน้า C และ C ++ซึ่งเป็นแนวคิด "เฟส" แรกของคอมไพเลอร์ C หรือ C ++ (ในอดีตมันเป็นโปรแกรมแยกต่างหาก/lib/cppตอนนี้ด้วยเหตุผลด้านประสิทธิภาพที่รวมเข้ากับคอมไพเลอร์ที่เหมาะสมcc1 หรือcc1plus) อ่านเอกสารของ GNU cpppreprocessor เป็นพิเศษ ดังนั้นในทางปฏิบัติคอมไพเลอร์แนวคิดแรกประมวลผลหน่วยการรวบรวมของคุณ (หรือหน่วยการแปล ) แล้วทำงานในแบบฟอร์มการประมวลผลล่วงหน้า

คุณอาจจะต้องเสมอรวมถึงไฟล์ส่วนหัวfile.hถ้ามี (ตามที่กำหนดโดยการประชุมและนิสัย ):

  • นิยามแมโคร
  • ประเภทคำจำกัดความ (เช่นtypedef, struct, classฯลฯ ... )
  • คำจำกัดความของstatic inlineฟังก์ชัน
  • ประกาศของฟังก์ชั่นภายนอก

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

แน่นอนว่าการติดตั้งของคุณfile.cppจำเป็นต้องมีทั้งหมดข้างต้นดังนั้นต้องการใน#include "file.h" ตอนแรก

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

ประเด็นก็คือตัวประมวลผลล่วงหน้ากำลังทำการดำเนินการที่เป็นข้อความเท่านั้น คุณสามารถ (โดยหลักการ) หลีกเลี่ยงได้โดยการคัดลอกและวางหรือแทนที่ด้วย "preprocessor" หรือเครื่องกำเนิดรหัส C อื่น (เช่นgppหรือm4 )

ปัญหาเพิ่มเติมคือมาตรฐาน C (หรือ C ++) ล่าสุดกำหนดส่วนหัวมาตรฐานหลายรายการ การใช้งานส่วนใหญ่ใช้ส่วนหัวมาตรฐานเหล่านี้จริงๆเป็นไฟล์ (เฉพาะการใช้งาน) แต่ฉันเชื่อว่ามันจะเป็นไปได้สำหรับการใช้งานที่สอดคล้องกับการนำมาตรฐานมาใช้ (รวมถึง#include <stdio.h>C หรือ#include <vector>C ++) ด้วยเทคนิคมายากลบางอย่าง (เช่นการใช้ฐานข้อมูล ข้อมูลภายในคอมไพเลอร์)

หากใช้คอมไพเลอร์GCC (เช่นgccหรือg++) คุณสามารถใช้-Hแฟ-C -Eล็กเพื่อรับข้อมูลเกี่ยวกับการรวมทุกครั้งและแฟล็กเพื่อขอรับแบบฟอร์มที่ประมวลผลล่วงหน้า แน่นอนว่ามีแฟล็กคอมไพเลอร์อื่น ๆ อีกหลายตัวที่มีผลต่อการประมวลผลล่วงหน้า (เช่น-I /some/dir/การเพิ่ม/some/dir/สำหรับการค้นหาไฟล์ที่รวมอยู่และ-Dเพื่อกำหนดมาโครตัวประมวลผลล่วงหน้าบางอย่าง ฯลฯ ฯลฯ )

NB รุ่นอนาคตของ C ++ (อาจC ++ 20บางทีก็ในภายหลัง) อาจมีc ++ โมดูล


1
หากการเชื่อมโยงเน่านี้จะยังคงเป็นคำตอบที่ดี?
Dan Pichelman

4
ฉันแก้ไขคำตอบของฉันเพื่อปรับปรุงและฉันเลือกลิงค์ - เพื่อวิกิพีเดียหรือไปยังเอกสาร GNU - ซึ่งฉันเชื่อว่าจะยังคงเกี่ยวข้องเป็นเวลานาน
Basile Starynkevitch

3

เนื่องจากโมเดลการสร้างหลายหน่วยของ C ++ คุณต้องมีวิธีที่จะมีรหัสที่ปรากฏในโปรแกรมของคุณเพียงครั้งเดียว (คำจำกัดความ) และคุณต้องการวิธีที่จะมีรหัสที่ปรากฏในแต่ละหน่วยการแปลของโปรแกรมของคุณ (การประกาศ)

จากนี้จะเกิดสำนวนส่วนหัว C ++ มันเป็นแบบแผนด้วยเหตุผล

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


0

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

ดูเหมือนว่าเหตุผลสำหรับไฟล์ส่วนหัวมีแนวโน้มที่จะหลงทางในการสอนและพูดคุยเกี่ยวกับ C / C ++

ไฟล์ส่วนหัวจัดเตรียมโซลูชันสำหรับสองปัญหาในการพัฒนาแอปพลิเคชัน:

  1. การแยกส่วนต่อประสานและการใช้งาน
  2. ปรับปรุงเวลารวบรวม / ลิงค์สำหรับโปรแกรมขนาดใหญ่

C / C ++ สามารถปรับขนาดจากโปรแกรมขนาดเล็กไปจนถึงหลายล้านบรรทัดที่มีขนาดใหญ่มากและหลายพันโปรแกรมไฟล์ การพัฒนาแอพพลิเคชั่นสามารถปรับขนาดได้จากทีมนักพัฒนาหนึ่งรายไปจนถึงนักพัฒนาหลายร้อย

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

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

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

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

นอกจากนี้ไฟล์ส่วนหัวยังช่วยให้ผู้พัฒนาสามารถจัดทำไลบรารี่ (รวบรวมแล้ว) และไฟล์ส่วนหัว ผู้ใช้ไลบรารีรายอื่นอาจไม่เห็นการใช้งานที่เหมาะสม แต่ยังสามารถใช้ไลบรารีกับไฟล์ส่วนหัวได้ คุณทำสิ่งนี้ทุกวันด้วยไลบรารีมาตรฐาน C / C ++

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


0

คุณอาจถามว่าทำไมไม่เพียงแค่ใส่รหัสทั้งหมดลงในไฟล์เดียว

คำตอบที่ง่ายที่สุดคือการบำรุงรักษาโค้ด

มีบางครั้งที่การสร้างคลาสมีเหตุผล:

  • ทั้งหมดอยู่ในหัว
  • ทั้งหมดอยู่ในหน่วยรวบรวมและไม่มีส่วนหัวเลย

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

(เทมเพลตที่ต้องมีการ inline ทั้งหมดเป็นปัญหาที่แตกต่างกันเล็กน้อย)

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

เวลาที่คุณอาจรวมคลาสทั้งหมดไว้ในหน่วยการคอมไพล์และไม่แสดงส่วนหัวของมันเลยคือ:

  • คลาส "impl" ที่ใช้โดยคลาสที่ใช้เท่านั้น มันเป็นรายละเอียดการใช้งานของชั้นเรียนนั้นและไม่ได้ใช้จากภายนอก

  • การใช้คลาสฐานนามธรรมที่สร้างขึ้นโดยวิธีการบางอย่างจากโรงงานที่ส่งกลับตัวชี้ / การอ้างอิง / ตัวชี้สมาร์ท) ไปยังคลาสฐาน วิธีการจากโรงงานจะถูกเปิดเผยโดยคลาสเอง (นอกจากนี้หากคลาสมีอินสแตนซ์ที่ลงทะเบียนตัวเองบนตารางผ่านอินสแตนซ์คงที่ก็ไม่จำเป็นต้องเปิดเผยผ่านโรงงาน)

  • คลาสประเภท "functor"

ในคำอื่น ๆ ที่คุณไม่ต้องการให้ใครรวมส่วนหัว

ฉันรู้ว่าคุณกำลังคิดอะไรอยู่ ... หากเป็นเพียงการบำรุงรักษาโดยการรวมไฟล์ cpp (หรือส่วนหัวที่ inline ทั้งหมด) คุณสามารถแก้ไขไฟล์เพื่อ "ค้นหา" รหัสและสร้างใหม่ได้อย่างง่ายดาย

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

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


-1

ตัวอย่างของ Snowman ต้องการส่วนขยายเล็กน้อยเพื่อแสดงให้คุณเห็นว่าทำไมไฟล์. h จึงจำเป็น

เพิ่มบาร์คลาสอื่นเข้าไปในการเล่นซึ่งขึ้นอยู่กับคลาสของ Foo ด้วย

Foo.h -> มีการประกาศสำหรับคลาส Foo

Foo.cpp -> มีคำจำกัดความ (การบังคับใช้) ของคลาส Foo

Main.cpp -> ใช้ตัวแปรประเภท Foo

Bar.cpp -> ใช้ตัวแปรประเภท Foo ด้วย

ตอนนี้ไฟล์ cpp ทั้งหมดจำเป็นต้องมี Foo.h มันจะเป็นข้อผิดพลาดในการรวม Foo.cpp ในไฟล์ cpp อื่นมากกว่าหนึ่งไฟล์ ตัวเชื่อมโยงจะล้มเหลวเนื่องจากคลาส Foo จะถูกกำหนดมากกว่าหนึ่งครั้ง


1
นั่นไม่ใช่อย่างใดอย่างหนึ่ง นั่นอาจแก้ไขได้ด้วยวิธีเดียวกันกับที่แก้ไขใน.hไฟล์ต่อไป - พร้อมตัวป้องกันและเป็นปัญหาเดียวกับที่คุณมีกับ.hไฟล์ นี่ไม่ใช่.hไฟล์ใด ๆ เลย
Benjamin Gruenbaum

ฉันไม่แน่ใจว่าคุณจะใช้ยามรวมได้อย่างไรเพราะทำงานได้ต่อไฟล์เท่านั้น (เช่นเดียวกับการประมวลผลล่วงหน้า) ในกรณีนี้เนื่องจาก Main.cpp และ Bar.cpp เป็นไฟล์ที่แตกต่างกัน Foo.cpp จะรวมอยู่เพียงครั้งเดียวในทั้งคู่ (ป้องกันหรือไม่ได้สร้างความแตกต่างใด ๆ ) Main.cpp และ Bar.cpp ทั้งสองจะรวบรวม แต่ข้อผิดพลาดการเชื่อมโยงยังคงเกิดขึ้นเนื่องจากคำจำกัดความที่สองของคลาส Foo อย่างไรก็ตามยังมีมรดกที่ฉันไม่ได้พูดถึง การประกาศสำหรับโมดูลภายนอก (dll's) เป็นต้น
SkaarjFace

ไม่มันจะเป็นหนึ่งในหน่วยการคอมไพล์โดยไม่มีการลิงก์ใด ๆ เกิดขึ้นเนื่องจากคุณรวม.cppไฟล์ไว้ด้วย
Benjamin Gruenbaum

ฉันไม่ได้บอกว่ามันจะไม่รวบรวม แต่มันจะไม่เชื่อมโยงทั้ง main.o และ bar.o ในปฏิบัติการเดียวกัน โดยไม่ต้องเชื่อมโยงสิ่งที่เป็นจุดรวบรวมเลย
SkaarjFace

อ๊ะ ... รับรหัสให้ทำงานเหรอ? คุณยังคงสามารถลิงก์ได้ แต่ไม่เชื่อมโยงไฟล์เข้าด้วยกัน แต่จะเป็นหน่วยการรวบรวมเดียวกัน
Benjamin Gruenbaum

-2

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

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