क्या हेडर पर भरोसा करना अच्छा है, जो सकारत्मक रूप से शामिल किया जा रहा है?


37

मैं एक C ++ प्रोजेक्ट में शामिल सफाई कर रहा हूं, जिस पर मैं काम कर रहा हूं, और मैं सोचता रहता हूं कि क्या मुझे किसी विशेष फ़ाइल में सीधे उपयोग किए गए सभी हेडर को स्पष्ट रूप से शामिल करना चाहिए या नहीं, या क्या मुझे केवल नंगे न्यूनतम शामिल करना चाहिए।

यहाँ एक उदाहरण है Entity.hpp:

#include "RenderObject.hpp"
#include "Texture.hpp"

struct Entity {
    Texture texture;
    RenderObject render();
}

(मान लेते हैं कि आगे की घोषणा RenderObjectएक विकल्प नहीं है।)

अब, मुझे पता है कि RenderObject.hppइसमें शामिल हैं Texture.hpp- मुझे पता है कि क्योंकि प्रत्येक RenderObjectमें एक Textureसदस्य है। फिर भी, मैं स्पष्ट रूप से शामिल Texture.hppमें Entity.hpp, क्योंकि मुझे यकीन है कि अगर यह एक अच्छा विचार है पर यह में शामिल किए जाने भरोसा करने के लिए नहीं कर रहा हूँ RenderObject.hpp

तो: यह अच्छा अभ्यास है या नहीं?


19
आपके उदाहरण में शामिल गार्ड कहां हैं? आप उन्हें भूल से भूल गए, मुझे उम्मीद है?
डॉक्टर ब्राउन

3
एक समस्या जो तब होती है जब आप सभी उपयोग की गई फ़ाइलों को शामिल नहीं करते हैं, कभी-कभी आपके द्वारा फ़ाइलों को शामिल करने वाले आदेश महत्वपूर्ण हो जाते हैं। यह वास्तव में सिर्फ एक मामले में कष्टप्रद है जहां ऐसा होता है, लेकिन कभी-कभी स्नोबॉल और आप वास्तव में उस व्यक्ति को बधाई देते हैं जिसने कोड लिखा था जैसे कि फायरिंग दस्ते के सामने मार्च किया जाएगा।
डंक

यही कारण है कि वहाँ हैं #ifndef _RENDER_H #define _RENDER_H ... #endif
संपतश्री

@ मुझे लगता है कि आपने समस्या को गलत समझा। उनके किसी भी सुझाव के साथ ऐसा नहीं होना चाहिए।
मूविंग डक

1
@DocBrown, #pragma onceइसे सुलझाता है, नहीं?
पचेरियर

जवाबों:


65

आपको हमेशा सभी हेडर को शामिल करना चाहिए जो उस फ़ाइल में .cpp फ़ाइल में प्रयुक्त किसी भी ऑब्जेक्ट को परिभाषित करता है, चाहे आप उन फ़ाइलों में क्या जानते हैं, इसके बारे में। आपको यह सुनिश्चित करने के लिए सभी हेडर फ़ाइलों में गार्ड को शामिल करना चाहिए कि हेडर सहित कई बार कोई फर्क नहीं पड़ता।

कारण:

  • यह उन डेवलपर्स को स्पष्ट करता है जो स्रोत को पढ़ते हैं वास्तव में स्रोत फ़ाइल की क्या आवश्यकता है। यहां, फ़ाइल में पहली कुछ पंक्तियों को देखने वाला व्यक्ति यह देख सकता है कि आप Textureइस फ़ाइल में ऑब्जेक्ट्स के साथ काम कर रहे हैं ।
  • यह उन मुद्दों से बचता है जहां रिफलेक्ट किए गए हेडर संकलन मुद्दों का कारण बनते हैं जब उन्हें अब विशेष हेडर की आवश्यकता नहीं होती है। उदाहरण के लिए, मान लें कि आपको RenderObject.hppवास्तव में Texture.hppस्वयं की आवश्यकता नहीं है।

एक कोरोलरी यह है कि आपको कभी भी किसी हेडर को किसी अन्य हेडर में शामिल नहीं करना चाहिए जब तक कि उस फाइल में स्पष्ट रूप से इसकी आवश्यकता न हो।


10
कोरोलरी के साथ सहमत - अनंतिम के साथ इसे अन्य शीर्षकों को शामिल करना चाहिए अगर इसे इसकी आवश्यकता है!
एंड्रयू

1
मैं सभी व्यक्तिगत वर्गों के लिए सीधे हेडर सहित अभ्यास को नापसंद करता हूं। मैं संचयी हेडर के पक्ष में हूं। यही है, मुझे लगता है कि उच्च स्तर की फाइल को किसी तरह के "मॉड्यूल" का उपयोग करना चाहिए, जो कि इसका उपयोग कर रहा है, लेकिन सीधे सभी व्यक्तिगत भागों को शामिल करने की आवश्यकता नहीं है।
edA-qa मोर्ट-ओरा-वाई

8
यह बड़े, अखंड हेडर की ओर जाता है जिसमें हर फ़ाइल शामिल होती है, तब भी जब उन्हें केवल बिट्स की आवश्यकता होती है जो इसमें है, जो लंबे संकलन समय की ओर जाता है और रिफैक्टिंग को और अधिक कठिन बना देता है।
रोबोट

6
Google ने इस सलाह को ठीक-ठीक लागू करने में मदद करने के लिए एक उपकरण बनाया है, जिसे शामिल करें-क्या-आप उपयोग करते हैं
मैथ्यू जी।

3
बड़े मोनोलिथिक हेडर के साथ प्राथमिक संकलन समय मुद्दा हेडर कोड को स्वयं संकलित करने का समय नहीं है, लेकिन हेडर में हर बार आपके आवेदन में हर सीपीपी फ़ाइल को संकलित करने का समय है। Precompiled हेडर मदद नहीं करते हैं।
रोबोट

23

अंगूठे का सामान्य नियम है: इसमें शामिल करें जो आप उपयोग करते हैं। यदि आप सीधे किसी ऑब्जेक्ट का उपयोग करते हैं, तो उसकी हेडर फ़ाइल को सीधे शामिल करें। यदि आप B का उपयोग करने वाले ऑब्जेक्ट A का उपयोग करते हैं लेकिन B का उपयोग स्वयं नहीं करते हैं, तो केवल आह को शामिल करें

इसके अलावा जब हम विषय पर होते हैं, तो आपको अपनी हेडर फ़ाइल में केवल अन्य हेडर फ़ाइलों को शामिल करना चाहिए, यदि आपको वास्तव में हेडर में इसकी आवश्यकता है। यदि आपको केवल .cpp में इसकी आवश्यकता है, तो केवल इसे वहां शामिल करें: यह एक सार्वजनिक और निजी निर्भरता के बीच का अंतर है, और आपकी कक्षा के उपयोगकर्ताओं को हेडर में खींचने से रोक देगा, जिनकी उन्हें वास्तव में आवश्यकता नहीं है।


10

मैं सोचता रहता हूं कि मुझे किसी विशेष फाइल में सीधे इस्तेमाल किए जाने वाले सभी हेडर को स्पष्ट रूप से शामिल करना चाहिए या नहीं

हाँ।

आपको कभी नहीं पता होगा कि उन अन्य हेडर कब बदल सकते हैं। यह दुनिया में सभी अनुवाद इकाई में शामिल करने के लिए सभी समझ में आता है, हेडर आप जानते हैं कि अनुवाद इकाई की जरूरत है।

हमारे पास यह सुनिश्चित करने के लिए हेडर गार्ड हैं कि दोहरे समावेश हानिकारक नहीं है।


3

इस पर राय अलग-अलग है, लेकिन मेरा विचार है कि प्रत्येक फ़ाइल (चाहे c / cpp स्रोत फ़ाइल हो, या h / hpp हैडर फ़ाइल) को संकलित या विश्लेषण करने में सक्षम होना चाहिए।

जैसे, सभी फ़ाइलों को किसी भी और सभी हेडर फ़ाइलों को #include करना चाहिए जिनकी उन्हें आवश्यकता है - आपको यह नहीं मानना ​​चाहिए कि एक हेडर फ़ाइल को पहले ही शामिल किया जा चुका है।

यह एक वास्तविक दर्द है अगर आपको एक हेडर फ़ाइल जोड़ने की आवश्यकता है और यह पता लगाएं कि यह एक ऐसी वस्तु का उपयोग करता है जो कहीं और परिभाषित है, बिना सीधे इसमें शामिल है ... इसलिए आपको ढूंढना होगा (और संभवतः गलत एक के साथ समाप्त हो सकता है!)

दूसरी तरफ, यह (एक सामान्य नियम के रूप में) मायने नहीं रखता है यदि आप एक ऐसी फ़ाइल को बनाते हैं जिसकी आपको आवश्यकता नहीं है ...


व्यक्तिगत शैली के एक बिंदु के रूप में, मैं वर्णमाला के क्रम में #include फ़ाइलों को व्यवस्थित करता हूं, सिस्टम और एप्लिकेशन में विभाजित करता हूं - यह "स्वयं निहित और पूरी तरह से सुसंगत" संदेश को सुदृढ़ करने में मदद करता है।


शामिल के आदेश के बारे में ध्यान दें: कभी-कभी यह आदेश महत्वपूर्ण होता है, उदाहरण के लिए जब X11 हेडर शामिल हैं। यह डिजाइन के कारण हो सकता है (जो उस मामले में खराब डिजाइन माना जा सकता है), कभी-कभी यह दुर्भाग्यपूर्ण असंगति के मुद्दों के कारण होता है।
हाइड

अनावश्यक हेडर सहित के बारे में एक नोट, यह सीधे संकलन के लिए मायने रखता है, पहले सीधे (विशेषकर यदि यह टेम्पलेट-हैवी सी ++ है), लेकिन विशेष रूप से उसी या निर्भरता परियोजना के हेडर सहित जहां फ़ाइल भी बदलती है, और फिर से शुरू हो जाएगी इसमें सब कुछ शामिल है (यदि आपके पास काम करने की निर्भरता है, यदि आप नहीं करते हैं तो आपको हर समय साफ निर्माण करना होगा ...)।
हाइड

2

यह इस बात पर निर्भर करता है कि क्या परिवर्तनशील समावेश आवश्यकता (उदाहरण के आधार वर्ग) या कार्यान्वयन विस्तार (निजी सदस्य) के कारण है।

स्पष्ट करने के लिए, संवादात्मक समावेशन आवश्यक है जब इसे हटाकर केवल मध्यवर्ती शीर्षलेख में घोषित इंटरफेस को बदलने के बाद ही किया जा सकता है। चूंकि यह पहले से ही एक ब्रेकिंग परिवर्तन है, इसलिए इसका उपयोग करने वाली किसी भी .cpp फ़ाइल को वैसे भी जांचना होगा।

उदाहरण: आह को Bh द्वारा शामिल किया गया है जो C.cpp द्वारा उपयोग किया जाता है। यदि B ने कुछ कार्यान्वयन विवरण के लिए आह का उपयोग किया है, तो C.cpp को यह नहीं मानना ​​चाहिए कि Bh ऐसा करना जारी रखेगा। लेकिन अगर Bh आधार वर्ग के लिए आह का उपयोग करता है, तो C.cpp मान सकता है कि B अपने आधार वर्गों के लिए प्रासंगिक शीर्षलेखों को शामिल करना जारी रखेगा।

आप यहाँ हेडर इंक्लूज़न की नकल नहीं करने का वास्तविक लाभ देखते हैं। कहो कि आधार वर्ग का उपयोग भ द्वारा वास्तव में आह में नहीं किया गया था और इसे भा में ही बदल दिया गया है। भ अब एक स्टैंडअलोन हेडर है। यदि सी। सी। पी। निरूपित रूप से आह शामिल है, तो इसमें अब एक अनावश्यक हेडर शामिल है।


2

एक और मामला हो सकता है: आपके पास आह, भ और आपका सी। सी। पी। है, भ में आह भी शामिल है

तो सी। सी। पी। में, आप लिख सकते हैं

#include "B.h"
#include "A.h" // < this can be optional as B.h already has all the stuff in A.h

इसलिए यदि आप यहाँ "आह" नहीं लिखते हैं, तो क्या हो सकता है? आपके C.cpp में, A और B (जैसे वर्ग) दोनों का उपयोग किया जाता है। बाद में आपने अपना सी। कोड कोड बदल दिया, बी संबंधित सामान को हटा दिया, लेकिन भ को वहीं छोड़ दिया।

यदि आप इस बिंदु पर आह और भ और दोनों को शामिल करते हैं, तो अनावश्यक का पता लगाने वाले उपकरण आपको यह इंगित करने में मदद कर सकते हैं कि भ को शामिल करने की अब आवश्यकता नहीं है। यदि आप केवल ऊपर के रूप में बी को शामिल करते हैं, तो आपके कोड परिवर्तन के बाद अनावश्यक शामिल का पता लगाना उपकरण / मानव के लिए कठिन है।


1

मैं प्रस्तावित जवाबों से थोड़ा अलग दृष्टिकोण ले रहा हूं।

हेडर में, हमेशा सिर्फ एक नंगे न्यूनतम शामिल करें, बस संकलन पास बनाने के लिए क्या आवश्यक है। जहां भी संभव हो आगे की घोषणा का उपयोग करें।

स्रोत फ़ाइलों में, यह महत्वपूर्ण नहीं है कि आप कितना शामिल करते हैं। मेरी प्राथमिकताएँ अभी भी इसे शामिल करने के लिए न्यूनतम शामिल हैं।

छोटी परियोजनाओं के लिए, जिनमें हेडर शामिल हैं और यहां कोई फर्क नहीं पड़ेगा। लेकिन मध्यम से बड़ी परियोजनाओं के लिए, यह एक समस्या बन सकती है। यहां तक ​​कि अगर नवीनतम हार्डवेयर का उपयोग संकलन करने के लिए किया जाता है, तो अंतर ध्यान देने योग्य हो सकता है। कारण यह है कि कंपाइलर में अभी भी शामिल हेडर को खोलना है और इसे पार्स करना है। इसलिए, बिल्ड का अनुकूलन करने के लिए, उपरोक्त तकनीक लागू करें (नंगे न्यूनतम शामिल करें, और आगे की घोषणा का उपयोग करें)।

हालांकि थोड़ा पुराना है, बड़े पैमाने पर C ++ सॉफ्टवेयर डिज़ाइन (जॉन लैकोस द्वारा) यह सब विवरण में बताया गया है।


1
इस रणनीति से असहमत ... यदि आप एक स्रोत फ़ाइल में एक हेडर फ़ाइल शामिल करते हैं, तो आपको इसके सभी निर्भरता को ट्रैक करना होगा। सूची में शामिल होने और प्रयास करने की तुलना में सीधे शामिल करना बेहतर है!
एंड्रयू

@ और फिर क्या, और कितनी बार, यह जांचने के लिए उपकरण और स्क्रिप्ट शामिल हैं।
B10овиЈ

1
मैंने इससे निपटने के लिए कुछ नवीनतम संकलकों पर अनुकूलन पर ध्यान दिया है। वे एक विशिष्ट गार्ड स्टेटमेंट को पहचानते हैं, और इसे प्रोसेस करते हैं। फिर, जब # इसे फिर से जोड़ते हैं, तो वे फ़ाइल लोड को पूरी तरह से ऑप्टिमाइज़ कर सकते हैं। हालाँकि, आगे की घोषणाओं की आपकी सिफारिश में शामिलों की संख्या को कम करना बहुत बुद्धिमानी है। एक बार जब आप आगे की घोषणाओं का उपयोग करना शुरू कर देते हैं, तो यह कंपाइलर रन-टाइम (आगे की घोषणाओं में सुधार) और उपयोगकर्ता-मित्रता (सुविधाजनक अतिरिक्त #includes द्वारा सुधार) का एक संतुलन बन जाता है, जो एक संतुलन है जिसे प्रत्येक कंपनी अलग-अलग तरीके से सेट करती है।
Cort Ammon

1
@CortAmmon एक विशिष्ट हेडर में गार्ड्स शामिल हैं, लेकिन कंपाइलर को अभी भी इसे खोलना है, और यह धीमा संचालन है
B operationовиЈ

4
@ B @овиЈ: वास्तव में, वे नहीं करते हैं। उन्हें केवल इतना ही पहचानना है कि फ़ाइल में "विशिष्ट" हेडर गार्ड हैं और उन्हें ध्वजांकित करें ताकि यह केवल एक बार खुल जाए। Gcc, उदाहरण के लिए, कब और कहाँ यह अनुकूलन लागू होता है के बारे में प्रलेखन है: gcc.gnu.org/onbuildocs/cppinternals/Guard-Macros.html
Cort Ammon

-4

अच्छा अभ्यास यह है कि जब तक यह संकलित हो जाता है तब तक अपनी हेडर रणनीति के बारे में चिंता न करें।

आपके कोड का हेडर सेक्शन सिर्फ लाइनों का एक ब्लॉक है जिसे तब तक किसी को भी नहीं देखना चाहिए जब तक कि आप आसानी से हल की गई संकलन त्रुटि प्राप्त न करें। मैं 'सही' शैली की इच्छा को समझता हूं, लेकिन न तो वास्तव में सही तरीके से वर्णित किया जा सकता है। हर वर्ग के लिए हेडर शामिल करना कष्टप्रद आदेश-आधारित संकलन त्रुटियों के कारण होने की अधिक संभावना है, लेकिन वे संकलन त्रुटियां उन समस्याओं को भी दर्शाती हैं जो सावधान कोडिंग को ठीक कर सकती हैं (हालांकि यकीनन वे ठीक करने के लिए समय के लायक नहीं हैं)।

और हाँ, आपके पास भूमि में शामिल होने के बाद उन आदेश-आधारित समस्याएं होंगीfriend

आप दो मामलों में समस्या के बारे में सोच सकते हैं।


केस 1: आपके पास एक दूसरे के साथ बातचीत करने वाली कक्षाओं की एक छोटी संख्या है, एक दर्जन से भी कम। आप नियमित रूप से इन हेडर को जोड़ते हैं, हटाते हैं, और अन्यथा इन तरीकों को संशोधित करते हैं, जो एक-दूसरे पर उनकी निर्भरता को प्रभावित कर सकते हैं। यह वह मामला है जो आपका कोड उदाहरण बताता है।

हेडर का सेट काफी छोटा है कि यह किसी भी समस्या को हल करने के लिए जटिल नहीं है जो फसल होती है। एक या दो हेडर को फिर से लिखने से कोई भी मुश्किल समस्या तय होती है। आपकी हेडर रणनीति के बारे में चिंता करना उन समस्याओं को हल कर रहा है जो मौजूद नहीं हैं।


केस 2: आपके पास दर्जनों कक्षाएं हैं। कुछ कक्षाएं आपके प्रोग्राम की रीढ़ का प्रतिनिधित्व करती हैं, और उनके हेडर को फिर से लिखना आपको अपने कोड बेस की एक बड़ी मात्रा को फिर से लिखने / फिर से लिखने के लिए मजबूर करेगा। अन्य वर्ग चीजों को पूरा करने के लिए इस रीढ़ का उपयोग करते हैं। यह एक विशिष्ट व्यवसाय सेटिंग का प्रतिनिधित्व करता है। हेडर निर्देशिकाओं में फैले हुए हैं और आप हर चीज के नाम को वास्तविक रूप से याद नहीं रख सकते हैं।

समाधान: इस बिंदु पर, आपको अपनी कक्षाओं को तार्किक समूहों में सोचने और उन समूहों को हेडर में इकट्ठा करने की आवश्यकता है जो आपको #includeअधिक से अधिक होने से रोकते हैं । यह न केवल जीवन को सरल बनाता है, बल्कि पूर्वनिर्धारित हेडर का लाभ उठाने के लिए भी यह एक आवश्यक कदम है ।

आप अंत में उन #includeकक्षाओं को समाप्त करते हैं जिनकी आपको आवश्यकता नहीं है लेकिन कौन परवाह करता है ?

इस स्थिति में, आपका कोड ऐसा दिखेगा ...

#include <Graphics.hpp>

struct Entity {
    Texture texture;
    RenderObject render();
}

13
मुझे यह -1 करना था क्योंकि मैं ईमानदारी से किसी भी वाक्य को मानता हूं जो "अच्छा अभ्यास के रूप में है, जब तक कि यह संकलन नहीं करता है तब तक आपको अपनी ____ रणनीति के बारे में चिंता नहीं करनी चाहिए" लोगों को बुरे निर्णय की ओर ले जाता है। मैंने पाया है कि दृष्टिकोण बहुत तेजी से अपठनीयता की ओर जाता है, और अपठनीयता ALMOST जितना बुरा "काम नहीं करता है" है। मुझे कई प्रमुख पुस्तकालय भी मिले हैं जो आपके द्वारा वर्णित दोनों मामलों के परिणामों से असहमत हैं। एक उदाहरण के रूप में, बूस्ट "संग्रह" हेडर करते हैं जो आप मामले 2 में सुझाते हैं, लेकिन वे वर्ग-दर-वर्ग हेडर प्रदान करने के लिए एक बड़ा सौदा करते हैं जब आपको उनकी आवश्यकता होती है।
Cort Ammon

3
मैंने व्यक्तिगत रूप से देखा है "चिंता मत करो अगर यह संकलित करता है" तो हमारा आवेदन "संकलन में 30 मिनट लगते हैं जब आप एक एनम के लिए एक मूल्य जोड़ते हैं, तो हम इसे कैसे ठीक करते हैं !?"
रोबोट

मैंने अपने उत्तर में संकलन समय के मुद्दे को संबोधित किया। वास्तव में, मेरा जवाब केवल दो में से एक है (जिनमें से कोई भी अच्छा स्कोर नहीं किया)। लेकिन वास्तव में, यह ओपी के प्रश्न के लिए स्पर्शरेखा है; यह एक "क्या मुझे ऊंट-केस मेरे चर नामों को चाहिए?" प्रश्न टाइप करें। मुझे लगता है कि मेरा जवाब अलोकप्रिय है, लेकिन हर चीज के लिए हमेशा सबसे अच्छा अभ्यास नहीं होता है, और यह उन मामलों में से एक है।
प्रश्नोत्तर

# 2 से सहमत हैं। पहले के विचारों के अनुसार - मुझे आशा है कि स्वचालन के लिए जो स्थानीय हेडर ब्लॉक को अपडेट करेगा - तब तक मैं पूरी सूची की वकालत करता हूं।
chux -

"सब कुछ और रसोई सिंक शामिल करें" दृष्टिकोण आपको शुरू में कुछ समय बचा सकता है - आपकी हेडर फाइलें और भी छोटी लग सकती हैं (क्योंकि ज्यादातर चीजें अप्रत्यक्ष रूप से .... कहीं से भी शामिल हैं)। जब तक आप उस बिंदु पर नहीं आते हैं जहां कहीं भी कोई भी परिवर्तन आपके प्रोजेक्ट के 30+ मिनट के पुन: संयोजन का कारण बनता है। और आपका आईडीई-स्मार्ट स्वतः पूर्ण सैकड़ों अप्रासंगिक सुझावों को लाता है। और आप गलती से दो समान रूप से नामित वर्गों या स्थिर कार्यों को मिलाते हैं। और आप एक नई संरचना जोड़ते हैं, लेकिन तब बिल्ड विफल हो जाता है क्योंकि आपके पास कहीं पूरी तरह से असंबंधित वर्ग के साथ एक नेमस्पेस टक्कर है ...
CharonX
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.