2016-10-18 8 views
7

बनाम एक स्थिर सदस्य चर जो की एक स्थिर सदस्य चर अन्य वर्ग से आरंभ नहीं हो जाता यह देखते हुए के साथ स्थिर सदस्य चर निर्भर आरंभीकरण पर स्थिर, गैर शाब्दिक struct ii कभी कभी करने के लिए 0या के लिए शुरू डिफ़ॉल्ट है 333। यह संकलन या लिंकिंग ऑर्डर पर निर्भर करता है। स्यूडोकोड प्रदर्शित करने के लिए:सी ++: पूर्णांक struct

class StaticClass: // file 'ONE.cpp/.hpp' 
    static int i = 2 
    static struct ii { int a = 333 } 

class ABC: // file 'abc.cpp' 
    static int abc_i = StaticClass::i // always 2 
    static struct abc_ii = StaticClass::ii // sometimes 0, sometimes 333 

कॉलिंग i = 2/ii = 0 में g++ -std=c++11 abc.cpp ONE.cpp && ./a.out परिणाम (जीसीसी 4.8.1, ++ बजना के साथ 3.7 एक ही; -Wall -Wextra कभी नहीं शिकायत)।

लेकिन g++ -std=c++11 ONE.cpp abc.cpp && ./a.out पर कॉलिंग i = 2/ii = 333में परिणाम!

ONE.o abc.o बनाम abc.o ONE.o और यह भी साथ एक ही होता है जब फ़ाइलों श्रृंखलाबद्ध एक तरह से या किसी अन्य रूप: करने के लिए डिफ़ॉल्ट प्रारंभ

cat ONE.cpp abc.cpp > X.cpp && g++ X.cpp && ./a.out बनाम cat abc.cpp ONE.cpp > Y.cpp && g++ Y.cpp && ./a.out

निकाला जा रहा है भी शामिल है और एकल फाइल में चारों ओर कोड का स्थान बदलना, 0 तब होता है जब यह ऑर्डर मौजूद होता है:

const OneI ABC::def_ii = StaticClass::ii; const OneI StaticClass::ii = OneI{333};

और इस आदेश के साथ 333 से है:

const OneI StaticClass::ii = OneI{333}; const OneI ABC::def_ii = StaticClass::ii;

क्यों भी दो अलग-अलग संकलन इकाइयों के साथ होता है? क्या इसे हर समय बाद के आदेश को लागू करके किसी भी तरह से बचा जा सकता है? ABC में StaticClass::ii सुरक्षित (हालांकि मैं नहीं करना चाहूंगा) में स्थिर सूचक का उपयोग कर रहा हूं?

पूर्ण सी ++ कोड:


/* File: abc.cpp */ 

#include <iostream> 
#include "ONE.hpp" 

struct ABC { 
    ABC(); 

    static const int def_i; 
    static const OneI def_ii; 
    void arg_i(const int &x) { std::cout << "i = " << x << " ";}; 
    void arg_ii(const OneI &x) { std::cout << "/ ii = " << x.a << " ";}; 

}; 

ABC::ABC() { 
    arg_i(def_i); 
    arg_ii(def_ii); 
} 

const int ABC::def_i = StaticClass::i; 
const OneI ABC::def_ii = StaticClass::ii; 

int main() { 
    ABC a; 
    std::cout << '\n'; 
} 
/* End: abc.cpp */ 

/* File: ONE.cpp */ 

#include <iostream> 

#include "ONE.hpp" 

const int StaticClass::i = 2; 
const OneI StaticClass::ii = OneI{333}; 

/* End: ONE.cpp */ 

/* File: ONE.hpp */ 

#include <iostream> 

#ifndef One 
#define One 

struct OneI { 
    OneI(int a_) : a(a_) { } 
    int a; 
}; 

struct StaticClass { 
    const static int i; 
    const static OneI ii; 
}; 

#endif // One header guard 

/* End: ONE.hpp */ 

उत्तर

4

बधाई! आपको static initialization order fiasco का सामना करना पड़ा है।

स्थैतिक वस्तुओं का प्रारंभिक क्रम कई अनुवाद इकाइयों में परिभाषित नहीं किया गया है।

StaticClass::iiONE.cpp और ABC::def_ii में परिभाषित किया गया है abc.cpp में परिभाषित किया गया है। इसलिए StaticClass::iiABC::def_ii से पहले प्रारंभ या प्रारंभ नहीं किया जा सकता है। चूंकि ABC::def_ii का प्रारंभ StaticClass::ii के मान का उपयोग करता है, तो यह मान इस बात पर निर्भर करेगा कि StaticClass::ii को अभी तक प्रारंभ किया गया था।

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

const OneI ABC::def_ii = StaticClass::ii; // StaticClass::ii wasn't initialized yet 
const OneI StaticClass::ii = OneI{333}; 

बाद इस आदेश को हर समय लागू करके किसी भी तरह से बचा जा सकता है?

सबसे छोटा समाधान दोनों ऑर्डर को उसी क्रम इकाई में सही क्रम में परिभाषित करना है। Construct On First Use Idiom का उपयोग करके अपनी स्थिर वस्तुओं को प्रारंभ करना एक और सामान्य समाधान है।

StaticClass::ii सुरक्षित करने के लिए ABC में एक स्थिर सूचक का उपयोग कर रहा है (मैं पसंद करते हैं नहीं है, हालांकि)?

जब तक सूचक की dereferenced मूल्य एक और अनुवाद इकाई है कि जहां उठाई वस्तु परिभाषित किया गया है, हाँ, एक सूचक के साथ ABC::def_ii की जगह सुरक्षित होगा में एक स्थिर वस्तु के प्रारंभ के दौरान किया जाता है।


StaticClass::ii † शून्य से आरंभ किया गया है जाएगा स्थिर आरंभीकरण चरण †† दौरान। स्टेटिक प्रारंभिक आदेश फियास्को गतिशील प्रारंभिक चरण †† से संबंधित है।

†† सी ++ मानक प्रारूप [basic.start.static]

लगातार प्रारंभ नहीं किया जाता है, तो स्थिर भंडारण अवधि ([basic.stc.static]) या धागा भंडारण अवधि ([basic.stc साथ एक चर। थ्रेड]) शून्य-प्रारंभिक ([dcl.init]) है। साथ में, शून्य-प्रारंभिकरण और निरंतर प्रारंभिक को स्थिर प्रारंभिक कहा जाता है; अन्य सभी प्रारंभिक गतिशील प्रारंभिकरण है। किसी गतिशील प्रारंभिक होने से पहले स्टेटिक प्रारंभिकता की जाएगी। [नोट: गैर-स्थानीय चर के गतिशील प्रारंभिकरण को [basic.start.dynamic] में वर्णित किया गया है; स्थानीय स्थैतिक चर के [stmt.dcl] में वर्णित है। - अंत नोट]

+0

आप स्थैतिक और गतिशील प्रारंभिक चरणों और गतिशील प्रारंभिकरण के आवश्यक क्रम को कैसे स्थापित करना चाहते हैं, विस्तृत करना चाहते हैं। अन्यथा आपका उत्तर आधा कहानी से कम है। –

+0

@MaximEgorushkin मैंने स्थिर और गतिशील प्रारंभिकरण के बारे में विस्तार जोड़ा। हालांकि यह समझने में मदद करता है कि क्यों प्रारंभिक 'स्टेटिक क्लास :: ii' का मान 0 होना चाहिए, मैं इस सवाल के महत्व के बारे में असहमत हूं। – user2079303

+0

ऑर्डर ए है) शून्य-प्रारंभिकरण, बी) स्थिर प्रारंभिकरण, सी) गतिशील प्रारंभिकरण? और चूंकि 'const int StaticClass :: i = 2' b है),' const int एबीसी :: def_i = StaticClass :: i' क्या है? गतिशील मैं अनुमान लगाऊंगा। – toting

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