स्थिर स्थिर स्ट्रिंग (कक्षा सदस्य)


445

मैं एक वर्ग के लिए एक निजी स्थैतिक स्थिर रखना चाहता हूं (इस मामले में एक आकार-कारखाना)।

मुझे कुछ करना होगा।

class A {
   private:
      static const string RECTANGLE = "rectangle";
}

दुर्भाग्य से मुझे C ++ (g ++) संकलक से सभी प्रकार की त्रुटि मिलती है, जैसे:

ISO C ++ में सदस्य 'RECTANGLE' की प्रारंभिक मनाही

गैर-अभिन्न प्रकार 'std :: string' के स्थैतिक डेटा सदस्य के अवैध इन-क्लास आरंभीकरण

त्रुटि: 'RECTANGLE' को स्थिर बनाना

यह मुझे बताता है कि इस तरह का सदस्य डिजाइन मानक के अनुरूप नहीं है। आपके पास एक निजी शाब्दिक निरंतर (या शायद सार्वजनिक) एक #define निर्देश का उपयोग किए बिना कैसे होता है (मैं डेटा वैश्विकता के कुरूपता से बचना चाहता हूं!)

किसी भी मदद की सराहना की है।


15
आपके सभी महान जवाब के लिए धन्यवाद! लिव इन एसओ!
lb.

क्या कोई मुझे बता सकता है कि एक 'अभिन्न' प्रकार क्या है? आपका बहुत बहुत धन्यवाद।
lb.

1
इंटीग्रल प्रकार उन प्रकारों को संदर्भित करता है जो पूर्णांक संख्याओं का प्रतिनिधित्व करते हैं। देखें publib.boulder.ibm.com/infocenter/comphelp/v8v101/...
bleater

आपके कारखाने में निजी स्थिर स्ट्रिंग अच्छा समाधान नहीं है - विचार करें कि आपके कारखाने के ग्राहकों को यह जानना होगा कि किन आकारों का समर्थन किया जाता है, इसलिए इसे निजी स्थैतिक में रखने के बजाय, उन्हें स्थिर नाम के रूप में अलग-अलग नामस्थान में रखें std :: string RECTANGLE = "आयत "।
ल्यूककोडबकेर

यदि आपकी कक्षा एक टेम्पलेट क्लास है तो stackoverflow.com/q/3229883/52074
ट्रेवर बॉयड स्मिथ

जवाबों:


471

आपको कक्षा की परिभाषा के बाहर अपने स्थिर सदस्य को परिभाषित करना होगा और वहां इनिशियलाइज़र प्रदान करना होगा।

प्रथम

// In a header file (if it is in a header file in your case)
class A {   
private:      
  static const string RECTANGLE;
};

और फिर

// In one of the implementation files
const string A::RECTANGLE = "rectangle";

आप जिस वाक्यविन्यास का मूल रूप से उपयोग करने का प्रयास कर रहे थे (वर्ग परिभाषा के अंदर (प्रारंभिक रूप में)) केवल अभिन्न और enum प्रकारों की अनुमति है।


C ++ 17 से शुरू करके आपके पास एक और विकल्प है, जो आपके मूल घोषणा के समान है: इनलाइन चर

// In a header file (if it is in a header file in your case)
class A {   
private:      
  inline static const string RECTANGLE = "rectangle";
};

कोई अतिरिक्त परिभाषा की जरूरत नहीं है।

या इसके बजाय constआप इसे constexprइस प्रकार में घोषित कर सकते हैं । इसका मतलब inlineयह नहीं है कि स्पष्ट किया जाएगा ।constexprinline


8
इसके अलावा, अगर एसटीएल स्ट्रिंग का उपयोग करने के लिए कोई आवश्यकता नहीं है, तो आप बस एक कास्ट चार * परिभाषित कर सकते हैं। (कम ओवरहेड)
KSchmidt

50
मुझे यकीन नहीं है कि यह हमेशा कम ओवरहेड है - यह उपयोग पर निर्भर करता है। यदि इस सदस्य को ऐसे कार्यों के लिए एक तर्क के रूप में पारित किया जाता है जो स्थैतिक स्ट्रिंग लेते हैं और, प्रारंभ के दौरान प्रत्येक कॉल बनाम एक स्ट्रिंग ऑब्जेक्ट निर्माण के लिए अस्थायी रूप से बनाया जाएगा। स्थिर स्ट्रिंग ऑब्जेक्ट बनाने के लिए IMHO ओवरहेड नगण्य है।
तेदुशे कोपेक

23
मैं इसके बजाय std :: string का हर समय उपयोग करता हूँ। ओवरहेड नगण्य है, लेकिन आपके पास बहुत अधिक विकल्प हैं और बहुत कम मूर्खतापूर्ण चीजें हैं जैसे "जादू" == A :: RECTANGLE केवल उनके पते की तुलना करने के लिए ...
Matthieu M.

9
char const*अच्छाई है कि यह पहले सभी गतिशील प्रारंभ किया जाता है आरंभ नहीं हो जाता है। तो किसी भी वस्तु के निर्माता में, आप पर भरोसा कर सकते हैं कि RECTANGLEतब प्रारंभिक रूप से क्षय हो गया था।
जोहान्स शहाब -

8
@cirosantilli: क्योंकि C ++ में शुरुआती समय से आरम्भिक आयोजक परिभाषाओं के भाग थे , घोषणाएँ नहीं । और कक्षा के अंदर डेटा सदस्य घोषणा केवल यह है: एक घोषणा। (दूसरी ओर, कास्ट इंटीग्रल और एनम सदस्यों के लिए, और C ++ 11 में - शाब्दिक प्रकारों के कॉस्ट सदस्यों के लिए एक अपवाद बनाया गया था ।)
AnT

153

C ++ 11 में अब आप कर सकते हैं:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};

30
दुर्भाग्य से यह समाधान std :: string के लिए काम नहीं करता है।
हेलोवर्ल्ड

2
ध्यान दें कि 1. यह केवल शाब्दिक के साथ काम करता है और 2. यह मानक अनुरूप नहीं है, हालांकि ग्नू / जीसीसी जुर्माना की शिकायत करता है, अन्य संकलक एक त्रुटि फेंक देंगे। परिभाषा शरीर में होनी है।
मैनुएल श्नाइड 3r

2
@ ManuelSchneid3r यह वास्तव में "मानक अनुरूप नहीं" कैसे है? यह मेरे लिए bog-standard C ++ 11 brace-or-equal-initialisisation जैसा दिखता है।
अंडरस्कोर_ड

3
@rvighne, नहीं यह गलत है। constexprसंस्करण के constलिए तात्पर्य , प्रकार के लिए नहीं, यह इंगित करता है। यानी static constexpr const char* constजैसा है, वैसा है static constexpr const char*, लेकिन जैसा है वैसा नहीं है static constexpr char*
midenok

2
@ abyss.7 - आपके उत्तर के लिए धन्यवाद, और मेरे पास एक और कृपया है: यह स्थिर क्यों होना है?
गाए अवराम

34

वर्ग परिभाषाओं के अंदर आप केवल स्थैतिक सदस्यों की घोषणा कर सकते हैं। उन्हें वर्ग के बाहर परिभाषित करना होगा । संकलन-समय के अभिन्न स्थिरांक के लिए मानक अपवाद बनाता है जिसे आप सदस्यों को "इनिशियलाइज़" कर सकते हैं। यह अभी भी एक परिभाषा नहीं है, हालांकि। उदाहरण के बिना, पते को लेना काम नहीं करेगा।

मैं यह उल्लेख करना चाहूंगा कि मुझे स्थिरांक का उपयोग करने का लाभ नहीं दिखता है: कॉन्स्टेंटों के लिए तार पर कास्ट [] । std :: string अच्छी है और सभी में इसे गतिशील आरंभीकरण की आवश्यकता है। तो, अगर आप कुछ ऐसा लिखते हैं

const std::string foo = "hello";

नेमस्पेस स्कोप में कंस्ट्रक्टर के मुख्य भाग के निष्पादन से ठीक पहले चलाया जाएगा और यह कंस्ट्रक्टर ढेर मेमोरी में निरंतर "हैलो" की एक प्रति बनाएगा। जब तक आपको वास्तव में RECTANGLE की जरूरत नहीं है, एक std :: string आप बस लिख सकते हैं

// class definition with incomplete static member could be in a header file
class A {
    static const char RECTANGLE[];
};

// this needs to be placed in a single translation unit only
const char A::RECTANGLE[] = "rectangle";

वहाँ! कोई ढेर आवंटन, कोई नकल नहीं, कोई गतिशील आरंभीकरण नहीं।

चीयर्स, एस।


1
यह पूर्व C ++ 11 उत्तर है। मानक C ++ का उपयोग करें और std :: string_view का उपयोग करें।

1
C ++ 11 में std :: string_view नहीं है।
लुकास सालिच

17

यह सिर्फ अतिरिक्त जानकारी है, लेकिन अगर आप वास्तव में हेडर फ़ाइल में स्ट्रिंग चाहते हैं, तो कुछ इस तरह की कोशिश करें:

class foo
{
public:
    static const std::string& RECTANGLE(void)
    {
        static const std::string str = "rectangle";

        return str;
    }
};

हालांकि मुझे संदेह है कि सिफारिश की गई है।


यह अच्छा लग रहा है :) - लगता है कि आप सी + + के अलावा अन्य भाषाओं में एक पृष्ठभूमि है?
lb.

5
मैं इसकी सिफारिश नहीं करूंगा। मैं अक्सर ऐसा करता हूं। यह ठीक काम करता है और मुझे कार्यान्वयन फ़ाइल में स्ट्रिंग डालने की तुलना में यह अधिक स्पष्ट लगता है। Std :: string का वास्तविक डेटा अभी भी ढेर पर स्थित है। मैं एक कास्ट चार * लौटाता हूँ, जिस स्थिति में आपको स्थिर वैरिएबल घोषित करने की आवश्यकता नहीं होती है, इसलिए घोषणा कम जगह (कोड वार) लेगी। बस स्वाद की बात है।
जूमलेटर जू

15

C ++ 17 में आप इनलाइन चर का उपयोग कर सकते हैं :

class A {
 private:
  static inline const std::string my_string = "some useful string constant";
};

ध्यान दें कि यह abyss.7 के उत्तर से अलग है : यह एक वास्तविक std::stringवस्तु को परिभाषित करता है , न किconst char*


क्या आपको नहीं लगता कि उपयोग करने inlineसे बहुत सारे डुप्लिकेट बनेंगे?
शुवा


8

इन-क्लास इनिशियलाइज़ेशन सिंटैक्स का उपयोग करने के लिए, स्थिरांक को स्थिर या एन्युमरेशन प्रकार का स्थिर स्थिर होना चाहिए, जो एक स्थिर अभिव्यक्ति द्वारा आरम्भ किया गया हो।

यह प्रतिबंध है। इसलिए, इस मामले में आपको कक्षा के बाहर चर को परिभाषित करने की आवश्यकता है। उत्तर देखें @AndreyT से


7

श्रेणी स्थिर चर को हेडर में घोषित किया जा सकता है लेकिन इसे .cpp फ़ाइल में परिभाषित किया जाना चाहिए । ऐसा इसलिए है क्योंकि स्थैतिक चर का केवल एक ही उदाहरण हो सकता है और संकलक यह तय नहीं कर सकता है कि किस ऑब्जेक्ट फ़ाइल को इसे रखा जाए ताकि आपको इसके बजाय निर्णय करना पड़े।

C ++ 11 में घोषणा के साथ एक स्थिर मूल्य की परिभाषा रखने के लिए एक नेस्टेड स्थिर संरचना का उपयोग किया जा सकता है। इस मामले में स्थैतिक सदस्य एक संरचना है और इसे एक .cpp फ़ाइल में परिभाषित किया जाना है, लेकिन मान हेडर में हैं।

class A
{
private:
  static struct _Shapes {
     const std::string RECTANGLE {"rectangle"};
     const std::string CIRCLE {"circle"};
  } shape;
};

व्यक्तिगत सदस्यों को शुरू करने के बजाय पूरी स्थिर संरचना को .cpp में आरंभीकृत किया गया है:

A::_Shapes A::shape;

मानों के साथ पहुँचा है

A::shape.RECTANGLE;

या - चूंकि सदस्य निजी हैं और उनका उपयोग केवल A - के साथ किया जाना है

shape.RECTANGLE;

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

// file.h
class File {
public:
  static struct _Extensions {
    const std::string h{ ".h" };
    const std::string hpp{ ".hpp" };
    const std::string c{ ".c" };
    const std::string cpp{ ".cpp" };
  } extension;
};

// file.cpp
File::_Extensions File::extension;

// module.cpp
static std::set<std::string> headers{ File::extension.h, File::extension.hpp };

इस मामले में स्थैतिक चर हेडर में या तो {""} या {".h", ".hpp"} होगा, जो कि लिंकर द्वारा बनाए गए आरंभीकरण के आदेश पर निर्भर करता है।

जैसा कि @ abyss.7 ने उल्लेख किया है कि आप यह भी उपयोग constexprकर सकते हैं कि चर का मान संकलन समय पर किया जा सकता है। लेकिन अगर आप अपने तार के साथ घोषित करते हैं static constexpr const char*और आपके कार्यक्रम का उपयोग करता है std::stringअन्यथा एक ओवरहेड होगा क्योंकि std::stringहर बार जब आप इस तरह के स्थिरांक का उपयोग करते हैं तो एक नई वस्तु बनाई जाएगी:

class A {
public:
   static constexpr const char* STRING = "some value";
};
void foo(const std::string& bar);
int main() {
   foo(A::STRING); // a new std::string is constructed and destroyed.
}

अच्छी तरह से तैयार मार्को। दो विवरण: किसी को स्थिर वर्ग के सदस्यों के लिए cpp फ़ाइलों की आवश्यकता नहीं है, और किसी भी प्रकार के स्थिरांक के लिए std :: string_view का भी उपयोग करें।

4

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


4

संभव बस करो:

static const std::string RECTANGLE() const {
    return "rectangle";
} 

या

#define RECTANGLE "rectangle"

11
#Define का उपयोग करना जब एक टाइप किए गए निरंतर का उपयोग किया जा सकता है तो यह गलत है।
अर्तुर कज्जाका

आपका पहला उदाहरण मूल रूप से एक अच्छा समाधान है यदि आपके पास नहीं है constexprलेकिन आप एक स्थिर कार्य नहीं कर सकते हैं const
फ्रैंक पफर

इस घोल से बचना चाहिए। यह हर आह्वान पर एक नया तार बनाता है। यह बेहतर होगा:static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
ओज सोलोमन

पूर्ण-मूल्य वाले कंटेनर का उपयोग वापसी मूल्य के रूप में क्यों किया जाता है? Std :: string_vew का उपयोग करें। यह इस मामले में मान्य रहेगा। स्ट्रिंग दृश्य को बनाने और वापस करने के लिए और भी बेहतर स्ट्रिंग स्ट्रिंगल्स का उपयोग करें ... और अंतिम नहीं बल्कि कम से कम, कॉन्स्ट रिटर्न वैल्यू का यहाँ कोई अर्थ या प्रभाव नहीं है .. हाँ, और यह इनलाइन के रूप में है, स्टेटिक नहीं, कुछ हेडर में नामित नाम ... और कृपया इसे बनाने के लिए विवश करें

4

आप या तो const char*ऊपर वर्णित समाधान के लिए जा सकते हैं , लेकिन फिर अगर आपको हर समय स्ट्रिंग की आवश्यकता होती है, तो आपके पास बहुत अधिक ओवरहेड होने वाला है।
दूसरी ओर, स्टेटिक स्ट्रिंग को डायनामिक इनिशियलाइज़ेशन की आवश्यकता होती है, इस प्रकार यदि आप किसी अन्य ग्लोबल / स्टैटिक वेरिएबल के इनिशियलाइज़ेशन के दौरान इसके मूल्य का उपयोग करना चाहते हैं, तो आप इनिशियलाइज़ेशन ऑर्डर की समस्या से जूझ सकते हैं। उससे बचने के लिए, सबसे सस्ती चीज एक स्टेटर ऑब्जेक्ट को एक गेट्टर के माध्यम से एक्सेस कर रही है, जो यह जांचता है कि आपकी ऑब्जेक्ट आरंभीकृत है या नहीं।

//in a header  
class A{  
  static string s;   
public:   
  static string getS();  
};  
//in implementation  
string A::s;  
namespace{  
  bool init_A_s(){  
    A::s = string("foo");   
    return true;  
  }  
  bool A_s_initialized = init_A_s();  
}  
string A::getS(){      
  if (!A_s_initialized)  
    A_s_initialized = init_A_s();  
  return s;  
}  

केवल उपयोग करना याद रखें A::getS()। क्योंकि किसी भी थ्रेडिंग केवल द्वारा शुरू किया जा सकता है main(), और A_s_initializedपहले शुरू कर दिया है main(), आप एक multithreaded वातावरण में भी ताले की जरूरत नहीं है। A_s_initializedडिफ़ॉल्ट रूप से (डायनामिक इनिशियलाइज़ेशन से पहले) 0 है, इसलिए यदि आप getS()एस इनिशियलाइज़ होने से पहले उपयोग करते हैं, तो आप इनिट फ़ंक्शन को सुरक्षित रूप से कॉल करते हैं।

Btw, ऊपर दिए गए उत्तर में: " स्टेटिक कॉन्स्टेबल std :: string RECTANGLE () कॉन्स्टेबल ", स्टैटिक फंक्शन नहीं हो constसकते क्योंकि वे किसी भी तरह से ऑब्जेक्ट को स्टेट्स नहीं बदल सकते हैं (यह पॉइंटर नहीं है)।


4

2018 और सी ++ 17 के लिए तेजी से आगे।

  • std :: string का उपयोग न करें, std :: string_view literals का उपयोग करें
  • कृपया 'कॉन्स्ट्रेक्स' को नोटिस करें। यह एक "संकलन समय" तंत्र भी है।
  • कोई इनलाइन का मतलब पुनरावृत्ति नहीं है
  • इसके लिए कोई cpp फाइलें आवश्यक नहीं हैं
  • स्थिर समय पर ही static_assert 'कार्य' करता है

    using namespace std::literals;
    
    namespace STANDARD {
    constexpr 
    inline 
    auto 
    compiletime_static_string_view_constant() {
    // make and return string view literal
    // will stay the same for the whole application lifetime
    // will exhibit standard and expected interface
    // will be usable at both
    // runtime and compile time
    // by value semantics implemented for you
        auto when_needed_ =  "compile time"sv;
        return when_needed_  ;
    }

    };

ऊपर एक उचित और कानूनी मानक C ++ नागरिक है। यह किसी भी और सभी सीडी में आसानी से शामिल हो सकता है :: एल्गोरिदम, कंटेनर, उपयोगिताओं और एक ऐसा। उदाहरण के लिए:

// test the resilience
auto return_by_val = []() {
    auto return_by_val = []() {
        auto return_by_val = []() {
            auto return_by_val = []() {
return STANDARD::compiletime_static_string_view_constant();
            };
            return return_by_val();
        };
        return return_by_val();
    };
    return return_by_val();
};

// actually a run time 
_ASSERTE(return_by_val() == "compile time");

// compile time 
static_assert(
   STANDARD::compiletime_static_string_view_constant() 
   == "compile time" 
 );

मानक C ++ का आनंद लें


std::string_viewयदि आप string_viewअपने सभी कार्यों में मापदंडों का उपयोग करते हैं, तो केवल स्थिरांक का उपयोग करें । यदि आपका कोई भी कार्य const std::string&पैरामीटर का उपयोग करता है , तो string_viewउस पैरामीटर के माध्यम से एक स्थिरांक बनाते समय एक स्ट्रिंग की एक प्रति बनाई जाएगी । यदि आपके स्थिरांक टाइप के हैं std::stringतो प्रतियां न तो const std::string&मापदंडों के लिए बनाई जाएंगी और न ही std::string_viewमापदंडों के लिए।
मार्को Mahnič

अच्छा जवाब है, लेकिन क्यों एक समारोह से string_view वापस आ रहा है के रूप में उत्सुक? इस तरह की चाल inlineउनके ODR शब्दार्थ के साथ C ++ 17 में चर आने से पहले उपयोगी थी । लेकिन string_view C ++ 17 भी है, तो बस constexpr auto some_str = "compile time"sv;काम करता है (और वास्तव में, यह एक चर नहीं है, यह है constexpr, इसलिए inlineइसका निहितार्थ है; यदि आपके पास एक चर है - यानी नहीं constexpr- तो inline auto some_str = "compile time"sv;यह करेंगे, हालांकि यह एक नेमस्पेस-स्कोप है) चर, जो अनिवार्य रूप से एक वैश्विक चर है, शायद ही कभी एक अच्छा विचार होगा)।
लॉस मेंटलिटी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.