C ++ में आगे की घोषणाएं क्या हैं?


215

पर: http://www.learncpp.com/cpp-tutorial/19-header-files/

निम्नलिखित का उल्लेख किया गया है:

add.cpp:

int add(int x, int y)
{
    return x + y;
}

main.cpp:

#include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

हमने एक फॉरवर्ड डिक्लेरेशन का इस्तेमाल किया ताकि कंपाइलर को पता चले कि " add" कंपाइलिंग के दौरान क्या था main.cpp। जैसा कि पहले उल्लेख किया गया है, हर फ़ंक्शन के लिए आगे की घोषणाओं को लिखना जो आप किसी अन्य फ़ाइल में रहते हैं, जल्दी से थकाऊ हो सकते हैं।

क्या आप " आगे की घोषणा " को आगे बता सकते हैं ? यदि हम इसे main()फ़ंक्शन में उपयोग करते हैं तो क्या समस्या है?


1
एक "आगे की घोषणा" वास्तव में सिर्फ एक घोषणा है। देखें (अंत का) यह उत्तर: stackoverflow.com/questions/1410563/…
sbi

जवाबों:


381

C ++ में फॉरवर्ड-डिक्लेयर क्यों जरूरी है

संकलक यह सुनिश्चित करना चाहता है कि आपने वर्तनी की गलतियाँ नहीं की हैं या फ़ंक्शन के लिए गलत तर्क पारित नहीं किए हैं। इसलिए, यह इस बात पर जोर देता है कि इसका उपयोग करने से पहले यह पहले 'ऐड' (या किसी अन्य प्रकार, वर्ग या कार्य) की घोषणा देखता है।

यह वास्तव में संकलक को कोड को मान्य करने का एक बेहतर काम करने की अनुमति देता है, और इसे ढीले छोरों को साफ करने की अनुमति देता है ताकि यह एक साफ दिखने वाली वस्तु फ़ाइल का उत्पादन कर सके। यदि आपको चीजों को घोषित करने की आवश्यकता नहीं है, तो कंपाइलर एक ऑब्जेक्ट फ़ाइल का उत्पादन करेगा जिसमें सभी संभावित अनुमानों के बारे में जानकारी होगी जिसमें फ़ंक्शन 'ऐड' हो सकता है। और लिंक करने वाले को कोशिश करने और काम करने के लिए बहुत ही चतुर तर्क सम्‍मिलित करने होंगे, जिसे 'एड' करने के लिए आप वास्‍तव में कॉल करना चाहते हैं, जब 'ऐड' फंक्शन किसी अलग ऑब्जेक्ट में रह सकता है, तो फाइलर उस एड के साथ जुड़ रहा है, जो ऐड का निर्माण करता है। एक dll या exe। यह संभव है कि लिंक करने वाले को गलत ऐड मिल सकता है। मान लें कि आप int add (int a, float b) का उपयोग करना चाहते थे, लेकिन गलती से इसे लिखना भूल गए, लेकिन लिंकर को पहले से मौजूद int add (int a) मिला int b) और सोचा कि यह सही था और इसके बजाय इसका इस्तेमाल किया। आपका कोड संकलित करेगा, लेकिन वह नहीं करेगा जो आपने अपेक्षित किया था।

इसलिए, बस चीजों को स्पष्ट रखने और अनुमान लगाने आदि से बचने के लिए, कंपाइलर आपके द्वारा उपयोग किए जाने से पहले सब कुछ घोषित करने पर जोर देता है।

घोषणा और परिभाषा के बीच अंतर

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

कैसे अग्रेषण-घोषणाएँ बिल्ड समय को काफी कम कर सकती हैं

आप अपने वर्तमान में .cpp या .h फ़ाइल में एक फ़ंक्शन की घोषणा # शीर्षलेख के साथ कर सकते हैं जिसमें पहले से ही फ़ंक्शन की घोषणा शामिल है। हालाँकि, यह आपके संकलन को धीमा कर सकता है, खासकर यदि आप अपने प्रोग्राम के .cpp के बजाय एक .hp में हेडर लगाते हैं, तो वह सब कुछ के रूप में जो # लिखता है। यदि आप लिख रहे हैं तो सभी हेडर # शामिल होंगे। आपने इसके लिए भी # लिखा था। अचानक, संकलक में #included पृष्ठों और कोड के पृष्ठ होते हैं, जिन्हें उस समय भी संकलित करने की आवश्यकता होती है जब आप केवल एक या दो फ़ंक्शन का उपयोग करना चाहते थे। इससे बचने के लिए, आप फ़ॉरवर्ड-डिक्लेरेशन का उपयोग कर सकते हैं और फ़ाइल के शीर्ष पर स्वयं फ़ंक्शन की घोषणा टाइप करें। यदि आप केवल कुछ फ़ंक्शन का उपयोग कर रहे हैं, तो यह वास्तव में हेडर को हमेशा #including करने की तुलना में आपके संकलन को तेज बना सकता है। वास्तव में बड़ी परियोजनाओं के लिए,

चक्रीय संदर्भों को तोड़ें जहां दो परिभाषाएँ दोनों एक दूसरे का उपयोग करती हैं

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

उदाहरण के लिए:

फ़ाइल Car.h

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

फ़ाइल व्हील

हम्म ... कार की घोषणा यहां आवश्यक है क्योंकि व्हील में एक कार के लिए एक संकेतक है, लेकिन कार को यहां शामिल नहीं किया जा सकता है क्योंकि यह एक संकलक त्रुटि के परिणामस्वरूप होगा। यदि Car.h को शामिल किया गया था, तो वह Wheel.h को शामिल करने का प्रयास करेगा जिसमें Car.h शामिल होगा जिसमें Wheel.h शामिल होगा और यह हमेशा के लिए चलेगा, इसलिए इसके बजाय संकलक एक त्रुटि उठाता है। इसके बजाय कार को घोषित करने के लिए समाधान आगे है:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

यदि क्लास व्हील में ऐसी विधियाँ होती हैं, जिनमें कार के तरीकों को कॉल करने की आवश्यकता होती है, तो उन तरीकों को Wheel.cpp में परिभाषित किया जा सकता है और Wheel.cpp अब एक चक्र पैदा किए बिना Car.h को शामिल करने में सक्षम है।


4
आगे की घोषणा भी आवश्यक है जब एक फ़ंक्शन दो या कई वर्गों के अनुकूल होता है
बरुण

1
हे स्कॉट, बिल्ड टाइम पर अपनी बात पर: क्या आप कहेंगे कि एक सामान्य / सर्वोत्तम प्रैक्टिस है जिसे हमेशा घोषित करने और हेड को आवश्यकतानुसार शामिल करने के लिए .cpp फाइल में? आपके उत्तर को पढ़ने से यह प्रतीत होता है कि ऐसा होना चाहिए, लेकिन मुझे आश्चर्य है कि क्या कोई चेतावनी है?
जिप्पी

5
@Zepee यह एक संतुलन है। त्वरित निर्माण के लिए, मैं कहूंगा कि यह अच्छा अभ्यास है और मैं इसे आजमाने की सलाह देता हूं। हालाँकि, यह कोड के कुछ प्रयास और अतिरिक्त लाइनें ले सकता है जिन्हें बनाए रखने और अद्यतन करने की आवश्यकता हो सकती है यदि प्रकार के नाम आदि को अभी भी बदला जा रहा है (हालांकि स्वचालित रूप से सामान का नाम बदलने में उपकरण बेहतर हो रहे हैं)। तो एक व्यापार है। मैंने कोड बेस देखे हैं जहां कोई भी परेशान नहीं करता है। यदि आप अपने आप को एक ही आगे के परिभाषित को दोहराते हुए पाते हैं, तो आप उन्हें हमेशा एक अलग हेडर फाइल में डाल सकते हैं और उसमें शामिल हो सकते हैं, जैसे: stackoverflow.com/questions/4300696/what-is-the-iosfwd-header
स्कॉट लैंगहम

जब हेडर फाइलें एक-दूसरे को संदर्भित करती हैं तो आगे की घोषणा की आवश्यकता होती है: यानी stackoverflow.com/questions/396084/…
निकोलस हैमिल्टन

1
मैं इसे अपनी टीम के अन्य देवों को कोडबेस के वास्तव में बुरे नागरिक होने की अनुमति देता देख सकता हूं। यदि आपको आगे की घोषणा के साथ एक टिप्पणी की आवश्यकता नहीं है, // From Car.hतो आप सड़क के नीचे एक परिभाषा खोजने की कोशिश कर रहे कुछ बालों वाली स्थितियों की गारंटी दे सकते हैं।
डैगरूम

25

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

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

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

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


12

क्योंकि C ++ को ऊपर से नीचे की ओर रखा गया है, इसलिए कंपाइलर को उपयोग करने से पहले चीजों के बारे में जानना आवश्यक है। इसलिए, जब आप संदर्भ दें:

int add( int x, int y )

मुख्य फ़ंक्शन में संकलक को यह जानना आवश्यक है कि यह मौजूद है। यह साबित करने के लिए इसे मुख्य फ़ंक्शन के नीचे ले जाने का प्रयास करें और आपको एक कंपाइलर त्रुटि मिलेगी।

तो एक ' फॉरवर्ड डिक्लेरेशन ' सिर्फ यह है कि यह टिन पर क्या कहता है। यह इसके उपयोग से पहले ही कुछ घोषित कर रहा है।

आम तौर पर आप एक हेडर फ़ाइल में आगे की घोषणाओं को शामिल करेंगे और फिर उस हेडर फ़ाइल को उसी तरह से शामिल करेंगे जिस तरह से iostream शामिल है।


12

C ++ में " फॉरवर्ड डिक्लेरेशन " शब्द का प्रयोग केवल क्लास डिक्लेरेशन के लिए किया जाता है । देखें (अंत में) इस उत्तर के लिए एक वर्ग के "आगे की घोषणा" वास्तव में एक फैंसी नाम के साथ सिर्फ एक साधारण वर्ग की घोषणा क्यों है

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

(जैसा कि एक परिभाषा के विपरीत एक घोषणा क्या है , फिर से देखें कि एक परिभाषा और एक घोषणा के बीच अंतर क्या है? )


2

जब कंपाइलर देखता है add(3, 4)तो उसे पता होना चाहिए कि इसका क्या मतलब है। आगे की घोषणा के साथ आप मूल रूप से संकलक को बताते हैं जो addएक ऐसा फ़ंक्शन है जो दो ints लेता है और एक int देता है। यह संकलक के लिए यह महत्वपूर्ण जानकारी है क्योंकि इसे स्टैक पर सही प्रतिनिधित्व में 4 और 5 लगाने की जरूरत है और यह जानने की जरूरत है कि ऐड किस प्रकार की चीज है।

उस समय, संकलक के बारे में चिंतित नहीं है वास्तविक के कार्यान्वयन add, यानी कि वह कहाँ है (या अगर कोई है भी एक) और अगर यह संकलित करता है। जब लिंकर को लागू किया जाता है तो स्रोत फ़ाइलों को संकलित करने के बाद, बाद में देखने में आता है।


1
int add(int x, int y); // forward declaration using function prototype

क्या आप "आगे की घोषणा" को और अधिक समझा सकते हैं? यदि हम इसे मुख्य () फ़ंक्शन में उपयोग करते हैं तो क्या समस्या है?

यह वैसा ही है #include"add.h"। यदि आप जानते हैं, तो प्रीप्रोसेसर उस फ़ाइल का विस्तार करता है जिसका आप उल्लेख करते हैं #include, .cpp फ़ाइल में जहाँ आप #includeनिर्देश लिखते हैं । इसका मतलब है, यदि आप लिखते हैं, तो आपको #include"add.h"वही मिलता है, यह ऐसा है जैसे आप "आगे की घोषणा" कर रहे हैं।

मैं मान रहा हूँ कि add.hयह रेखा है:

int add(int x, int y); 

1

एक त्वरित परिशिष्ट के बारे में: आमतौर पर आप उन फ़ॉरवर्ड रेफ़र को .c (पीपी) फ़ाइल से संबंधित हेडर फ़ाइल में डालते हैं जहाँ फ़ंक्शन / वेरिएबल आदि को लागू किया जाता है। आपके उदाहरण में यह इस तरह दिखेगा: add.h:

extern int add (इंट ए, इंट बी);

कीवर्ड एक्सटर्नल बताता है कि फ़ंक्शन वास्तव में एक बाहरी फ़ाइल (एक पुस्तकालय आदि भी हो सकता है) में घोषित किया गया है। आपका main.c इस तरह दिखेगा:

#शामिल 
# शामिल करें "add.h"

मुख्य प्रवेश बिंदु()
{
।
।
।


लेकिन, क्या हम केवल हेडर फाइल में घोषणाएं नहीं करते हैं? मुझे लगता है कि यही कारण है कि फ़ंक्शन को "add.cpp" में परिभाषित किया गया है, और इस प्रकार आगे की घोषणाओं का उपयोग किया जा रहा है? धन्यवाद।
सादगी

0

एक समस्या यह है, कि संकलक को पता नहीं है, कि आपके फ़ंक्शन द्वारा किस प्रकार का मूल्य दिया गया है; मान लिया जाता है, कि intइस मामले में फ़ंक्शन वापस आ गया है, लेकिन यह सही हो सकता है क्योंकि यह गलत हो सकता है। एक और समस्या यह है कि कंपाइलर को यह पता नहीं होता है कि आपके कार्य की किस तरह की दलीलें हैं, और अगर आप गलत तरह के मूल्यों से गुजर रहे हैं तो आपको चेतावनी नहीं दे सकते। विशेष "पदोन्नति" नियम हैं, जो गुजरते समय लागू होते हैं, अघोषित फ़ंक्शन के लिए फ्लोटिंग पॉइंट वैल्यू कहते हैं (कंपाइलर को उन्हें डबल टाइप करने के लिए चौड़ा करना पड़ता है), जो अक्सर नहीं होता है, फ़ंक्शन वास्तव में क्या उम्मीद करता है, जिससे कीड़े खोजने में मुश्किल होती है। चलने के समय पर।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.