2010-07-15 16 views
11

के जटिल आरंभीकरण इस तरह एक वर्ग पर विचार करें:स्थिरांक क्षेत्रों

class MyReferenceClass 
{ 
public: 
    MyReferenceClass(); 
    const double ImportantConstant1; 
    const double ImportantConstant2; 
    const double ImportantConstant3; 
private: 
    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3); 
} 

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

क्या कक्षा के संबंधित दोहरे क्षेत्रों में इन गणना मूल्यों को स्टोर करने का कोई समझदार तरीका है?

यदि नहीं, तो क्या आप सी ++ में ऐसी कक्षा घोषित करने का एक और प्राकृतिक तरीका सुझा सकते हैं?

सी # में मैं एक स्थिर श्रेणी के साथ एक स्थैतिक श्रेणी का उपयोग करता हूं, लेकिन यह सी ++ में कोई विकल्प नहीं है। मैंने महत्वपूर्ण कॉन्स्टेंट 1..3 या तो गैर-कॉन्स फ़ील्ड या फ़ंक्शन कॉल करने पर विचार किया है, लेकिन दोनों कम दिखते हैं।

मुझे मिले कॉन्स्ट फ़ील्ड को शुरू करने का एकमात्र तरीका use initializer lists है, लेकिन ऐसी सूची में बहु-आउटपुट गणना के परिणाम पास करना संभव नहीं लगता है।

+0

यदि यह संभव है, तो क्या आप बता सकते हैं कि 'ComputeImportantConstants' कैसे लागू किया गया है? क्या यह काफी लंबा है? तीन स्थिरांक कैसे बातचीत करते हैं, अन्य कारक कैसे शामिल होते हैं? –

उत्तर

9

तुम क्यों नहीं कर सकते:

MyReferenceClass ComputeImportantConstants(){ 
    //stuff to compute 
    return MyReferenceClass(const1, const2, const3); 
} 

MyReferenceClass{ 
public: 
    MyReferenceClass(double _1, double _2, double _3) 
     : m_Const1(_1), 
     m_Const2(_2), 
     m_Const3(_3){} 

    double getImportantConst1() const { return m_Const1; } 
    double getImportantConst2() const { return m_Const2; } 
    double getImportantConst3() const { return m_Const3; } 
private: 
    const double m_Const1, 
       m_Const2, 
       m_Const3; 
}; 
कि जैसा

और एक कारखाने समारोह में calculate समारोह बारी है?

+0

एक सुधार विचार: ComputeImportantConstants() में एक स्थिर चर बनायें और सबकुछ गणना होने के बाद इस चर को वापस कर दें। इस तरह, ComputeImportantConstants के बाद की कॉल एक अतिरिक्त गणना ट्रिगर नहीं करते हैं। –

+1

सी ++ नाइटपिक: किसी फ़ंक्शन से 'कॉन्स्ट डबल' लौटने से ज्यादा समझ नहीं आती है। यह कॉलर के लिए सुरक्षा में सुधार किए बिना केवल ज़्यादा ज़रूरी ज़रूरी बनाता है। सदस्य चर सभी के बाद मूल्य से वापस आते हैं। –

+0

अच्छा नाइटपिक और आप सही हैं। – wheaties

5

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

class A 
private: 
    double important1; 
    double important2; 
    double important3; 
    A() { ComputeImportantConstants(); } //no need for parameters, it accesses the members 
    void ComputeImportantConstants(); 
public: 
    inline double GetImportant1() { return important1; } 
    inline double GetImportant2() { return important2; } 
    inline double GetImportant3() { return important3; } 
}; 

आप अभी भी इसे सिंगलटन या तो किसी तरह का बनाने के द्वारा इस वर्ग में सुधार कर सकते हैं:

दूसरा:

कुछ इस तरह से करते हैं।

3

आप एक आधार वर्ग के लिए const क्षेत्रों चला जाता, और फिर उन्हें प्रारंभ करने में एक आवरण वर्ग पारित:

class MyBase 
{ 
protected: 
    const double ImportantConstant1; 
    const double ImportantConstant2; 
    const double ImportantConstant3; 

    struct Initializer 
    { 
     double d1; 
     double d2; 
     double d3; 
    }; 

    MyBase(Initializer const& i): 
     ImportantConstant1(i.d1),ImportantConstant2(i.d2),ImportantConstant3(i.d3) 
    {} 
}; 

class MyReferenceClass: 
    private MyBase 
{ 
public: 
    using MyBase::ImportantConstant1; 
    using MyBase::ImportantConstant2; 
    using MyBase::ImportantConstant3; 
    MyReferenceClass(): 
     MyBase(makeInitializer()) 
    {} 

private: 
    MyBase::Initializer makeInitializer() 
    { 
     MyBase::Initializer i; 
     ComputeImportantConstants(&i.d1,&i.d2,&i.d3); 
     return i; 
    } 

    void ComputeImportantConstants(double *out_const1, double *out_const2, double *out_const3); 
}; 
+0

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

2

एक ही रास्ता स्थिरांक क्षेत्रों मैंने पाया कि प्रारंभकर्ता सूचियों का उपयोग करने के लिए है प्रारंभ करने में, लेकिन ऐसी सूची में बहु-आउटपुट गणना के परिणामों को पारित करना संभव नहीं लगता है।

यह सच है; हालांकि, आप एक सदस्य को प्रारंभ कर सकते हैं - जो स्थिरांक की संरचना है। निचे देखो।

मैंने भी महत्वपूर्ण कॉन्स्टेंट 1..3 को गैर-कॉन्स फ़ील्ड या फ़ंक्शन कॉल करने पर विचार किया है, लेकिन दोनों कम दिखते हैं।

मुझे नहीं लगता कि गेटर फ़ंक्शन कम होंगे। कंपाइलर सबसे अधिक संभावना है कि इनलाइन।इस पर विचार करें:

class MyReferenceClass 
{ 
public: 
    MyReferenceClass() : m_constants(ComputeImportantConstants()) { } 

    inline double ImportantConstant1() const { return m_constants.c1; } 
    inline double ImportantConstant2() const { return m_constants.c2; } 
    inline double ImportantConstant3() const { return m_constants.c3; } 

private: 
    struct Constants { 
     Constants(double c1_, double c2_, double c3_) : c1(c1_), c2(c2_), c3(c3_) { } 

     const double c1; 
     const double c2; 
     const double c3; 
    }; 

    Constants ComputeImportantConstants() { 
     return Constants(1.0, 2.0, 3.0); 
    } 

    const Constants m_constants; 
}; 

m_constants के बाद से और साथ ही सभी अपने क्षेत्रों में लगातार कर रहे हैं, मानों अन्य सदस्य तरीके से नहीं बदला जा सकता है - बस कोड आप अपने प्रश्न में स्केच में। एक प्रारंभिक हो सकता है क्योंकि हम एक मूल्य को प्रारंभ करते हैं: एक संरचना।

स्थिरांक तक पहुंच (सबसे अधिक संभावना) पहले की तरह कुशल होने के लिए है: कार्यों को रेखांकित करने का सुझाव और संकलक करने की संभावना है ताकि गेटर्स कितने छोटे हों।

1

बस बात यह है कि प्रारंभ और जटिल हिस्सा है, और प्रतिलिपि निर्माता के माध्यम से जटिल हिस्सा प्रारंभ करने में सरल है में बात अलग हो:

// here's the part with the consts: 
struct ComplexPart 
{ 
    const double a,b,c; 
    ComplexPart(double _a, double _b, double _c) {} 
}; 
// here's the expensive calc function: 
void calc(double *a,double *b,double *c); 

// and this is a helper which returns an initialized ComplexPart from the computation: 
ComplexPart calc2() 
{ 
    double *a,*b,*c; 
    calc(&a,&b,&b); 
    return ComplexPart(a,b,c); 
} 
// put everything together:  
struct MyReferenceClass : public ComplexPart 
{ 
    MyReferenceClass() : ComplexPart(calc2()) {} 
}; 
+0

यहां विरासत का उपयोग करना मेरे लिए दर्दनाक रूप से गलत लगता है। –

1

क्या ऐसा ही कुछ के बारे में:

class A 
{ 
    private: 
    static void calc(double &d1, double &d2, double &d3) 
    { 
     d1 = 1.0; 
     d2 = 2.0; 
     d3 = 3.0; 
    } 
    class D 
    { 
     public: 
     operator double() const 
     { 
      return(x); 
     } 
     private: 
     friend class A; 
     double x; 
    }; 
    public: 
    A() 
    { 
     calc(d1.x, d2.x, d3.x); 
    } 
    D d1, d2, d3; 
}; 

#include <iostream> 

int main() 
{ 
    A a; 
    std::cout << a.d1 << std::endl; 
    std::cout << a.d2 << std::endl; 
    std::cout << a.d3 << std::endl; 
    // the following lines will not compile so you can't change the value 
    // std::cout << a.d3.x << std::endl; 
    // a.d2.x = 0.0; 
    return(0); 
} 
1

ऊपर दिए गए किसी भी उत्तर में विस्तार से ध्यान देना प्रतीत होता है: static का उल्लेख यहां किया गया है, इसलिए ये स्थिरांक कक्षा के वास्तविक उदाहरण से स्वतंत्र प्रतीत होते हैं।

दूसरे शब्दों में: वे वैश्विक स्थिरांक हैं। जैसा कि आपने अनुमान लगाया है, const कीवर्ड की उपस्थिति यहां महत्वपूर्ण है, क्योंकि संकलक लागू करने वाले अनुकूलन के कारण।

वैसे भी, विचार एक सहायक संरचना का उपयोग करना है।

// foo.h 
class Foo 
{ 
public: 
    static double const m1; 
    static double const m2; 
    static double const m3; 
}; 

// foo.cpp 
struct Helper 
{ 
    double m1, m2, m3; 
    Helper() { complexInit(m1, m2, m3); } 
} gHelper; 

double const Foo::m1 = gHelper.m1; 
double const Foo::m2 = gHelper.m2; 
double const Foo::m3 = gHelper.m3; 
बेशक

, एक वास्तविक कार्यक्रम में, मैं आपको प्रोत्साहित करते हैं वास्तव में इंटरफ़ेस के कुछ प्रकार के पीछे स्थिरांक रैप करने के लिए होता है, यह वास्तव में बुरा व्यवहार उन्हें इस तरह से बेनकाब करने के लिए है, क्योंकि इससे उन्हें बदलने के लिए बनाता है (एक और प्रकार का प्रयोग करके) बहुत कठिन।

यह भी ध्यान रखें कि आपको आउटपुट पैरामीटर के लिए पॉइंटर्स की आवश्यकता नहीं है, सादे संदर्भ करते हैं।

2

स्वीकृत उत्तर में संशोधन करने के लिए, कृपया ध्यान दें कि सी ++ 11 के रूप में आप बहुत साफ चाल कर सकते हैं।

class MyReferenceClass { 

public: /* Methods: */ 

    MyReferenceClass() 
     : MyReferenceClass([](){ 
       std::array<double, 3u> cs; /* Helper class, array or tuple */ 
       computeImportantConstants(&cs[0u], &cs[1u], &cs[2u]); 
       return cs; 
      }) 
    {} 

    const double importantConstant1; 
    const double importantConstant2; 
    const double importantConstant3; 

private: /* Methods: */ 

    MyReferenceClass(std::array<double, 3u> constants) 
     : ImportantConstant1(constants[0u]) 
     , ImportantConstant2(constants[1u]) 
     , ImportantConstant3(constants[2u]) 
    {} 

    static void computeImportantConstants(double * out_const1, 
              double * out_const2, 
              double * out_const3); 

}; /* class MyReferenceClass { */ 

या बेहतर अभी तक, प्रवर्तन कोड computeImportantConstants से लैम्ब्डा में निर्माता में, ले जाने के यदि संभव हो तो: उदाहरण के लिए, अपने मूल समस्या इस प्रकार एक लैम्ब्डा और निर्माण प्रतिनिधिमंडल के साथ हल किया जा सकता।

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

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

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