2009-04-26 6 views
8

मैंने अपने ट्यूटोरियल का उपयोग करके क्यूटी सीखना शुरू कर दिया है। मैं वर्तमान में ट्यूटोरियल 7 पर हूं, जहां हमने एक नई एलसीडी श्रेणी कक्षा बनाई है। LCDRange (.cpp फ़ाइल) के कार्यान्वयन क्यूटी QSlider वर्ग का उपयोग करता है, तो .cpp फ़ाइल मेंक्या लाइब्रेरी कक्षाओं को आगे घोषित करना उचित है?

#include <QSlider> 

है, लेकिन शीर्षक में एक आगे घोषणा है:

class QSlider; 
क्यूटी के अनुसार

,

यह एक और क्लासिक चाल है, लेकिन वह अक्सर बहुत कम उपयोग किया जाता है। चूंकि हमें वर्ग के इंटरफ़ेस में QSlider की आवश्यकता नहीं है, केवल कार्यान्वयन में, हम हेडर फ़ाइल में कक्षा की अगली घोषणा का उपयोग करते हैं और .cpp फ़ाइल में QSlider के लिए हेडर फ़ाइल शामिल करते हैं।

यह बड़ी परियोजनाओं का संकलन बहुत तेज़ बनाता है, क्योंकि संकलक आमतौर पर अपने अधिकांश समय पार्सिंग हेडर फाइलों को वास्तविक स्रोत कोड नहीं देता है। यह चाल अकेले दो या दो से अधिक कारकों द्वारा संकलन को तेज कर सकती है।

क्या यह करने योग्य है? ऐसा लगता है, लेकिन ट्रैक रखने के लिए यह एक और बात है - मुझे लगता है कि हेडर फ़ाइल में सबकुछ शामिल करना बहुत आसान होगा।

उत्तर

17

बिल्कुल। सी/सी ++ बिल्ड मॉडल है ... अहम ... एक अनाचारवाद (सर्वश्रेष्ठ कहने के लिए)। बड़ी परियोजनाओं के लिए यह एक गंभीर पिटा बन जाता है।

जैसा कि नील सही तरीके से नोट करता है, यह आपके वर्ग डिज़ाइन के लिए डिफ़ॉल्ट दृष्टिकोण नहीं होना चाहिए, जब तक आपको वास्तव में आवश्यकता न हो, तब तक अपने रास्ते से बाहर न जाएं।

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

// a.h 
#include "b.h" 
struct A { B * a; } 

// b.h 
#include "a.h" // circlular include reference 
struct B { A * a; } 

// Solution: break circular reference by forward delcaration of B or A 

समय के पुनर्निर्माण को कम करना - निम्नलिखित कोड

// foo.h 
#include <qslider> 
class Foo 
{ 
    QSlider * someSlider; 
} 

अब हर .cpp फ़ाइल है कि प्रत्यक्ष या परोक्ष रूप Foo.h में खींचती भी QSlider.h और उसके निर्भरता के सभी में खींचती है कल्पना कीजिए। यह सैकड़ों .cpp फाइलें हो सकती है!(प्रीकंपिल्ड हेडर थोड़ा सा मदद करते हैं - और कभी-कभी बहुत कुछ - लेकिन वे स्मृति/डिस्क दबाव में डिस्क/सीपीयू दबाव को बदलते हैं, और इस प्रकार जल्द ही "अगली" सीमा को मार रहे हैं)

यदि शीर्षलेख को केवल एक संदर्भ घोषणा की आवश्यकता है, तो निर्भरता अक्सर कुछ फ़ाइलों तक सीमित हो सकती है, उदाहरण के लिए foo.cpp।

वृद्धिशील निर्माण समय को कम करना - प्रभाव स्वयं को (स्थिर पुस्तकालय की बजाय) शीर्षकों से निपटने पर भी अधिक स्पष्ट है। कल्पना कीजिए कि आप

// bar.h 
#include "foo.h" 
class Bar 
{ 
    Foo * kungFoo; 
    // ... 
} 

अब अगर आपके सीपीपी की जरूरत के सबसे bar.h में खींचने के लिए, वे भी परोक्ष रूप से foo.h. में खींच इस प्रकार, foo.h ट्रिगर्स के प्रत्येक परिवर्तन इन सभी .cpp फ़ाइलों का निर्माण (जो फू को जानने की भी आवश्यकता नहीं है!)। bar.h बजाय फू के लिए एक आगे घोषणा उपयोग करता है, foo.h पर निर्भरता bar.cpp तक ही सीमित है:

// bar.h 
class Foo; 
class Bar 
{ 
    Foo * kungFoo; 
    // ... 
} 

// bar.cpp 
#include "bar.h" 
#include "foo.h" 
// ... 

यह इतना आम है कि यह एक पैटर्न है - PIMPL pattern। इसका उपयोग दो गुना है: पहला यह सच इंटरफेस/कार्यान्वयन अलगाव प्रदान करता है, दूसरा निर्माण निर्भरताओं को कम कर रहा है। प्रैक्टिस में, मैं उनकी उपयोगीता 50:50 वजन कम करूंगा।

आपको हेडर में संदर्भ की आवश्यकता है, तो आप निर्भर प्रकार का प्रत्यक्ष तत्काल नहीं हो सकते हैं। यह उन मामलों को सीमित करता है जहां आगे की घोषणाओं को लागू किया जा सकता है। यदि आप इसे स्पष्ट रूप से करते हैं, तो इसके लिए उपयोगिता वर्ग (जैसे boost::scoped_ptr) का उपयोग करना आम है।

क्या बिल्ड समय इसके लायक है?Definitely, मैं कहूंगा। सबसे खराब मामले में निर्माण समय परियोजना में फाइलों की संख्या के साथ बहुपद बढ़ता है। अन्य तकनीकें - जैसे तेज मशीनें और समांतर बिल्ड - केवल प्रतिशत लाभ प्रदान कर सकती हैं।

तेजी से निर्माण, अधिकतर डेवलपर्स परीक्षण करते हैं कि उन्होंने क्या किया है, अधिकतर यूनिट परीक्षण चलते हैं, तेजी से निर्माण ब्रेक तय किए जा सकते हैं, और कम डेवलपर्स को समाप्त होने का अंत होता है।

प्रैक्टिस में, अपने निर्माण समय को प्रबंधित करना, जबकि एक बड़ी परियोजना (सैकड़ों स्रोत फाइलें) पर आवश्यक है, यह अभी भी छोटी परियोजनाओं पर "आराम अंतर" बनाता है। इसके अलावा, तथ्य के बाद सुधार जोड़ना अक्सर धैर्य में व्यायाम होता है, क्योंकि एक फिक्स 40 मिनट के निर्माण के केवल सेकंड (या कम) को बंद कर सकता है।

+0

यदि बिल्डिंग समय के बारे में आपने जो कहा है, वह विस्तारित रूप से बढ़ रहा है, जिस परियोजना पर मैं काम कर रहा हूं (जिसमें 100 स्रोत फाइलें हैं) को स्क्रैच से संकलित करने के लिए लगभग 2^100 सेकंड लेना चाहिए - इसके बजाय इसमें लगभग एक मिनट लगते हैं। –

+0

@Neil: आपके द्वारा जोड़े जाने वाली प्रत्येक फ़ाइल के लिए, बिल्ड समय परियोजना के आकार के अनुपात में बढ़ता है (क्योंकि नई फ़ाइल को संकलित करने के लिए, आपको प्रोजेक्ट के आकार के आनुपातिक कुछ शीर्षकों को संसाधित करना होगा)। यह मेरे लिए घातीय लगता है। –

+0

स्पष्ट किया गया है: यह निश्चित रूप से सबसे खराब स्थिति परिदृश्य (कोड-गहन, दृढ़ता से परस्पर निर्भर हेडर) है। मेरे अनुभव में, मशीन की ईप्रोफॉर्मेंस सीमाओं को मारने के बाद स्पीड सर्पिल बनाएं, और आप जो कुछ भी कर सकते हैं वह बाधाओं के बीच व्यापार कर रहा है। --- मुझे उम्मीद है कि "निष्क्रिय उपेक्षा" आम तौर पर (केवल) बहुपद बढ़ने का कारण बनती है, लेकिन गलत उम्मीदें या अनुपयुक्त कोडिंग मानकों से आप एक्सपोनियल ड्रेनहोल का नेतृत्व कर सकते हैं। – peterchen

3

हां, यह निश्चित रूप से मदद करता है। यदि आप संकलन समय के बारे में चिंतित हैं तो अपने प्रदर्शन में जोड़ने के लिए एक और चीज प्रीकंपिल्ड हेडर है।

अप FAQ 39.12 और 39.13

+0

क्या यह वीएस चीज नहीं है? – Skilldrick

+0

http://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html - सहायता करनी चाहिए। – dirkgently

+0

धन्यवाद! केवल एक चीज ऊपर अब तक कि मैं precompiled हेडर के बारे में जानते थे कि एक एसडीएल ट्यूटोरियल मैं उन्हें दृश्य स्टूडियो में बंद करने के लिए मुझे बता द्वारा शुरू किया पीछा किया ... – Skilldrick

2

मानक पुस्तकालय करता मानक हेडर <iosfwd> में iostream वर्ग के कुछ के लिए देखो। हालांकि, यह आम तौर पर लागू तकनीक नहीं है - ध्यान दें कि अन्य मानक लाइब्रेरी प्रकारों के लिए ऐसे कोई शीर्षलेख नहीं हैं, और यह वर्ग विरासत को डिजाइन करने के लिए (IMHO) आपका डिफ़ॉल्ट दृष्टिकोण नहीं होना चाहिए।

हालांकि यह प्रोग्रामर के लिए पसंदीदा "अनुकूलन" माना जाता है, मुझे संदेह है कि अधिकतर अनुकूलन की तरह, उनमें से कुछ ने वास्तव में ऐसी घोषणाओं के साथ और बिना दोनों परियोजनाओं के निर्माण का समय दिया है। इस क्षेत्र में मेरे सीमित प्रयोगों से संकेत मिलता है कि आधुनिक कंपाइलरों में प्री-कंपाइल हेडर का उपयोग इसे अनावश्यक बनाता है।

+0

+1: "यह डिफ़ॉल्ट दृष्टिकोण नहीं होना चाहिए", और "उपाय" के लिए। – peterchen

+0

"अनुकूलन" टिप्पणी पर सहमत हुए। वितरित बिल्डों के साथ, मुझे लगता है कि (समांतर) संकलन चरण (सिंगल-थ्रेडेड) लिंक चरण से छोटा होता है, खासकर जब पूरे प्रोग्राम अनुकूलन का उपयोग करते हैं। आगे की घोषणाओं में वृद्धिशील समय का समय कम हो सकता है, लेकिन यह मापना कठिन है, और मेरी टीम के रात के निर्माण को प्रभावित नहीं करता है। – bk1e

8

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

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

/* --- --- --- Y.hpp */ 
class X; 
class Y { 
    X *x; 
}; 

/* --- --- --- Y.cpp */ 
#include <x.hpp> 
#include <y.hpp> 

... 

स्मार्ट संकेत दिए गए कि विशेष रूप से अधूरा प्रकार के संकेत दिए गए के साथ काम करने के लिए डिज़ाइन कर रहे हैं:

यह किसी न किसी योजना है।एक बहुत प्रसिद्ध व्यक्ति boost::shared_ptr है।

+0

मैं हमेशा इस रणनीति का पालन करता हूं, और यह संकलन समय को कम करने में मदद करता है। – Naveen

0

सामान्य रूप से, नहीं।

मैं जितना संभव था उतना घोषित करना चाहता था, लेकिन अब नहीं।

जहां तक ​​क्यूटी का संबंध है, आप देख सकते हैं कि <QtGui> में ऐसी फ़ाइल शामिल है जो सभी जीयूआई विजेटों को खींच लेगी। इसके अलावा, <QtCore>, <QtWebKit>, <QtNetwork> आदि है। प्रत्येक मॉड्यूल के लिए एक हेडर फ़ाइल है। ऐसा लगता है कि क्यूटी टीम का मानना ​​है कि यह पसंदीदा तरीका भी है। वे अपने मॉड्यूल दस्तावेज में ऐसा कहते हैं।

सच है, संकलन समय बढ़ाया जा सकता है। लेकिन मेरे अनुभव में यह इतना नहीं है। और यदि यह था, प्रीकंपील्ड हेडर का उपयोग करना अगला कदम होगा।

+0

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

+0

जैसे कि आप QtGui/qregion.h में देखते हैं, तो आप देखेंगे कि इसमें संपूर्ण qvector.hpp शामिल नहीं है क्योंकि इसे एक सदस्य फ़ंक्शन के रिटर्न प्रकार में QVector की आवश्यकता है। लेकिन यह एक आगे की घोषणा का उपयोग करता है "टेम्पलेट कक्षा क्यूवीक्टर;" बजाय। –

+1

उह। मुझे पता था कि मैं इसके लिए मतदान करने जा रहा था। ऐसा लगता है कि एसओ सबसे लोकप्रिय जवाब पाने के बारे में है, सबसे सही नहीं। –

-1

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

1

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

0

जब आप लिखना ...

"foo.h"

में शामिल हैं ... आप इस तरह, एक पारंपरिक निर्माण प्रणाली "किसी भी समय whatsover पुस्तकालय फ़ाइल foo.h में कोई बदलाव नहीं आया है हिदायत इस संकलन इकाई को त्यागें और इसे पुनर्निर्माण करें, भले ही foo.h के साथ जो भी हुआ वह एक टिप्पणी के अतिरिक्त था, या foo.h में कुछ फ़ाइल को टिप्पणी जोड़ने के अलावा, यहां तक ​​कि यदि कुछ हुआ तो कुछ अल्ट्रा-हास्यास्पद सहयोगी घुंघराले ब्रेसिज़ को दोबारा संतुलित करें, भले ही किसी दबाए गए सहयोगी के अलावा कुछ भी नहीं हुआ, foo.h में अपरिवर्तित और अनजाने में अपना टाइमस्टैम्प बदल गया। "

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

सी ++ नियम नाम स्थान क्रम आगे घोषणा का समर्थन करने के एक संकलन इकाई (एक struct या वर्ग के विपरीत) में किसी भी बिंदु पर फिर से खोले जाने के लिए अनुमति देते हैं।

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