क्या मुझे # एल्फाइन, एनम या कॉन्स्ट का इस्तेमाल करना चाहिए?


125

C ++ प्रोजेक्ट में मैं काम कर रहा हूं, मेरे पास एक ध्वज प्रकार का मूल्य है जिसमें चार मान हो सकते हैं। उन चार झंडों को जोड़ा जा सकता है। झंडे डेटाबेस में रिकॉर्ड का वर्णन करते हैं और हो सकते हैं:

  • नया रिकॉर्ड
  • हटाए गए रिकॉर्ड
  • संशोधित रिकॉर्ड
  • मौजूदा रिकॉर्ड

अब, प्रत्येक रिकॉर्ड के लिए मैं इस विशेषता को रखना चाहता हूं, इसलिए मैं एक एनम का उपयोग कर सकता हूं:

enum { xNew, xDeleted, xModified, xExisting }

हालाँकि, कोड में अन्य स्थानों पर, मुझे यह चुनने की आवश्यकता है कि कौन से रिकॉर्ड उपयोगकर्ता को दिखाई देने वाले हैं, इसलिए मैं चाहूंगा कि इसे एक ही पैरामीटर के रूप में पास किया जा सके, जैसे:

showRecords(xNew | xDeleted);

तो, ऐसा लगता है कि मेरे पास तीन संभावित कारण हैं:

#define X_NEW      0x01
#define X_DELETED  0x02
#define X_MODIFIED 0x04
#define X_EXISTING 0x08

या

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

या

namespace RecordType {
    static const uint8 xNew = 1;
    static const uint8 xDeleted = 2;
    static const uint8 xModified = 4;
    static const uint8 xExisting = 8;
}

अंतरिक्ष की आवश्यकताएं महत्वपूर्ण हैं (बाइट बनाम इंट) लेकिन महत्वपूर्ण नहीं। परिभाषित करने के साथ मैं प्रकार की सुरक्षा खो देता हूं, और enumमैं कुछ स्थान (पूर्णांक) खो देता हूं और संभवत: जब मुझे एक बिटवाइज़ ऑपरेशन करना होता है तो मुझे कास्ट करना पड़ता है। साथ constमैं मुझे लगता है कि यह भी खो प्रकार सुरक्षा के बाद से एक यादृच्छिक uint8गलती से में मिल सकता है।

क्या कोई और क्लीनर तरीका है?

यदि नहीं, तो आप क्या उपयोग करेंगे और क्यों?

PS बाकी कोड बिना #defines के आधुनिक C ++ को साफ करते हैं , और मैंने कुछ स्थानों में नामस्थान और टेम्पलेट्स का उपयोग किया है, इसलिए वे प्रश्न से बाहर नहीं हैं।


"एनम के साथ मैं कुछ जगह (पूर्णांक) खो देता हूं"। जरुरी नहीं। देखें stackoverflow.com/questions/366017/... और stackoverflow.com/questions/1113855/... (और जीसीसी के -fshort-enum मैं इन सी जवाब अभी भी सी में सत्य हैं ++ संभालने कर रहा हूँ
idbrii

@pydave यदि आप C और C ++ की अनुकूलता के बारे में अनिश्चित हैं, तो मुझे यह लिंक बहुत उपयोगी लगी
aka.nice

3
यह उच्च वोटों वाला एक पुराना विषय है, क्या इस समस्या की स्थिति के लिए C ++ 11 एनम वर्गों का उल्लेख नहीं करने का एक कारण है।
ब्रैंडिन

एक नोट के रूप में, छोटे आकार के साथ enum RecordType : uint8_tप्रकार की सुरक्षा को जोड़ती enumहै uint8_t, हालांकि आपको अभी भी बिटवाइज़ ऑपरेटरों को प्रदान करना होगा।
जस्टिन टाइम -

जवाबों:


88

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

वैश्विक नाम स्थान को प्रदूषित करने से स्थिरांक को रोकने के लिए एक नाम स्थान में एनम रखें।

namespace RecordType {

एक एनुम घोषित किए गए संकलित समय को संकलित करता है और परिभाषित करता है। हमेशा यह सुनिश्चित करने के लिए संकलन समय प्रकार जाँच का उपयोग करें कि तर्क और चर सही प्रकार दिए गए हैं। C ++ में typedef की कोई आवश्यकता नहीं है।

enum TRecordType { xNew = 1, xDeleted = 2, xModified = 4, xExisting = 8,

अमान्य स्थिति के लिए कोई अन्य सदस्य बनाएँ। यह त्रुटि कोड के रूप में उपयोगी हो सकता है; उदाहरण के लिए, जब आप राज्य वापस करना चाहते हैं लेकिन I / O ऑपरेशन विफल हो जाता है। यह डिबगिंग के लिए भी उपयोगी है; वैरिएबल लिस्ट और डिस्ट्रक्टर्स में इसका इस्तेमाल यह जानने के लिए करें कि वेरिएबल की वैल्यू का इस्तेमाल किया जाए या नहीं।

xInvalid = 16 };

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

inline bool IsValidState( TRecordType v) {
    switch(v) { case xNew: case xDeleted: case xModified: case xExisting: return true; }
    return false;
}

 inline bool IsValidMask( TRecordType v) {
    return v >= xNew  && v < xInvalid ;
}

usingयदि आप अक्सर प्रकार का उपयोग करना चाहते हैं तो एक निर्देश जोड़ें ।

using RecordType ::TRecordType ;

मूल्य जाँच फ़ंक्शन उपयोग किए जाने के साथ ही खराब मानों को फंसाने के लिए जोर लगाने में उपयोगी होते हैं। जितनी जल्दी आप बग को पकड़ते हैं, उतना ही कम नुकसान हो सकता है।

यहाँ सभी को एक साथ रखने के लिए कुछ उदाहरण दिए गए हैं।

void showRecords(TRecordType mask) {
    assert(RecordType::IsValidMask(mask));
    // do stuff;
}

void wombleRecord(TRecord rec, TRecordType state) {
    assert(RecordType::IsValidState(state));
    if (RecordType ::xNew) {
    // ...
} in runtime

TRecordType updateRecord(TRecord rec, TRecordType newstate) {
    assert(RecordType::IsValidState(newstate));
    //...
    if (! access_was_successful) return RecordType ::xInvalid;
    return newstate;
}

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


1
ज्यादातर एक अच्छा जवाब - लेकिन सवाल यह तय करता है कि झंडे संयुक्त हो सकते हैं और IsValidState () फ़ंक्शन उन्हें संयुक्त होने की अनुमति नहीं देता है।
जोनाथन लेफ्लर 21

3
@ जोनाथन लेफ़लर: जहां से मुझे लगता है कि मुझे लगता है कि 'IsValidState' ऐसा नहीं करना चाहिए, 'IsValidMask' है।
जोओ पोर्टेला

1
क्या यह वांछित है कि IsValidMaskकोई भी (यानी 0) का चयन करने की अनुमति नहीं देता है ?
जोआचिम सॉर

2
−1 रनटाइम प्रकार की जाँच का विचार एक घृणित है।
चीयर्स एंड हीथ। - अल्फ

54

परिभाषित भूल जाओ

वे आपके कोड को प्रदूषित करेंगे।

bitfields?

struct RecordFlag {
    unsigned isnew:1, isdeleted:1, ismodified:1, isexisting:1;
};

उस का उपयोग कभी मत करो । 4 ints को कम करने की तुलना में आप गति से अधिक चिंतित हैं। बिट फ़ील्ड का उपयोग करना वास्तव में किसी अन्य प्रकार की पहुंच से धीमा है।

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

स्रोत: http://en.wikipedia.org/wiki/Bit_field :

और अगर आप अधिक कारणों की जरूरत नहीं bitfields उपयोग करते हैं, शायद रेमंड चेन आप अपने में समझाने होगा द ओल्ड नई बात : पोस्ट बूलियन्स का एक संग्रह के लिए bitfields की लागत लाभ विश्लेषण पर http://blogs.msdn.com/oldnewthing/ संग्रह / 2008/11/26 / 9143050.aspx

const int?

namespace RecordType {
    static const uint8 xNew = 1;
    static const uint8 xDeleted = 2;
    static const uint8 xModified = 4;
    static const uint8 xExisting = 8;
}

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

आह, हाँ: स्थैतिक कीवर्ड को हटा दें । जब आप करते हैं तो C ++ में स्टेटिक को हटा दिया जाता है, और यदि uint8 एक बिल्डिन प्रकार है, तो आपको एक ही मॉड्यूल के कई स्रोतों द्वारा शामिल हेडर में इसे घोषित करने की आवश्यकता नहीं होगी। अंत में, कोड होना चाहिए:

namespace RecordType {
    const uint8 xNew = 1;
    const uint8 xDeleted = 2;
    const uint8 xModified = 4;
    const uint8 xExisting = 8;
}

इस दृष्टिकोण की समस्या यह है कि आपका कोड आपके स्थिरांक का मूल्य जानता है, जो युग्मन को थोड़ा बढ़ाता है।

enum

कुछ हद तक मजबूत टाइपिंग के साथ कास्ट इंट।

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

वे अभी भी वैश्विक नाम स्थान को प्रदूषित कर रहे हैं। वैसे ... टाइप्डिफ को हटा दें । आप C ++ में काम कर रहे हैं। एनम और स्ट्रक्चर के वे टाइपराइफ कुछ और से कोड को प्रदूषित कर रहे हैं।

परिणाम थोड़े है:

enum RecordType { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } ;

void doSomething(RecordType p_eMyEnum)
{
   if(p_eMyEnum == xNew)
   {
       // etc.
   }
}

जैसा कि आप देख रहे हैं, आपकी दुश्मनी वैश्विक नाम स्थान को प्रदूषित कर रही है। यदि आप इस एनम को नाम स्थान में रखते हैं, तो आपके पास कुछ इस तरह होगा:

namespace RecordType {
   enum Value { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } ;
}

void doSomething(RecordType::Value p_eMyEnum)
{
   if(p_eMyEnum == RecordType::xNew)
   {
       // etc.
   }
}

बाहरी कास्ट इंट?

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

// Header.hpp
namespace RecordType {
    extern const uint8 xNew ;
    extern const uint8 xDeleted ;
    extern const uint8 xModified ;
    extern const uint8 xExisting ;
}

तथा:

// Source.hpp
namespace RecordType {
    const uint8 xNew = 1;
    const uint8 xDeleted = 2;
    const uint8 xModified = 4;
    const uint8 xExisting = 8;
}

आप उन स्थिरांक पर स्विच का उपयोग नहीं कर पाएंगे, हालांकि। तो अंत में, अपना जहर उठाओ ...: -प


5
आपको क्यों लगता है कि बिटफिल्ड धीमी है? क्या आपने वास्तव में इसे और किसी अन्य विधि का उपयोग करके कोड को प्रोफाइल किया है? यहां तक ​​कि अगर यह है, तो स्पष्टता गति से अधिक महत्वपूर्ण हो सकती है, जिससे "कभी भी उपयोग न करें" थोड़ा सरल हो जाता है।
12

"स्थिर कास्ट uint8 xNew;" केवल निरर्थक है क्योंकि C ++ const namespace-scoped variables default to internal linkage। "कॉन्स्ट" निकालें और इसमें बाहरी लिंकेज है। इसके अलावा, "enum {...} RecordType;" एक वैश्विक वैरिएबल घोषित करता है जिसका नाम "रिकॉर्ड टाइप" है जिसका प्रकार एक गुमनाम एनम है।
bk1e

onebyone: सबसे पहले, मुख्य कारण यह था कि लाभ (कुछ बाइट्स, यदि कोई हो) को नुकसान से उखाड़ फेंका गया था (उपयोग करने के लिए धीमा, दोनों पढ़ें और लिखें) ...
17

3
onebyone: दूसरा, मैं या घर पर काम करने वाले सभी कोड स्वाभाविक रूप से थ्रेड सुरक्षित हैं। यह करना आसान है: कोई ग्लोबल्स, कोई स्थिर नहीं, थ्रेड्स के बीच साझा नहीं किया जाता जब तक कि लॉक-प्रोटेक्टेड न हो। इस मुहावरे के प्रयोग से यह मूल सूत्र-सुरक्षा टूट जाएगी। और किस लिए? कुछ बाइट्स शायद ! ... :-) ...
17

बिटमंड्स की छिपी लागत पर रेमंड चेन के लेख के संदर्भ में जोड़ा गया।
पियरसबल 21

30

क्या आपने एसटीडी :: बिटसेट से इंकार किया है? झंडे के सेट यह किसके लिए है। करना

typedef std::bitset<4> RecordType;

फिर

static const RecordType xNew(1);
static const RecordType xDeleted(2);
static const RecordType xModified(4);
static const RecordType xExisting(8);

क्योंकि बिटसेट के लिए ऑपरेटर ओवरलोड का एक गुच्छा है, अब आप कर सकते हैं

RecordType rt = whatever;      // unsigned long or RecordType expression
rt |= xNew;                    // set 
rt &= ~xDeleted;               // clear 
if ((rt & xModified) != 0) ... // test

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

मान लें कि आप bitset की संभावना से इनकार कर दिया है, मैं के लिए मतदान enum

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

मैं enum के किसी भी अंतरिक्ष लागत के बारे में संदिग्ध हूँ। uint8 वैरिएबल और पैरामीटर शायद किसी भी स्टैक की तुलना में कम स्टैक का उपयोग नहीं करेंगे, इसलिए केवल कक्षाओं में भंडारण। ऐसे कुछ मामले हैं जहां एक संरचना में कई बाइट्स पैक करने से जीत होगी (जिस स्थिति में आप enint को uint8 स्टोरेज से बाहर और अंदर डाल सकते हैं), लेकिन सामान्य रूप से पैडिंग किसी भी तरह से लाभ को मार देगा।

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

वरीयता के लिए, मैं "= 2" को एनम में डालूंगा, वैसे। यह आवश्यक नहीं है, लेकिन "कम से कम विस्मय का सिद्धांत" बताता है कि सभी 4 परिभाषाएं समान दिखनी चाहिए।


1
दरअसल, मैं बिटसेट बिल्कुल नहीं मानता था। हालांकि, मुझे यकीन नहीं है कि यह अच्छा होगा। बिटसेट के साथ, मुझे 1, 2, 3, 4 के रूप में बिट्स को संबोधित करना होगा जो कोड को कम पठनीय बना देगा - जिसका अर्थ है कि मैं बिट्स को 'नाम' देने के लिए एक एनम का उपयोग करूंगा। हालांकि एक अंतरिक्ष सेवर हो सकता है। धन्यवाद।
मिलान बाबूसकोव

मिलान, आपको एक एनम का उपयोग करके बिट्स को "नाम" देने की ज़रूरत नहीं है, आप बस पूर्वनिर्धारित बिट्स का उपयोग कर सकते हैं जैसा कि ऊपर दिखाया गया है। यदि आप my_bitset.flip (1) के बजाय बिट को चालू करना चाहते हैं, तो आप my_bitet | = xNew करेंगे;
मस्जिद

यह आपको कम और एसटीएल में अधिक पर निर्देशित किया गया है, लेकिन: मुझे वास्तव में पूछना होगा: आप क्यों उपयोग करेंगे bitset ? यह आमतौर पर एक long(मेरे कार्यान्वयन iirc में? हां, कितना बेकार) या वैसे ही प्रत्येक तत्व के लिए समान अभिन्न प्रकार का अनुवाद करता है, इसलिए केवल अनबॉर्स्ड इंटीग्रल्स का उपयोग क्यों न करें? (या, आजकल, constexprशून्य भंडारण के साथ)
अंडरस्कोर_ड

[टाइमआउट संपादित करें] ... लेकिन तब मैं वास्तव में bitsetवर्ग के लिए तर्क को कभी नहीं समझ पाया , इसके अलावा जो कुछ भी हो रहा है, वह है 'उह, के आसपास की चर्चाओं में एक आवर्ती अवरोही, हमें भाषा के निचले स्तर की जड़ों को कवर करना चाहिए '
अंडरस्कोर_ड

" uint8चर और पैरामीटर शायद किसी भी कम स्टैक का उपयोग नहीं करेंगे ints" गलत है। यदि आपके पास 8-बिट रजिस्टर के साथ एक सीपीयू है , तो intकम से कम 2 रजिस्टरों की uint8_tआवश्यकता है जबकि केवल 1 की आवश्यकता है, इसलिए आपको अधिक स्टैक स्थान की आवश्यकता होगी क्योंकि आप रजिस्टरों से बाहर होने की अधिक संभावना रखते हैं (जो धीमी भी है और कोड आकार बढ़ा सकते हैं ( अनुदेश सेट पर निर्भर करता है))। (आपके पास एक प्रकार है, यह uint8_tनहीं होना चाहिए uint8)
12431234123412341234123

8

यहाँ कांस्ट बनाम मैक्रोज़ बनाम एनमों पर कुछ लेख हैं:

प्रतीकात्मक स्थिरांक
संकेंद्रित स्थिरांक बनाम स्थिर वस्तु

मुझे लगता है कि आपको विशेष रूप से मैक्रोज़ से बचना चाहिए क्योंकि आपने लिखा था कि आपका अधिकांश नया कोड आधुनिक C ++ में है।


5

यदि संभव हो तो मैक्रोज़ का उपयोग न करें। जब वे आधुनिक C ++ की बात करते हैं तो उनकी बहुत प्रशंसा नहीं होती है।


4
सच। मुझे खुद मैक्रों से नफरत है कि अगर आप गलत हैं तो आप उनमें कदम नहीं रख सकते।
कार्ल

मुझे लगता है कि ऐसा कुछ है जो संकलक में तय किया जा सकता है।
केल्टिकमिस्ट्रेल

4

एनम अधिक उपयुक्त होंगे क्योंकि वे "पहचानकर्ताओं को अर्थ" के साथ-साथ सुरक्षा प्रदान करते हैं। आप स्पष्ट रूप से बता सकते हैं कि "xDeleted" "RecordType" का है और यह वर्षों के बाद भी "रिकॉर्ड का प्रकार" (वाह!) का प्रतिनिधित्व करता है। कॉन्स को इसके लिए टिप्पणियों की आवश्यकता होगी, साथ ही उन्हें कोड में ऊपर और नीचे जाने की आवश्यकता होगी।


4

परिभाषित के साथ मैं टाइप सुरक्षा खो देता हूं

जरुरी नहीं...

// signed defines
#define X_NEW      0x01u
#define X_NEW      (unsigned(0x01))  // if you find this more readable...

और एनम के साथ मैं कुछ स्थान खो देता हूं (पूर्णांक)

जरूरी नहीं - लेकिन आपको भंडारण के बिंदुओं पर स्पष्ट होना होगा ...

struct X
{
    RecordType recordType : 4;  // use exactly 4 bits...
    RecordType recordType2 : 4;  // use another 4 bits, typically in the same byte
    // of course, the overall record size may still be padded...
};

और शायद तब डालना होगा जब मैं बिटवाइज़ ऑपरेशन करना चाहता हूँ।

आप दर्द से बाहर निकालने के लिए ऑपरेटर बना सकते हैं:

RecordType operator|(RecordType lhs, RecordType rhs)
{
    return RecordType((unsigned)lhs | (unsigned)rhs);
}

कास्ट के साथ मुझे लगता है कि मैं भी एक प्रकार की सुरक्षा खो देता हूं क्योंकि एक यादृच्छिक uint8 गलती से मिल सकता है।

इनमें से किसी भी तंत्र के साथ भी ऐसा ही हो सकता है: रेंज और वैल्यू चेक आमतौर पर सेफ्टी टू टाइप सेफ्टी होते हैं (हालांकि यूजर-डिफाइंड-टाइप- यानी आपकी खुद की क्लासेस - अपने डेटा के बारे में "इनवेंटरी" लागू कर सकती हैं)। Enums के साथ, मूल्यों को होस्ट करने के लिए एक बड़ा प्रकार चुनने के लिए संकलक का मुफ्त, और एक अनइंस्टाल्यूटेड, दूषित या बस मिस-सेट एनम चर अभी भी अपने बिट पैटर्न की व्याख्या कर सकता है एक संख्या के रूप में आप अपेक्षा नहीं करेंगे - किसी से भी असमान की तुलना करना गणना पहचानकर्ता, उनमें से कोई भी संयोजन, और 0।

क्या कोई और क्लीनर तरीका है? / यदि नहीं, तो आप क्या उपयोग करेंगे और क्यों?

ठीक है, अंत में कोशिश की और विश्वसनीय सी-स्टाइल बिटवाइज़ या एनुमरेशन्स बहुत अच्छी तरह से काम करता है जब आपके पास तस्वीर में बिट फ़ील्ड और कस्टम ऑपरेटर होते हैं। आप कुछ कस्टम सत्यापन कार्यों और अभिकथन के साथ अपनी मजबूती को और बेहतर बना सकते हैं जैसा कि mat_geek के उत्तर में है; तकनीक अक्सर स्ट्रिंग, इंट, डबल मान आदि को संभालने के लिए समान रूप से लागू होती है।

आप तर्क दे सकते हैं कि यह "क्लीनर" है:

enum RecordType { New, Deleted, Modified, Existing };

showRecords([](RecordType r) { return r == New || r == Deleted; });

मैं उदासीन हूं: डेटा बिट्स तंग करते हैं, लेकिन कोड काफी बढ़ता है ... यह निर्भर करता है कि आपको कितनी वस्तुएं मिली हैं, और लाम्बास - सुंदर वे जैसे हैं - अभी भी गड़बड़ हैं और बिटवाइज ओआरएस की तुलना में सही पाने के लिए कठिन हैं।

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


1
+1 ठीक है। लेकिन निर्देश से पहले operator|एक पूर्णांक प्रकार ( unsigned int) में डालना चाहिए |। एल्स operator|वसीयत को अपने आप कहेंगे और रन-टाइम स्टैक ओवरफ्लो का कारण बनेंगे। मेरा सुझाव है: return RecordType( unsigned(lhs) | unsigned(rhs) );। चीयर्स
ओलिब्रे

3

यहां तक ​​कि अगर आपको एक एनम को स्टोर करने के लिए 4 बाइट का उपयोग करना है (मैं C ++ से परिचित नहीं हूं - मुझे पता है कि आप अंतर्निहित प्रकार को C # में निर्दिष्ट कर सकते हैं), यह अभी भी इसके लायक है - एनम का उपयोग करें।

जीबी के मेमोरी वाले सर्वरों की इस दिन और उम्र में, 4 बाइट्स बनाम 1 बाइट की मेमोरी जैसी चीजें सामान्य तौर पर एप्लिकेशन स्तर पर नहीं होती हैं। बेशक, यदि आपकी विशेष स्थिति में, मेमोरी का उपयोग महत्वपूर्ण है (और एनम को वापस करने के लिए बाइट का उपयोग करने के लिए आपको C ++ नहीं मिल सकता है), तो आप 'स्टेटिक कास्ट' मार्ग पर विचार कर सकते हैं।

दिन के अंत में, आपको अपने आप से पूछना होगा कि क्या यह आपके डेटा संरचना के लिए 3 बाइट्स मेमोरी सेविंग के लिए 'स्टैटिक कॉस्ट' का उपयोग करने के रखरखाव हिट के लायक है?

कुछ और ध्यान में रखने के लिए - IIRC, x86 पर, डेटा संरचनाएं 4-बाइट संरेखित हैं, इसलिए जब तक आपके 'रिकॉर्ड' संरचना में बाइट-चौड़ाई वाले तत्वों की संख्या नहीं है, यह वास्तव में कोई फर्क नहीं पड़ता। परीक्षण / सुनिश्चित करें कि प्रदर्शन / स्थान के लिए स्थिरता में ट्रेडऑफ़ बनाने से पहले यह सुनिश्चित करता है।


आप C ++ में अंतर्निहित प्रकार निर्दिष्ट कर सकते हैं, जैसा कि भाषा संशोधन C ++ 11। तब तक, मेरा मानना ​​है कि यह "स्टोर करने के लिए कम से कम काफी बड़ा था और सभी निर्दिष्ट प्रगणकों के लिए एक बिट फ़ील्ड के रूप में उपयोग किया जाता है, लेकिन शायद intजब तक कि यह बहुत छोटा न हो"। [यदि आप C ++ 11 में अंतर्निहित प्रकार निर्दिष्ट नहीं करते हैं, तो यह विरासत व्यवहार का उपयोग करता है। इसके विपरीत, C ++ 11 enum classका अंतर्निहित प्रकार स्पष्ट रूप से चूक के लिए है intयदि अन्यथा निर्दिष्ट नहीं किया गया है।]
जस्टिन टाइम -

3

यदि आप गणन सिंटैक्स और बिट चेकिंग की सुविधा के साथ कक्षाओं की प्रकार की सुरक्षा चाहते हैं, तो C ++ में सुरक्षित लेबल पर विचार करें । मैंने लेखक के साथ काम किया है, और वह बहुत स्मार्ट है।

हालांकि, खबरदार। अंत में, यह पैकेज टेम्पलेट और मैक्रोज़ का उपयोग करता है !


मेरे छोटे से ऐप के लिए ओवरकिल जैसा दिखता है। लेकिन यह एक अच्छा समाधान की तरह लगता है।
मिलान बाबूसकोव

2

क्या आपको वास्तव में वैचारिक मूल्यों के रूप में ध्वज मूल्यों के आसपास से गुजरने की आवश्यकता है, या क्या आप प्रति-ध्वज कोड का एक बहुत कुछ करने जा रहे हैं? किसी भी तरह से, मुझे लगता है कि यह 1-बिट बिटफील्ड्स के वर्ग या संरचना के रूप में होने से वास्तव में स्पष्ट हो सकता है:

struct RecordFlag {
    unsigned isnew:1, isdeleted:1, ismodified:1, isexisting:1;
};

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


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

ठीक है, जब अलग है, बस एक int के लिए पूछना। एक साथ होने पर, संरचना को पास करें।
wnoise

यह बेहतर नहीं होगा। बिट फ़ील्ड की पहुंच किसी भी चीज़ की तुलना में धीमी होती है।
पियरसेबल

वास्तव में? आपको लगता है कि संकलक मैनुअल बिट-ट्विडलिंग की तुलना में बिट-फील्ड के परीक्षण के लिए काफी अलग कोड उत्पन्न करेगा? और यह काफी धीमी हो जाएगी? क्यों? एक बात जो आप मुहावरेदार नहीं कर सकते, इतनी आसानी से एक ही बार में कई झंडे गाड़ सकते हैं।
wnoise

एक साधारण रीडिंग टेस्ट चलाने से मुझे बिट-फील्ड एक्सेस के लिए 5.45-5.59 बिट-मास्किंग के लिए 5.50-5.58 सेकंड मिलते हैं। बहुत अधिक अप्रभेद्य।
wnoise

2

मैं शायद इस तरह के लिए एक एनम का उपयोग नहीं करूंगा जहां मूल्यों को एक साथ जोड़ा जा सकता है, अधिक सामान्यतः एनम पारस्परिक रूप से अनन्य राज्य हैं।

लेकिन जो भी विधि आप उपयोग करते हैं, उसे और अधिक स्पष्ट करने के लिए कि ये ऐसे मान हैं जो बिट्स हैं जिन्हें एक साथ जोड़ा जा सकता है, इस सिंटैक्स का उपयोग वास्तविक मानों के लिए करें:

#define X_NEW      (1 << 0)
#define X_DELETED  (1 << 1)
#define X_MODIFIED (1 << 2)
#define X_EXISTING (1 << 3)

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


1
इसके लिए पर्याप्त मिसाल है, विशेष रूप से ioctl () के लिए। मैं हेक्स स्थिरांक का उपयोग करना पसंद करता हूं, हालांकि: 0x01, 0x02, 0x04, 0x08, 0x10, ...
जोनाथन लेफ़लर

2

के आधार पर KISS , उच्च एकता और कम युग्मन , इन सवालों को पूछने -

  • किसको जानना है? मेरी कक्षा, मेरी लाइब्रेरी, अन्य कक्षाएं, अन्य पुस्तकालय, तीसरी पार्टियाँ
  • मुझे किस स्तर का अमूर्त प्रदान करने की आवश्यकता है? क्या उपभोक्ता बिट संचालन को समझता है।
  • क्या मुझे VB / C # आदि से इंटरफ़ेस करना होगा?

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


1
a) 5-6 कक्षाएं। बी) केवल मेरे लिए, यह एक वन-मैन प्रोजेक्ट है c) कोई
इंटरस्पेसिंग

2

यदि आप Qt का उपयोग कर रहे हैं तो आपको QFlags के लिए एक नज़र होनी चाहिए । QFlags वर्ग एक प्रकार-सुरक्षित तरीका प्रदान करता है जो एनरम मूल्यों के OR-संयोजन को संग्रहीत करता है।


नहीं, नहीं क्यूटी। दरअसल, यह एक wxWidgets प्रोजेक्ट है।
मिलन बाबुकोव

0

मैं बल्कि साथ जाना होगा

typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;

सिर्फ इसलिए कि:

  1. यह क्लीनर है और यह कोड को पठनीय और रखरखाव योग्य बनाता है।
  2. यह तार्किक रूप से स्थिरांक को समूहित करता है।
  3. जब तक आपके काम प्रोग्रामर समय, अधिक महत्वपूर्ण है है उन 3 बाइट्स को बचाने के लिए।

खैर, मैं आसानी से क्लास रिकॉर्ड के एक लाख उदाहरण दे सकता था, इसलिए यह महत्वपूर्ण हो सकता है। OTOH, यह केवल 1MB और 4MB का अंतर है, इसलिए शायद मुझे चिंता नहीं करनी चाहिए।
मिलान बाबूसकोव

@Vivek: क्या आपने पूर्णांक चौड़ाई सीमा पर विचार किया है? विशेष रूप से C ++ 11 से पहले।
user2672165

0

ऐसा नहीं है कि मुझे सब कुछ इंजीनियर करना पसंद है, लेकिन कभी-कभी इन मामलों में यह जानकारी छोटा करने के लिए एक (छोटा) वर्ग बनाने के लायक हो सकता है। यदि आप एक क्लास रिकॉर्ड टाइप बनाते हैं तो इसके कुछ कार्य हो सकते हैं जैसे:

शून्य सेटडलेट ();

शून्य साफ़ किया हुआ ();

बूल को हटा दिया गया है ();

आदि ... (या जो भी सम्मेलन सूट करता है)

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

वर्ग आपको प्रत्येक राज्य में सार्थक लॉगिंग जानकारी संलग्न करने की क्षमता भी दे सकता है, आप वर्तमान राज्य आदि के स्ट्रिंग प्रतिनिधित्व को वापस करने के लिए एक फ़ंक्शन जोड़ सकते हैं (या स्ट्रीमिंग ऑपरेटर '<<' का उपयोग कर सकते हैं)।

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

यदि आप शीर्ष फ़ाइल के बजाय सीपीपी फ़ाइल के अंदर एक अनाम नेमस्पेस में हैं, तो वास्तविक 'बिट' दुनिया के बाकी हिस्सों में दिखाई नहीं दे सकते हैं।

यदि आप पाते हैं कि अवैध संयोजन, लॉगिंग आदि से निपटने के लिए enum / # define / bitmask आदि का उपयोग करने वाले कोड में बहुत सारे 'सपोर्ट' कोड हैं, तो क्लास में एनकैप्सुलेशन विचार करने लायक हो सकता है। बेशक ज्यादातर समय सरल समस्याओं को सरल समाधान के साथ बेहतर किया जाता है ...


दुर्भाग्य से, घोषणा को .h फ़ाइल में होना चाहिए क्योंकि यह परियोजना में उपयोग किया जाता है (कुछ 5-6 कक्षाओं द्वारा उपयोग किया जाता है)।
मिलान बाबूसकोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.