स्टेटिक कास्ट बनाम # डेफिन


212

क्या प्रीप्रोसेसर की static constतुलना में var का उपयोग करना बेहतर है #define? या शायद यह संदर्भ पर निर्भर करता है?

प्रत्येक विधि के लिए फायदे / नुकसान क्या हैं?


14
स्कॉट मेयर्स इस विषय को बहुत अच्छी तरह से और पूरी तरह से शामिल करते हैं। उनका आइटम # 2 "इफेक्टिव सी ++ थर्ड एडिशन" में। दो विशेष मामलों (1) स्थिर कास्ट को वर्ग विशिष्ट स्थिरांक के लिए एक वर्ग दायरे में पसंद किया जाता है; (2) नेमस्पेस या अनाम स्कोप कास्ट #define से अधिक पसंद किया जाता है।
एरिक

2
मुझे एनम पसंद है। क्योंकि यह दोनों का हाइब्रिड है। जब तक आप इसका एक चर नहीं बनाते तब तक स्थान पर कब्जा नहीं करता है। यदि आप केवल एक स्थिर के रूप में उपयोग करना चाहते हैं, तो एनम सबसे अच्छा विकल्प है। इसमें C / C ++ 11 std में टाइप सेफ्टी है और एक परफेक्ट कंटिन्यू भी है। #define टाइप असुरक्षित है, अगर कंपाइलर इसे ऑप्टिमाइज़ नहीं कर सकता तो कॉस्ट जगह लेती है।
सिद्धसिंह

1
मेरे निर्णय का उपयोग करना है #defineया static const(तार के लिए) के द्वारा संचालित है आरंभीकरण (यह नीचे दिए गए उत्तर के माध्यम से उल्लेख नहीं किया गया था) पहलू: अगर लगातार विशेष संकलन इकाई के भीतर प्रयोग किया जाता है केवल, तो मैं के साथ जाना static constहै, और मैं उपयोग #define- से बचने के स्थिर आदेश प्रारंभ असफलता isocpp.org/wiki/faq/ctors#static-init-order
मार्टिन ड्वोरक

अगर const, constexprया enumआपके मामले में कोई भिन्नता काम करती है, तो इसे पसंद करें#define
23:19 पर फिल 1970

@MartinDvorak " स्थैतिक क्रम आरंभीकरण से बचें " यह कैसे स्थिरांक के लिए एक समस्या है?
जिज्ञासु १५'१

जवाबों:


139

निजी तौर पर, मैं प्रीप्रोसेसर से घृणा करता हूं, इसलिए मैं हमेशा साथ रहूंगा const

इसका मुख्य लाभ #defineयह है कि इसे आपके प्रोग्राम में स्टोर करने के लिए कोई मेमोरी की आवश्यकता नहीं है, क्योंकि यह वास्तव में कुछ पाठ को शाब्दिक मान से बदल रहा है। इसका यह भी लाभ है कि इसका कोई प्रकार नहीं है, इसलिए इसका उपयोग बिना किसी चेतावनी के किसी भी पूर्णांक मान के लिए किया जा सकता है।

" const" लाभ यह है कि उन्हें स्कोप किया जा सकता है, और उनका उपयोग उन स्थितियों में किया जा सकता है, जहां किसी ऑब्जेक्ट को पॉइंटर पास करने की आवश्यकता होती है।

मैं नहीं जानता कि वास्तव में आप " static" भाग के साथ क्या कर रहे हैं । यदि आप विश्व स्तर पर घोषणा कर रहे हैं, तो मैं इसे उपयोग करने के बजाय एक अनाम नाम स्थान पर रखूंगा static। उदाहरण के लिए

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}

8
स्ट्रिंग स्थिरांक विशेष रूप से उन लोगों में से एक हैं जो #defineडी होने से लाभान्वित हो सकते हैं , कम से कम यदि वे बड़े स्ट्रिंग आवेदकों के लिए "बिल्डिंग ब्लॉक" के रूप में उपयोग किए जा सकते हैं। एक उदाहरण के लिए मेरा उत्तर देखें।
चींटी

62
#defineकिसी भी स्मृति का उपयोग नहीं कर का लाभ गलत है। उदाहरण में "60" को कहीं भी संग्रहीत किया जाना है, भले ही यह static constया #define। वास्तव में, मैंने ऐसे कंपाइलर देखे हैं जहां #define का उपयोग करने से बड़े पैमाने पर (केवल-पढ़ने के लिए) मेमोरी की खपत होती है, और स्थैतिक कास्ट ने बिना किसी आवश्यक मेमोरी का उपयोग किया है।
गिलाद नोर

3
यदि आप इसे टाइप कर चुके हैं तो #define ऐसा है, इसलिए निश्चित रूप से यह मेमोरी से नहीं आ रहा है।
रेवरेंड

27
@ क्या शाब्दिक मूल्य किसी भी तरह से मशीन संसाधनों का उपभोग करने से छूट जाते हैं? नहीं, वे बस उन्हें अलग-अलग तरीकों से उपयोग कर सकते हैं, हो सकता है कि यह स्टैक या हीप पर दिखाई न दे, लेकिन कुछ बिंदु पर कार्यक्रम को मेमोरी में लोड किया जाता है और इसके साथ संकलित सभी मूल्यों के साथ।
सकीले

13
@ गिल्ड-नोर, आप सामान्य रूप से सही हैं, लेकिन 60 जैसे छोटे पूर्णांक वास्तव में कभी-कभी आंशिक अपवाद हो सकते हैं। कुछ निर्देश सेटों में पूर्णांक या पूर्णांकों के एक उपसमूह को सीधे निर्देश धारा में एनकोड करने की क्षमता होती है। उदाहरण के लिए MIPs तत्काल ( cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/addi.html ) जोड़ते हैं । इस तरह के मामले में एक # पूर्णांक पूर्णांक सही मायने में कोई स्थान का उपयोग करने के लिए नहीं कहा जा सकता है क्योंकि संकलित बाइनरी में यह कुछ अतिरिक्त बिट्स को निर्देश में रखता है जो वैसे भी मौजूद थे।
आह

242

उपयोग के आधार पर #defines, consts और (जो आप भूल गए हैं) के बीच पेशेवरों और विपक्ष enum:

  1. enumहै:

    • पूर्णांक मानों के लिए ही संभव है
    • अच्छी तरह से स्कूप्ड / आइडेंटिफ़ायर क्लैश मुद्दों को अच्छी तरह से संभाला जाता है, विशेष रूप से C ++ 11 एंम क्लासेस में जहां के लिए एन्यूमरेशन enum class Xस्कोप द्वारा डिसिबिगर किए जाते हैंX::
    • दृढ़ता से टाइप किया गया, लेकिन एक बड़े-पर्याप्त हस्ताक्षरित-या-अहस्ताक्षरित int आकार पर, जिस पर आपके पास C ++ 03 में कोई नियंत्रण नहीं है (हालांकि आप एक बिट फ़ील्ड निर्दिष्ट कर सकते हैं जिसमें उन्हें पैक किया जाना चाहिए यदि एनम संरचना का सदस्य है / वर्ग / संघ), जबकि C ++ 11 चूक intलेकिन प्रोग्रामर द्वारा स्पष्ट रूप से निर्धारित किया जा सकता है
    • पता नहीं लगा सकते - वहाँ एक नहीं है क्योंकि उपयोग के बिंदुओं पर गणना मूल्यों को प्रभावी ढंग से इनलाइन प्रतिस्थापित किया जाता है
    • मजबूत उपयोग पर प्रतिबंध (उदाहरण के लिए वेतन वृद्धि - template <typename T> void f(T t) { cout << ++t; }संकलन नहीं करेगा, हालांकि आप एक वर्ग में निहित निर्माणकर्ता, कास्टिंग ऑपरेटर और उपयोगकर्ता-परिभाषित ऑपरेटरों के साथ एक एनम लपेट सकते हैं)
    • संलग्नक एनुम से लिया गया प्रत्येक स्थिर प्रकार, इसलिए template <typename T> void f(T)अलग-अलग एनम से समान संख्यात्मक मान पारित करने पर एक अलग तात्कालिकता प्राप्त होती है, जो सभी किसी भी वास्तविक f(int)तात्कालिकता से अलग होती हैं । प्रत्येक फ़ंक्शन का ऑब्जेक्ट कोड समान हो सकता है (एड्रेस ऑफ़सेट्स को नज़रअंदाज़ करना), लेकिन मैं अनावश्यक कॉपी को खत्म करने के लिए एक कंपाइलर / लिंकर की अपेक्षा नहीं करूंगा, हालांकि आप देखभाल करने पर अपने कंपाइलर / लिंकर की जांच कर सकते हैं।
    • टाइपोफ़ / डिक्टाइप के साथ भी, संख्यात्मक मानों को सार्थक मूल्यों और संयोजनों के संयोजन में उपयोगी अंतर्दृष्टि प्रदान करने की उम्मीद नहीं कर सकता है (वास्तव में, "कानूनी" संयोजन स्रोत कोड में भी सूचित नहीं हैं, विचार करें enum { A = 1, B = 2 }- A|Bएक प्रोग्राम लॉजिक से "कानूनी" है परिप्रेक्ष्य?)
    • आरटीआई, संकलक संदेश आदि में विभिन्न स्थानों में एनुम का टाइपनेम दिखाई दे सकता है - संभवतः उपयोगी, संभवतः आपत्तिजनक
    • आप अनुवाद इकाई के बिना एक गणना का उपयोग नहीं कर सकते हैं वास्तव में मूल्य को देखकर, जिसका अर्थ है कि पुस्तकालय एपीआई में एनमों को हेडर में उजागर होने वाले मूल्यों की आवश्यकता होती है, makeऔर अन्य टाइमस्टैम्प-आधारित पुनर्संयोजन उपकरण क्लाइंट पुनर्संयोजन को बदल देंगे जब वे बदल जाते हैं (खराब! )

  1. constहै:

    • अच्छी तरह से scoped / पहचानकर्ता संघर्ष मुद्दों अच्छी तरह से संभाला
    • मजबूत, एकल, उपयोगकर्ता-निर्दिष्ट प्रकार
      • आप एक #defineएएलए "टाइप" करने की कोशिश कर सकते हैं #define S std::string("abc"), लेकिन निरंतर उपयोग के प्रत्येक बिंदु पर अलग-अलग अस्थायी के निर्माण से बचा जाता है
    • एक परिभाषा नियम जटिलताओं
    • पता ले सकते हैं, उनके लिए कॉन्स्टेंट रेफरेंस बना सकते हैं आदि।
    • एक गैर- constमूल्य के समान , जो दोनों के बीच स्विच करने पर काम और प्रभाव को कम करता है
    • मूल्य को कार्यान्वयन फ़ाइल के अंदर रखा जा सकता है, जिससे एक स्थानीय पुनर्नवीनीकरण और परिवर्तन को लेने के लिए सिर्फ क्लाइंट लिंक की अनुमति मिलती है

  1. #defineहै:

    • "ग्लोबल" स्कोप / अधिक विवादास्पद usages के लिए प्रवण, जो हार्ड-टू-रिज़ॉल्यूशन संकलन मुद्दों और अनपेक्षित रन-टाइम परिणामों का निर्माण कर सकता है, न कि साइन त्रुटि संदेशों के बजाय; इसे कम करने की आवश्यकता है:
      • लंबे, अस्पष्ट और / या केंद्रीय रूप से समन्वित पहचानकर्ता, और उन तक पहुंच का उपयोग अंतर्निहित मिलान / वर्तमान / कोएनिग-लुक-अप नाम स्थान, नाम स्थान उपनाम आदि से लाभ नहीं हो सकता है।
      • ट्रम्पिंग सबसे अच्छा अभ्यास करते समय टेम्पलेट पैरामीटर पहचानकर्ताओं को एकल-वर्ण अपरकेस अक्षर (संभवतः एक संख्या के बाद) की अनुमति देता है, लोअरकेस अक्षरों के बिना पहचानकर्ताओं के अन्य उपयोग पारंपरिक रूप से ओएस और सी / सी / सी + लाइब्रेरी के बाहर प्रीप्रोसेसर डिफाइनर्स के लिए आरक्षित और अपेक्षित हैं। हेडर)। यह प्रबंधनीय पैमाने पर प्रीप्रोसेसर उपयोग के लिए प्रबंधनीय रहने के लिए महत्वपूर्ण है। 3 पार्टी पुस्तकालयों से अनुपालन की उम्मीद की जा सकती है। इसका तात्पर्य यह है कि परिभाषित करने से / करने के लिए मौजूदा कास्ट्स या एनमोंस का माइग्रेशन कैपिटलाइज़ेशन में बदलाव को शामिल करता है, और इसलिए इसे "सरल" रेम्पाइल के बजाय क्लाइंट सोर्स कोड के संपादन की आवश्यकता होती है। (व्यक्तिगत रूप से, मैं गणना के पहले अक्षर को कैपिटल करता हूं, लेकिन कॉन्स्ट्स नहीं, इसलिए मुझे उन दो के बीच भी माइग्रेट करना होगा - शायद उस पर पुनर्विचार करने का समय।)
    • अधिक संकलित समय संचालन संभव: स्ट्रिंग शाब्दिक संघनन, कड़ाई (आकार लेना), पहचानकर्ताओं में संघनन
      • नकारात्मक पक्ष यह है कि दिए गए #define X "x"और कुछ ग्राहक उपयोग अला "pre" X "post", यदि आप चाहते हैं या आपको एक रनटाइम-परिवर्तनशील चर बनाने की आवश्यकता है, तो एक निरंतर के बजाय आप क्लाइंट कोड को संपादित करते हैं (केवल पुनर्संयोजन के बजाय), जबकि वह संक्रमण एक const char*या const std::stringदिया से आसान है पहले से ही उपयोगकर्ता को संघटन कार्यों को शामिल करने के लिए मजबूर करता है (उदाहरण के "pre" + X + "post"लिए string)
    • sizeofएक परिभाषित संख्यात्मक शाब्दिक पर सीधे उपयोग नहीं कर सकते
    • अप्रकाशित (GCC की तुलना में चेतावनी नहीं दी जाती है unsigned)
    • कुछ संकलक / लिंकर / डिबगर श्रृंखला पहचानकर्ता को प्रस्तुत नहीं कर सकते हैं, इसलिए आपको "मैजिक नंबर" (स्ट्रिंग्स, जो भी हो ...) को देखना कम हो जाएगा
    • पता नहीं लगा सकते
    • प्रतिस्थापित मूल्य की आवश्यकता उस संदर्भ में कानूनी (या असतत) नहीं होनी चाहिए, जहां #define बनाया गया है, क्योंकि इसका उपयोग प्रत्येक बिंदु पर किया जाता है, इसलिए आप अभी तक घोषित वस्तुओं का संदर्भ नहीं दे सकते हैं, जिन्हें "कार्यान्वयन" पर निर्भर होना चाहिए। पहले से शामिल रहें, "स्थिरांक" बनाएं जैसे { 1, 2 }कि सरणियों को इनिशियलाइज़ करने के लिए इस्तेमाल किया जा सकता है, या #define MICROSECONDS *1E-6आदि ( निश्चित रूप से यह अनुशंसा नहीं की जा सकती है )!
    • कुछ विशेष चीजें जैसे __FILE__और __LINE__मैक्रो प्रतिस्थापन में शामिल किया जा सकता है
    • आप #ifकोड सहित सशर्त रूप से अस्तित्व के लिए बयानों और मूल्य के लिए परीक्षण कर सकते हैं (पूर्व -प्रकरण से अधिक शक्तिशाली "यदि" कोड के रूप में आवश्यक नहीं है यदि प्रीप्रोसेसर द्वारा चयनित नहीं है), उपयोग #undef-इन, रीडिफाइन आदि।
    • प्रतिस्थापित पाठ को उजागर करना होगा:
      • अनुवाद इकाई में इसका उपयोग किया जाता है, जिसका अर्थ है कि क्लाइंट के उपयोग के लिए पुस्तकालयों में मैक्रो हेडर में होना चाहिए, इसलिए makeऔर टाइमस्टैम्प-आधारित पुनर्संयोजन उपकरण क्लाइंट पुनर्संयोजन को बदल देंगे जब वे बदल जाएंगे (खराब!)
      • या कमांड लाइन पर, जहां क्लाइंट कोड को पुन: स्थापित करने के लिए और भी अधिक देखभाल की आवश्यकता होती है (उदाहरण के लिए परिभाषा की आपूर्ति करने वाली मेकफाइल या स्क्रिप्ट को एक निर्भरता के रूप में सूचीबद्ध किया जाना चाहिए)

मेरी निजी राय:

एक सामान्य नियम के रूप में, मैं constएस का उपयोग करता हूं और उन्हें सामान्य उपयोग के लिए सबसे अधिक पेशेवर विकल्प मानता हूं (हालांकि अन्य में इस पुराने आलसी प्रोग्रामर के लिए एक सरलता है)।


1
बहुत बढ़िया जवाब। एक छोटी सी नाइटी: मैं कभी-कभी स्थानीय एनम का उपयोग करता हूं जो कोड की स्पष्टता के लिए हेडर में नहीं होते हैं, जैसे कि छोटे राज्य मशीनों और ऐसे। इसलिए उन्हें हर समय हेडर में नहीं रहना पड़ता।
कीर्त

प्रो और विपक्ष को मिलाया जाता है, मैं एक तुलना तालिका देखना बहुत पसंद करूंगा।
अज्ञात १२

@ Unknown123: एक पोस्ट करने के लिए स्वतंत्र महसूस करें - अगर आप यहाँ से योग्य किसी भी बिंदु को महसूस करते हैं तो मुझे कोई आपत्ति नहीं है। चीयर्स
टोनी डेलरो

48

यदि यह एक C ++ प्रश्न है और यह #defineएक विकल्प के रूप में उल्लेख करता है, तो यह "वैश्विक" (यानी फ़ाइल-स्कोप) स्थिरांक के बारे में है, वर्ग के सदस्यों के बारे में नहीं। जब C ++ में ऐसे स्थिरांक की बात आती है तो यह static constबेमानी है। C ++ constमें डिफ़ॉल्ट रूप से आंतरिक लिंकेज है और उन्हें घोषित करने का कोई मतलब नहीं है static। तो यह वास्तव में constबनाम के बारे में है #define

और, अंत में, सी ++ constमें बेहतर है। कम से कम क्योंकि इस तरह के स्थिरांक टाइप और स्कोप होते हैं। वहाँ बस कोई कारणों पसंद कर रहे हैं #defineसे अधिक constकुछ अपवादों को छोड़ कर,।

स्ट्रिंग स्थिरांक, BTW, इस तरह के अपवाद का एक उदाहरण है। साथ #defineघ स्ट्रिंग स्थिरांक एक के रूप में, सी / सी के ++ compilers संकलन समय संयोजन सुविधा का उपयोग कर सकते हैं

#define OUT_NAME "output"
#define LOG_EXT ".log"
#define TEXT_EXT ".txt"

const char *const log_file_name = OUT_NAME LOG_EXT;
const char *const text_file_name = OUT_NAME TEXT_EXT;

पीएस अगेन, केवल मामले में, जब कोई static constविकल्प के रूप में उल्लेख करता है #define, तो इसका आमतौर पर मतलब होता है कि वे C के बारे में बात कर रहे हैं, C ++ के बारे में नहीं। मुझे आश्चर्य है कि क्या यह प्रश्न ठीक से टैग किया गया है ...


1
" बस कोई कारण नहीं #define पसंद करते हैं " पर क्या? हेडर फ़ाइल में स्थिर चर को परिभाषित किया गया है?
जिज्ञासु

9

#define अप्रत्याशित परिणाम हो सकते हैं:

#include <iostream>

#define x 500
#define y x + 5

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

एक गलत परिणाम आउटपुट:

y is 505
z is 510

हालाँकि, यदि आप इसे स्थिरांक से बदलते हैं:

#include <iostream>

const int x = 500;
const int y = x + 5;

int z = y * 2;

int main()
{
    std::cout << "y is " << y;
    std::cout << "\nz is " << z;
}

यह सही परिणाम का उत्पादन करता है:

y is 505
z is 1010

ऐसा इसलिए है क्योंकि #defineकेवल टेक्स्ट को बदल देता है। क्योंकि ऐसा करने से संचालन के क्रम में गड़बड़ी हो सकती है, मैं इसके बजाय एक निरंतर चर का उपयोग करने की सलाह दूंगा।


1
मेरे पास एक अनपेक्षित परिणाम yथा : मूल्य था 5500, थोड़ा-सा एंडियन कॉन्सेप्टन xऔर 5.
कोड्स विथ हैमर

5

स्टैटिक कॉस्ट का उपयोग करना आपके कोड में किसी अन्य कॉन्स्टेबल चर का उपयोग करने जैसा है। इसका मतलब यह है कि आप जहां भी जानकारी मिलती है, उसका पता लगा सकते हैं, जैसा कि एक #define के विपरीत है, जिसे केवल पूर्व-संकलन प्रक्रिया में कोड में बदल दिया जाएगा।

आप इस प्रश्न के लिए C ++ FAQ Lite पर एक नज़र डाल सकते हैं: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7


4
  • एक स्थैतिक कास्ट टाइप होता है (इसका एक प्रकार होता है) और कंपाइलर द्वारा वैधता, पुनर्परिभाषित आदि के लिए जाँच की जा सकती है।
  • एक #define को जो भी अपरिभाषित किया जा सकता है, उसे फिर से परिभाषित किया जा सकता है।

आमतौर पर आपको स्टैटिक कॉस्ट पसंद करनी चाहिए। इसका कोई नुकसान नहीं है। प्रिप्रोसेसर को मुख्य रूप से सशर्त संकलन के लिए इस्तेमाल किया जाना चाहिए (और कभी-कभी वास्तव में गंदे ट्रिक्स के लिए)।


3

प्रीप्रोसेसर निर्देश #defineका उपयोग करके स्थिरांक को परिभाषित करना न केवल अंदर C++, बल्कि में भी लागू करने के लिए अनुशंसित नहीं है C। इन स्थिरांक का प्रकार नहीं होगा। यहां तक ​​कि स्थिरांक के Cलिए उपयोग करने का प्रस्ताव था const



2

हमेशा कुछ अतिरिक्त उपकरणों जैसे पूर्वप्रोसेसर पर भाषा सुविधाओं का उपयोग करना पसंद करते हैं।

ES.31: स्थिरांक या "फ़ंक्शंस" के लिए मैक्रोज़ का उपयोग न करें

मैक्रोज़ बग्स का एक प्रमुख स्रोत हैं। मैक्रो सामान्य दायरे और प्रकार के नियमों का पालन नहीं करते हैं। मैक्रोज़ तर्क पास करने के सामान्य नियमों का पालन नहीं करते हैं। मैक्रोज़ यह सुनिश्चित करते हैं कि मानव पाठक जो कुछ भी देखता है वह कंपाइलर से अलग दिखता है। मैक्रोज़ जटिल उपकरण निर्माण।

से सी ++ कोर दिशानिर्देश


0

यदि आप वर्ग के सभी उदाहरणों में साझा किए जाने के लिए एक निरंतर परिभाषित कर रहे हैं, तो स्थैतिक का उपयोग करें। यदि निरंतर प्रत्येक उदाहरण के लिए विशिष्ट है, तो बस कॉन्स्ट का उपयोग करें (लेकिन ध्यान दें कि वर्ग के सभी कंस्ट्रक्टर को इस कांस्टीट्यूशन चर को इनिशियलाइज़ेशन लिस्ट में इनिशियलाइज़ करना होगा)।

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