2010-09-19 14 views
9

मुझे लगता है कि मेरी समस्या सबसे अच्छा कोड में वर्णित है:सी ++ वैश्विक प्रारंभिक आदेश निर्भरताओं को अनदेखा करता है?

#include <stdio.h> 

struct Foo; 

extern Foo globalFoo; 

struct Foo { 
    Foo() { 
     printf("Foo::Foo()\n"); 
    } 

    void add() { 
     printf("Foo::add()\n"); 
    } 

    static int addToGlobal() { 
     printf("Foo::addToGlobal() START\n"); 

     globalFoo.add(); 

     printf("Foo::addToGlobal() END\n"); 

     return 0; 
    } 
}; 

Foo globalFoo; 

int dummy = Foo::addToGlobal(); 

int main() { 
    printf("main()\n"); 

    return 0; 
} 

ऊपर प्रिंट (जीसीसी 4.4.3 के साथ):

Foo::Foo() 
Foo::addToGlobal() START 
Foo::add() 
Foo::addToGlobal() END 
main() 

यह मैं क्या उम्मीद है, और तार्किक लगता है।

हालांकि, जब मैं निम्नलिखित लाइनों स्वैप:

Foo globalFoo; 
int dummy = Foo::addToGlobal(); 
इस में

:

int dummy = Foo::addToGlobal(); 
Foo globalFoo; 

कार्यक्रम आउटपुट निम्नलिखित:

Foo::addToGlobal() START 
Foo::add() 
Foo::addToGlobal() END 
Foo::Foo() 
main() 

ऐसा लगता है Foo की उदाहरण तरीकों एक उदाहरण का उपयोग कर कहा जा रहा है जो अभी तक नहीं है बनाया गया है! वैश्विक दायरे में एक चर के घोषणापत्र को स्थानांतरित करने के रूप में सरल कुछ प्रोग्राम के व्यवहार को प्रभावित कर रहा है, और इससे मुझे विश्वास होता है (1) ग्लोबल्स के प्रारंभिकरण के क्रम को परिभाषित नहीं किया गया है और (2) ग्लोबल्स के प्रारंभिकरण का क्रम सभी निर्भरताओं को अनदेखा करता है। क्या ये सही है? क्या यह सुनिश्चित करना संभव है कि Foo के निर्माता को dummy प्रारंभ करने से पहले कहा जाता है?

समस्या जो मैं हल करने की कोशिश कर रहा हूं वह वस्तुओं की एक संग्रह (Foo का एक स्थिर उदाहरण) भर रहा है। मेरे वर्तमान प्रयास में, मैं एक मैक्रो का उपयोग कर रहा हूं जो (अन्य चीजों के साथ) एक वैश्विक परिवर्तनीय बनाता है (नामांकन से बचने के लिए अज्ञात नामस्थान में) जिसका प्रारंभिक स्थिर प्रारंभिकरण को ट्रिगर करता है। शायद मैं गलत कोण से अपनी समस्या का सामना कर रहा हूँ? क्या कोई बेहतर विकल्प है? धन्यवाद।

उत्तर

9

प्रारंभ करने के क्रम में, here उत्तर पढ़ें।

प्रारंभिकरण समस्या को हल करने के तरीके पर, आप वैश्विक को एक फ़ंक्शन में स्थिर स्थानीय चर के रूप में धक्का दे सकते हैं। वहाँ मानक गारंटी देता है कि स्थिर स्थानीय चर कार्य करने के लिए पहली कॉल में प्रारंभ किया जाएगा:

class Foo { 
public: 
    static Foo& singleton() { 
     static Foo instance; 
     return instance; 
    } 
}; 

फिर अपने अन्य वैश्विक चर के रूप चर का उपयोग होगा:

Foo::singleton().add(); 

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

+0

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

+0

इस पैटर्न के साथ एक और संभावित समस्या यह है कि AFAIK एक साथ सिंगलटन तक पहुंच सुरक्षित नहीं है। – FuleSnabel

+1

@strager: जब स्थानीय चर को 'स्थैतिक' घोषित किया जाता है, तो इसका एक उदाहरण होगा, भले ही फ़ंक्शन को रेखांकित किया गया हो (यानी, अलग-अलग अनुवाद इकाइयों में अलग-अलग संकलित) लिंकर को यह सुनिश्चित करना चाहिए कि केवल एक ही परिभाषा चर मौजूद है। –

29

(1) वैश्विक का आरंभीकरण के आदेश से परिभाषित नहीं है

वैश्विक चर एक भी अनुवाद इकाई (स्रोत फ़ाइल) में वे किस क्रम में परिभाषित कर रहे हैं में प्रारंभ कर रहे हैं।

विभिन्न अनुवाद इकाइयों में वैश्विक चर के प्रारंभिकरण का क्रम निर्दिष्ट नहीं है।

(2) वैश्विक का आरंभीकरण के आदेश सभी निर्भरता

सही ध्यान नहीं देता।

क्या यह सुनिश्चित करना संभव है कि डू को शुरू करने से पहले फू के निर्माता को बुलाया जाए?

हाँ, अगर globalFoo से पहलेdummyपरिभाषित किया गया है और वे एक ही अनुवाद इकाई में हैं।

एक विकल्प वैश्विक उदाहरण के लिए एक स्थिर सूचक होना होगा; किसी गतिशील प्रारंभिक होने से पहले इस तरह के पॉइंटर को शून्य से शुरू किया जाएगा; addToGlobal तब परीक्षण कर सकता है कि सूचक शून्य है या नहीं; यदि ऐसा है, तो पहली बार वैश्विक उपयोग किया जा रहा है और addToGlobal वैश्विक Foo बना सकता है।

+0

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

+0

मुझे लगता है कि यह फू का एक स्थिर सदस्य होना बेहतर होगा जो भंडार है। फू को दोनों भंडार (यानी ग्लोबलफू) के रूप में और अन्य उद्देश्यों के लिए 'सही महसूस नहीं' के रूप में व्यवहार करना। यदि दूसरी तरफ फू 'ग्लोबल रिपोजिटरी है तो इनिट को ठीक काम करना चाहिए और डिज़ाइन क्लीनर लगता है। –

+0

मैं सिंगलटन को फ़ंक्शन स्थिर के रूप में कार्यान्वित करना चाहता हूं: 'Foo & global_foo() {static Foo foo; वापसी foo; } 'क्योंकि प्रारंभिकरण के क्रम में थोड़ा बेहतर गारंटी है: इसे विधि के पहले कॉल के दौरान शुरू किया जाएगा। अंतिमकरण के दौरान अभी भी समस्याएं होंगी ... –

1

आप सही हैं, अनुवाद इकाइयों के बीच ग्लोबल्स का प्रारंभिकरण अनिर्धारित है। singleton pattern का उपयोग करके इसे प्राप्त करना संभव है। हालांकि, चेतावनी दी जानी चाहिए कि इस डिजाइन पैटर्न का अक्सर दुरुपयोग किया जाता है। यह भी चेतावनी दी जानी चाहिए कि यदि आपके पास विनाशकों में निर्भरता है तो ग्लोबल्स का आदेश या विनाश भी अनिर्धारित है।

+1

विनाश का क्रम वास्तव में अच्छी तरह से परिभाषित किया गया है: स्थाई भंडारण अवधि वाली वस्तुओं को उनके प्रारंभिकरण या निर्माण के पूरा होने के विपरीत क्रम में नष्ट कर दिया जाता है (कुछ बारीकियां हैं, लेकिन यह सामान्य नियम है)। –

0

सी ++ में Ada's pragma elaborate जैसी कुछ भी कमी नहीं है, इसलिए आप इनसाइटिलेशन के आदेश के बारे में कुछ भी अनुमान नहीं लगा सकते हैं। क्षमा करें। यह बेकार है, लेकिन यह डिजाइन है।

0

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

class Factory { 
    static map<string, Creator*>* theTable; 
    static void register1(const string& string, Creator* creator); 
... 
}; 
... 
map<string, Creator*>* Factory::theTable= nullptr; 
void Factory::register1(const string& theName, Creator* creator) { 
    if (!theTable) theTable=new map<string, Creator*>; 
    (*theTable)[theName]=creator; 
} 

यह संकलित और दृश्य स्टूडियो में कुलपति के साथ ++ काम 2015

मैंने पहले उपयोग करने की कोशिश की थी इस

class Factory { 
    public: 
     static map<string, Creator*> theTable; 
     static map<string, Creator*>& getTable(); 
     static void register1(const string& string, Creator* creator); 
} 
map<string, Creator*> Factory::theTable; 
map<string, Creator*>& Factory::getTable() { 
    return theTable; 
} 
void Factory::register1(const string& theString, Creator* creator) { 
    getTable()[theString]=creator; // fails if executed before theTable is created 

} 

लेकिन मैं अभी भी अपवाद फेंक दिया था, जब theTable प्रयास करने से पहले नहीं बनाया गया था मानचित्र में एक प्रविष्टि डालने के लिए, जो तब हो सकता है जब वर्ग के पंजीकरण को फैक्ट्री तर्क से अलग संकलन इकाई में संभाला जाता है।

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