[C++] كيفية تهيئة أعضاء ثابت خاص في C ++؟


Answers

لمتغير :

foo.h:

class foo
{
private:
    static int i;
};

foo.cpp:

int foo::i = 0;

هذا لأنه يمكن أن يكون هناك مثيل واحد فقط من foo::i في البرنامج الخاص بك. وهو نوع من مكافئ extern int i في ملف رأس و int i في ملف مصدر.

بالنسبة للثابت ، يمكنك وضع القيمة في تعريف الفصل مباشرةً:

class foo
{
private:
    static int i;
    const static int a = 42;
};
Question

ما هي أفضل طريقة لتهيئة عضو بيانات خاص ثابت في C ++؟ لقد حاولت ذلك في ملف رأسي ، لكنه يعطيني أخطاء رابط غريبة:

class foo
{
    private:
        static int i;
};

int foo::i = 0;

أظن أن هذا لأنني لا أستطيع تهيئة عضو خاص من خارج الفصل. إذن ما هي أفضل طريقة للقيام بذلك؟




هل هذا يخدم الغرض الخاص بك؟

//header file

struct MyStruct {
public:
    const std::unordered_map<std::string, uint32_t> str_to_int{
        { "a", 1 },
        { "b", 2 },
        ...
        { "z", 26 }
    };
    const std::unordered_map<int , std::string> int_to_str{
        { 1, "a" },
        { 2, "b" },
        ...
        { 26, "z" }
    };
    std::string some_string = "justanotherstring";  
    uint32_t some_int = 42;

    static MyStruct & Singleton() {
        static MyStruct instance;
        return instance;
    }
private:
    MyStruct() {};
};

//Usage in cpp file
int main(){
    std::cout<<MyStruct::Singleton().some_string<<std::endl;
    std::cout<<MyStruct::Singleton().some_int<<std::endl;
    return 0;
}



ليس لدي ما يكفي من الممثلين هنا لإضافة هذا التعليق ، ولكن IMO أسلوب جيد لكتابة رؤوسك مع #include guards على أي حال ، والتي كما لاحظت Paranaix قبل بضع ساعات من شأنه أن يمنع خطأ متعدد التعريفات. ما لم تكن تستخدم بالفعل ملف CPP منفصل ، فليس من الضروري استخدام أحد الملفات فقط لتهيئة الأعضاء غير المتكاملين.

#ifndef FOO_H
#define FOO_H
#include "bar.h"

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

لا أرى حاجة لاستخدام ملف CPP منفصل لهذا الغرض. بالتأكيد ، يمكنك ذلك ، ولكن لا يوجد سبب تقني لضرورة القيام بذلك.




منذ C ++ 17 ، قد يتم تعريف أعضاء ثابت في رأس مع الكلمة الأساسية المضمّنة .

http://en.cppreference.com/w/cpp/language/static

"قد يتم تعريف عضو بيانات ثابت مضمّن. يمكن تعريف عضو البيانات الثابتة المضمنة في تعريف الفئة وقد يحدد مُبدئ عضو افتراضي. لا يحتاج إلى تعريف خارج الفئة:"

struct X
{
    inline static int n = 1;
};



ماذا عن طريقة set_default() ؟

class foo
{
    public:
        static void set_default(int);
    private:
        static int i;
};

void foo::set_default(int x) {
    i = x;
}

سيكون علينا فقط استخدام الأسلوب set_default(int x) وسيتم تهيئة متغير static لدينا.

لن يكون هذا خلافًا مع بقية التعليقات ، بل إنه يتبع نفس مبدأ تهيئة المتغير في نطاق عالمي ، ولكن باستخدام هذه الطريقة ، نجعلها واضحة (وسهلة الفهم) بدلاً من أن يكون لها التعريف للمتغير المعلق هناك.




يمكنك أيضًا تضمين الواجب في ملف الرأس إذا كنت تستخدم حراس الرأس. لقد استخدمت هذه التقنية لمكتبة C ++ قمت بإنشائها. طريقة أخرى لتحقيق نفس النتيجة هي استخدام أساليب ثابتة. فمثلا...

class Foo
   {
   public:
     int GetMyStatic() const
     {
       return *MyStatic();
     }

   private:
     static int* MyStatic()
     {
       static int mStatic = 0;
       return &mStatic;
     }
   }

يحتوي الكود السابق على "مكافأة" لا تتطلب ملف مصدر CPP /. مرة أخرى ، طريقة أستخدمها في مكتبات C ++ الخاصة بي.




ربما يكون سبب مشكلة الرابط التي واجهتها:

  • توفير تعريف كل عضو فئة وثابت في ملف الرأس ،
  • بما في ذلك هذا العنوان في اثنين أو أكثر من ملفات المصدر.

هذه مشكلة شائعة لأولئك الذين يبدأون بـ C ++. يجب تهيئة عضو الفئة الثابتة في وحدة ترجمة واحدة ، أي في ملف مصدر فردي.

لسوء الحظ ، يجب تهيئة عضو الفئة الثابتة خارج جسم الفصل. هذا يعقد تعليمات برمجية لرأس الكتابة فقط ، ولذلك أستخدم طريقة مختلفة تمامًا. يمكنك توفير الكائن الثابت الخاص بك من خلال وظيفة الفئة الثابتة أو غير الثابتة على سبيل المثال:

class Foo
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

    void func() {
        int &object = getValueInstance();
        object += 5;
    }
};



باستخدام مترجم Microsoft [1] ، يمكن أيضًا تعريف المتغيرات الثابتة التي لا تتشابه في ملف رأس ، ولكن خارج تعريف الفصل ، باستخدام __declspec(selectany) الخاص بـ Microsoft __declspec(selectany) .

class A
{
    static B b;
}

__declspec(selectany) A::b;

لاحظ أنني لا أقول أن هذا جيد ، أنا فقط أقول أنه يمكن القيام به.

[1] في هذه الأيام ، أكثر compilers من دعم MSC __declspec(selectany) - على الأقل gcc و clang. ربما أكثر من ذلك.