2009-11-12 9 views
5

मैं हाल ही में कक्षाओं में आया हूं जो कॉन्फ़िगरेशन के लिए सामान्य सेटटर विधियों के बजाय कॉन्फ़िगरेशन ऑब्जेक्ट का उपयोग करते हैं। एक छोटा सा उदाहरण:कॉन्फ़िगरेशन स्ट्रक्चर बनाम सेटर्स

class A { 
    int a, b; 
public: 
    A(const AConfiguration& conf) { a = conf.a; b = conf.b; } 
}; 

struct AConfiguration { int a, b; }; 

तेजी:

  • आप अपने वस्तु का विस्तार कर सकते हैं और आसानी से अपने उपयोगकर्ताओं को कभी इसके बारे में पता करने के लिए की जरूरत के बिना नए मूल्यों के लिए उचित मूलभूत मूल्यों की गारंटी।
  • आप स्थिरता के लिए कॉन्फ़िगरेशन की जांच कर सकते हैं (उदा। आपकी कक्षा केवल मूल्यों के कुछ संयोजनों की अनुमति देती है)
  • आप सेटर्स को ओमेट करके बहुत सारे कोड को सहेजते हैं।
  • आपको अपने कॉन्फ़िगरेशन स्ट्रक्चर के लिए डिफ़ॉल्ट कन्स्ट्रक्टर निर्दिष्ट करने के लिए एक डिफ़ॉल्ट कन्स्ट्रक्टर मिलता है और A(const AConfiguration& conf = AConfiguration()) का उपयोग करें।

नकारात्मक पक्ष (ओं):

  • आप निर्माण समय विन्यास पता करने की जरूरत है और बाद में इसे बदल नहीं सकते हैं।

क्या मुझे याद आ रही है कि इसके लिए और अधिक डाउनसाइड्स हैं? यदि नहीं हैं: यह अधिक बार क्यों उपयोग नहीं किया जाता है?

उत्तर

6

चाहे आप डेटा व्यक्तिगत रूप से या प्रति struct पारित शैली का सवाल है और मामला-दर-मामला आधार पर निर्णय लिया जाना चाहिए।

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

A(const AConfiguration& conf) : a(conf.a), b(conf.b) {} 

या

A(int a_, int b_) : a(a_), b(b_) {} 

सब इतना बात नहीं करता है। (जहां हर कोई पूर्व पसंद करेंगे मानकों की एक संख्या है, लेकिन जो संख्या यह है - और कि क्या इस तरह के एक वर्ग अच्छी तरह से बनाया गया है - यह बहस का मुद्दा है।) हालांकि, कि क्या मैं इस

A a1(Configuration(42,42)); 
A a2 = Configuration(4711,4711); 
A a3(7,7); 

या की तरह वस्तु का उपयोग कर सकते इस

A urgh; 
urgh.setA(13); 
urgh.setB(13); 

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

1

इस विधि का उपयोग बाइनरी संगतता को कठिन बनाता है।

यदि संरचना बदल दी गई है (एक नया वैकल्पिक फ़ील्ड जोड़ा गया है), कक्षा का उपयोग कर सभी कोड को एक पुन: संकलन की आवश्यकता हो सकती है। यदि एक नया गैर वर्चुअल सेटर फ़ंक्शन जोड़ा जाता है, तो ऐसा कोई पुनर्मूल्यांकन आवश्यक नहीं है।

+0

मैंने हाल ही में इसे पुराने कोड-बेस के साथ चलाया है जिसका मैं समर्थन कर रहा हूं। यह काफी परेशान है। –

+0

विंडोज एपीआई आकार आकार जोड़कर यह ठीक करता है, यह सुनिश्चित करता है कि structs पीओडी हैं, और अंत में केवल डेटा फ़ील्ड जोड़कर। नए structs (नए क्षेत्रों के लिए उचित डिफ़ॉल्ट मान के साथ) बनाने के लिए पुरानी रचनाकारों का समर्थन जोड़ें और संगतता वास्तव में _increased_ है। – sbi

+0

मैंने एक दर्पण उत्तर लिखा, जहां मैं दिखाता हूं कि इसके बजाय संगतता बढ़ी है। –

2

मुख्य उलझन यह है कि एक वस्तु अतुलनीय हो सकती है। मुझे नहीं पता कि AConfiguration stuct वास्तव में केवल एक ए और बी पैरामीटर पर किसी भी लाभ देता है।

4

इस विधि का उपयोग बाइनरी संगतता को आसान बनाता है।

जब पुस्तकालय संस्करण परिवर्तन और अगर विन्यास struct यह होता है, तो निर्माता भेद कर सकते हैं कि क्या "पुराने" या "नया" विन्यास पारित हो जाता है और "पहुँच उल्लंघन"/"segfault" से बचने जब गैर existant क्षेत्रों तक पहुँचने।

इसके अलावा, कन्स्ट्रक्टर का उलझन नाम बरकरार रखा गया है, जो इसके हस्ताक्षर को बदल देता है तो बदल जाता। यह हमें बाइनरी संगतता बरकरार रखने देता है।

उदाहरण:

//version 1 
struct AConfiguration { int version; int a; AConfiguration(): version(1) {} }; 
//version 2 
struct AConfiguration { int version; int a, b; AConfiguration(): version(2) {} }; 

class A { 
    A(const AConfiguration& conf) { 
    switch (conf.version){ 
     case 1: a = conf.a; b = 0; // No access violation for old callers! 
     break; 
     case 2: a = conf.a; b = conf.b; // New callers do have b member 
     break; 
    } 
    } 
}; 
0

मैं यहां कम बाइनरी संगतता का समर्थन करता हूं।

समस्या जो मैं देखता हूं वह संरचना क्षेत्रों तक सीधे पहुंच से आता है।

struct AConfig1 { int a; int b; }; 
struct AConfig2 { int a; std::map<int,int> b; } 

जब से मैं b के प्रतिनिधित्व संशोधित, मैं खराब कर रहा हूँ, जबकि साथ:

class AConfig1 { public: int getA() const; int getB() const; /* */ }; 
class AConfig2 { public: int getA() const; int getB(int key = 0) const; /* */ }; 

वस्तु के भौतिक लेआउट परिवर्तन हो सकता है, लेकिन मेरे ही टिककर खेल नहीं है और कार्यों के लिए ऑफसेट या तो नहीं है

बेशक, बाइनरी संगतता के लिए, PIMPL मुहावरे को देखना चाहिए।

namespace details { class AConfigurationImpl; } 

class AConfiguration { 
public: 
    int getA() const; 
    int getB() const; 
private: 
    AConfigurationImpl* m_impl; 
}; 

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

स्मृति तरीकों की संख्या पर निर्भर नहीं करता में एक उदाहरण के प्रतिनिधित्व, यह केवल पर निर्भर करता है: उपस्थिति या आभासी तरीकों के अभाव

  • आधार वर्ग
  • विशेषताओं

विज़िबल क्या है (जो पहुंच योग्य नहीं है)।

और यहां हम गारंटी देते हैं कि हमारे पास विशेषताओं में कोई बदलाव नहीं होगा। AConfigurationImpl की परिभाषा किसी भी समस्या के बिना बदल सकती है और विधियों के कार्यान्वयन भी बदल सकते हैं।

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

चाहे यह आपके अनुरूप हो या नहीं, आप निर्णय लेने के लिए अपने आप हैं।

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