2010-06-23 5 views
6

मैं अब कुछ हफ्तों के लिए सी ++ के साथ काम कर रहा हूं, लेकिन हेडर फाइलों (या लिंकर मुझे लगता है?) के पीछे तंत्र ने मुझे बिल्ली से बाहर कर दिया है। मुझे अपनी अन्य शीर्षलेख फ़ाइलों को समूहित करने के लिए "main.h" बनाने की आदत है और main.cpp को साफ रखें, लेकिन कभी-कभी उन हेडर फाइलें एक अलग शीर्षलेख फ़ाइल खोजने में सक्षम नहीं होने के बारे में शिकायत करती हैं (भले ही इसे घोषित किया गया हो "main.h" में)। शायद मैं इसे बहुत अच्छी तरह से समझा नहीं कर रहा हूँ इसलिए यहाँ मैं क्या कर रहा हूँ के एक संक्षिप्त संस्करण है:क्या कोई यह स्पष्ट करने में सहायता कर सकता है कि शीर्षलेख फ़ाइलें कैसे काम करती हैं?

//main.cpp 

#include "main.h" 
int main() { 
    return 0; 
} 

-

//main.h 

#include "player.h" 
#include "health.h" 
#include "custvector.h" 

-

//player.h 

#include "main.h" 
class Player { 
    private: 
     Vector playerPos; 
    public: 
     Health playerHealth; 
}; 

-

//custvector.h 

struct Vector { 
    int X; 
    int Y; 
    int Z; 
}; 

-

//health.h 
class Health { 
    private: 
     int curHealth; 
     int maxHealth; 
    public: 
     int getHealth() const; 
     void setHealth(int inH); 
     void modHealth(int inHM); 
}; 

मैं health.cpp शामिल नहीं करूंगा क्योंकि यह थोड़ा लंबा है (लेकिन काम करता है), इसमें #include "health.h" है।

वैसे भी, कंपाइलर (कोड :: ब्लॉक) शिकायत करता है कि "player.h" प्रकार 'स्वास्थ्य' या 'वेक्टर' नहीं ढूंढ सकता है। मैंने सोचा कि अगर मैंने #include "main.h" को "player.h" में इस्तेमाल किया है तो यह Health और Vector के लिए परिभाषाओं को समझने में सक्षम होगा, वे "main.h" में शामिल हैं। मैंने सोचा कि वे चाहते हैं कि वे अपने तरीके से सुरंग की तरह हों (player.h -> main.h -> health.h)। लेकिन यह बहुत अच्छा काम नहीं किया। क्या कोई ऐसा चित्र या वीडियो है जो स्पष्ट कर सकता है कि इसे कैसे स्थापित किया जाना चाहिए? Google बहुत मदद नहीं कर रहा था (न ही मेरी पुस्तक)।

+1

यह आपके प्रश्न का उत्तर नहीं देता है, लेकिन आपको वेक्टर संरचना को एक अलग नाम में बदलना चाहिए। जब आप std :: vector का उपयोग शुरू करते हैं तो यह भ्रमित हो जाएगा, और 3 डी स्पेस में एक बिंदु वास्तव में एक वेक्टर नहीं है। – reuscam

+0

धन्यवाद, मैं यह करूँगा। और आप सही हैं, यह बिंदु या कुछ होना चाहिए। –

+0

दरअसल, एक वेक्टर को केवल एक बिंदु से परिभाषित किया जाता है। – Spidey

उत्तर

8

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

//very beginning of the file 
#ifndef HEADER_FILE_H //use a name that is unique though!! 
#define HEADER_FILE_H 
... 
//code goes here 
... 
#endif 
//very end of the file 

साथ प्रत्येक फ़ाइल (उदाहरण के लिए एक हेडर फाइल) शुरू करने के लिए किया गया था। असल में, मैं हमेशा फ़ाइल नाम के सभी अपरकेस संस्करण का उपयोग करता हूं। custom-vector.h

#ifndef CUSTOM_VECTOR_H 
#define CUSTOM_VECTOR_H 

यह आपको परिपत्र निर्भरता बनाने क्योंकि अगर एक फाइल कई बार शामिल किया गया है, इसके पूर्वप्रक्रमक चर पहले से ही परिभाषित किया गया है बिना फ़ाइलों को विली-nillie शामिल करने के लिए अनुमति देता है हो जाता है, तो पूर्वप्रक्रमक फ़ाइल को छोड़ देता है। यह बाद में कोड के साथ काम करने में भी आसान बनाता है क्योंकि आपको यह सुनिश्चित करने के लिए अपनी पुरानी हेडर फाइलों के माध्यम से नहीं जाना पड़ेगा कि आपने पहले से कुछ शामिल नहीं किया है। मैं फिर से दोहरा दूंगा, सुनिश्चित करें कि आपके #define कथन में उपयोग किए जाने वाले वेरिएबल नाम आपके लिए अद्वितीय हैं अन्यथा आप उन समस्याओं में भाग ले सकते हैं जहां कुछ ठीक से शामिल नहीं होता है ;-)।

शुभकामनाएं!

+0

आशा है कि आपको कोई फर्क नहीं पड़ता: मैंने आपके CUSTOM_VECTOR_CPP उदाहरण को संपादित किया । '* .c' और' * .cpp' फ़ाइलों को गार्ड शामिल करने की आवश्यकता नहीं है क्योंकि आप उन्हें शामिल नहीं करते हैं। –

+0

तो उदाहरण के लिए, यदि मैं एक दुश्मन वर्ग बनाना चाहता था जिसके लिए वेक्टर और स्वास्थ्य की आवश्यकता थी, और प्लेयर क्लास में पहले से ही वेक्टर और हेल्थ शामिल था, तो मुझे उन्हें दुश्मन के लिए शामिल करने की आवश्यकता नहीं होगी? –

+0

@ जॉन कोई समस्या नहीं!अच्छा कॉल, मुझे टेम्पलेट प्रोग्रामिंग में उपयोग किया जाता है जहां आपको * .h फ़ाइलों में * .cpp फ़ाइलों को शामिल करना होगा। मुझे अभी यह करने की आदत मिली है :-) –

3

आपके पास एक परिपत्र निर्भरता है। प्लेयर में main.h शामिल है, लेकिन main.h में खिलाड़ी.h शामिल है। एक निर्भरता या अन्य को हटाकर इसे हल करें। \

प्लेयर.h में स्वास्थ्य.h और custvector.h शामिल होना चाहिए, और इस बिंदु पर, मुझे नहीं लगता कि main.h को किसी भी शामिल की आवश्यकता है। आखिरकार इसे player.h की आवश्यकता हो सकती है।

+1

#ifndef ... #endif एक और आसान तरीका है, साथ ही –

+0

क्या कोई समस्या नहीं होगी यदि किसी और को स्वास्थ्य का उपयोग करने की आवश्यकता है। क्या इसे दो बार कार्यक्रम में शामिल नहीं किया जाएगा? –

+1

सी या सी ++ में हेडर फ़ाइल को कई बार शामिल करने से बचने का तरीका एक गार्ड का उपयोग करना है। प्रत्येक हेडर फ़ाइल दो पंक्तियों '#ifndef UNIQUE_STRING' से शुरू होती है और '# UNIQUE_STRING' परिभाषित करती है (UNIQUE_STRING को उस नाम से बदलती है जिसे आप कहीं और परिभाषित नहीं करते हैं)। फ़ाइल तब '# endif' के साथ समाप्त होती है। http://en.wikipedia.org/wiki/Include_guard –

2

में काम बहुत आसान है, वे केवल प्रीप्रोसेसर को फ़ाइल की सामग्री जोड़ने के लिए आदेश देते हैं जहां सेट शामिल है। बुनियादी विचार है कि आप जिन शीर्षकों पर निर्भर करते हैं उन्हें शामिल करना है। player.h में आपको custvector.h और Health.h शामिल करना चाहिए। मुख्य में केवल player.h, क्योंकि सभी आवश्यक शामिल खिलाड़ी के साथ किए जाएंगे। और आपको player.h में शामिल करने की आवश्यकता नहीं है।

यह सुनिश्चित करने के लिए भी अच्छा है कि हेडर केवल एक बार शामिल है। इस प्रश्न में सामान्य समाधान How to prevent multiple definitions in C? को विजुअल स्टूडियो के मामले में दिया जाता है, यदि आप बोर्लैंड सी ++ में एक चाल भी है तो मैं #pragma once का उपयोग कर सकता हूं लेकिन मैं इसे भूल गया।

+0

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

+0

@ करल मेनके 'main.h' इसे रखने के लिए अच्छी जगह नहीं है, क्योंकि इसमें main.cpp के लिए चीजें भी शामिल होंगी। आपको 'common.h' जैसे कुछ बनाना चाहिए और वहां सामान्य संदर्भों को स्थानांतरित करना चाहिए। फिर इसका इस्तेमाल करें। – Andrey

10

आपकी शीर्षलेख फ़ाइलों के बारे में सोचने का सबसे अच्छा तरीका "स्वचालित प्रति-पेस्ट" के रूप में है।

इसके बारे में सोचने का एक अच्छा तरीका (हालांकि यह वास्तव में कैसे लागू किया जाता है) यह है कि जब आप एक सी फ़ाइल या सी ++ फ़ाइल संकलित करते हैं, तो प्रीप्रोसेसर पहले चलता है। प्रत्येक बार जब यह # अंतर्निहित कथन का सामना करता है, तो यह वास्तव में # अंतर्निहित कथन के बजाय उस फ़ाइल की सामग्री पेस्ट करेगा। यह तब तक किया जाता है जब तक इसमें कोई और शामिल न हो। अंतिम बफर कंपाइलर को पास किया जाता है।

यह कई जटिलताओं का परिचय:

पहला, अगर A.H B.H भी शामिल है और B.H शामिल A.h, आप एक समस्या मिल गया है। क्योंकि हर बार जब आप ए पेस्ट करना चाहते हैं, तो आपको बी की आवश्यकता होगी और इसमें आंतरिक रूप से ए होगा! यह एक रिकर्सन है। इस कारण से, हेडर फाइलें #ifndef का उपयोग करती हैं, यह सुनिश्चित करने के लिए कि एक ही भाग कई बार नहीं पढ़ा जाता है। यह संभवतः आपके कोड में हो रहा है।

दूसरा, आपके सी कंपाइलर फ़ाइल को सभी हेडर फ़ाइलों को "चपटा" के बाद फ़ाइल पढ़ता है, इसलिए आपको यह समझने की आवश्यकता है कि क्या पहले घोषित किया गया है।

+0

यह स्पष्टीकरण मुझे और अधिक पसंद है, और एक जो मैं सबसे अच्छा वर्णन करता हूं कि मैं इस समस्या को कैसे समझता हूं। यह आसान है, संकलक पाठ लेता है और इसे संकलित करता है। प्रीप्रोसेसर टेक्स्ट लेता है और इसे प्रीप्रोसेस करता है। बाहरी फाइल से कुछ पाठ को वर्तमान फ़ाइल में आयात करने के लिए केवल एक प्रीप्रोसेसर निर्देश शामिल है। – Spidey

+0

धन्यवाद, यह मदद करता है। –

+0

+1 सरल और ठंडा .. :) – liaK

1

क्या कोई ऐसा चित्र या वीडियो है जो यह स्पष्ट कर सकता है कि इसे कैसे स्थापित किया जाना चाहिए?

प्रश्न पर मेरे उत्तर का उत्तर दें "Clean up your #include statements?"।

+0

धन्यवाद, मैं इसे देख लूंगा। –

+0

@ करल मेनके - एक और है [यहां] (http://stackoverflow.com/questions/1598207/odd-circular- निर्भरता-issue/1598257#1598257) – ChrisW

1

DAG (निर्देशित, विश्वकोश ग्राफ) में आप अपने #includes (और उस मामले के लिए पुस्तकालय) को व्यवस्थित करना चाहते हैं। ,

बी ए शामिल हैं, A, B

शामिल नहीं होना चाहिए तो, "एक बड़ा गुरु main.h" का उपयोग सही तरीका नहीं है क्योंकि यह: यह कह रही है "हेडर फाइल के बीच चक्र से बचने" के जटिल तरीका है केवल प्रत्यक्ष निर्भरताओं को शामिल करना मुश्किल है।

प्रत्येक .cpp फ़ाइल में अपनी स्वयं की .h फ़ाइल शामिल होनी चाहिए। वह .h फ़ाइल में केवल उन चीज़ों को शामिल करना चाहिए जिन्हें स्वयं संकलित करने की आवश्यकता है।

आमतौर पर main.h नहीं है, क्योंकि main.cpp किसी को भी मुख्य की परिभाषा की आवश्यकता नहीं है।

इसके अलावा, आप include guards चाहते हैं ताकि आप एकाधिक सुविधाओं के विरुद्ध सुरक्षा कर सकें।

उदाहरण के लिए

//player.h 
#ifndef PLAYER_H_ 
#define PLAYER_H_ 
#include "vector.h" // Because we use Vector 
#include "health.h" // Because we use Health 
class Player { 
    private: 
     Vector playerPos; 
    public: 
     Health playerHealth; 
}; 
#endif 

-

//vector.h 
#ifndef VECTOR_H_ 
#define VECTOR_H_ 
struct Vector { 
    int X; 
    int Y; 
    int Z; 
}; 
#endif 

-

//health.h 
#ifndef HEALTH_H_ 
#define HEALTH_H_ 
class Health { 
    private: 
     int curHealth; 
     int maxHealth; 
    public: 
     int getHealth() const; 
     void setHealth(int inH); 
     void modHealth(int inHM); 
}; 
#endif 

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

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

सी प्रीप्रोसेसर सचमुच फ़ाइल में #include से फ़ाइल को सम्मिलित करता है जिसमें इसे शामिल किया गया है (जब तक कि इसे पहले से ही डाला नहीं गया है, यही कारण है कि आपको शामिल गार्ड की आवश्यकता है)। यह आपको उन फ़ाइलों में परिभाषित कक्षाओं का उपयोग करने की अनुमति देता है क्योंकि अब आपके पास उनकी परिभाषा तक पहुंच है।

संबंधित मुद्दे