سؤال

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

class foo
{
    private:
        static int i;
};

int foo::i = 0;

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

هل كانت مفيدة؟

المحلول

ويجب أن يكون تعريف فئة في ملف الرأس (أو في الملف المصدر إن لم يكن المشتركة).
ملف: foo.h

class foo
{
    private:
        static int i;
};

ولكن ينبغي أن تكون التهيئة في الملف المصدر.
ملف: foo.cpp

int foo::i = 0;

إذا والتهيئة في ملف الرأس ثم كل ملف يتضمن ملف الرأس سوف يكون لها تعريف عضو ثابت. وهكذا خلال المرحلة الرابط الذي سوف تحصل أخطاء رابط كرمز لتهيئة المتغير سوف يتم تحديدها في ملفات متعددة المصدر.

ملاحظة: مات كورتيس: يشير إلى أن C ++ يسمح للتبسيط ما سبق إذا كان المتغير عضو ثابت هو من CONST نوع int (مثل int، bool، char). يمكنك بعد ذلك أعلن وتهيئة متغير العضو مباشرة داخل تعريف فئة في ملف الرأس:

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

نصائح أخرى

عن متغير:

فو.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;
};

لالمشاهدين في المستقبل في هذه المسألة، وأنا أريد أن أشير إلى أنه يجب تجنب ما monkey0506 يقترح .

والملفات رأس هي للإعلانات.

والحصول على تجميع الملفات رأس مرة واحدة لكل ملف .cpp أن #includes بشكل مباشر أو غير مباشر، ورمز خارج أي وظيفة تشغيل في التهيئة البرنامج، قبل main().

ومن خلال وضع: foo::i = VALUE; في الرأس، سيتم تعيين foo:i وVALUE قيمة (أيا كان هذا هو) لكل ملف .cpp، وهذه المهام يحدث في أمر غير محدد (يحدده رابط) قبل تشغيل main()

وماذا لو أننا #define VALUE أن يكون عدد مختلف في أحد ملفات .cpp لدينا؟ وسوف تجمع غرامة وسيكون لدينا أي وسيلة لمعرفة أي واحد يفوز حتى وتشغيل البرنامج.

لا تضع كود تنفيذها في رأس لنفس السبب أنك لم #include ملف .cpp.

وتشمل الحراس (والتي أنا أتفق يجب عليك دائما استخدام) حمايتك من شيء مختلف: رأس نفس يجري #included غير مباشرة عدة مرات أثناء ترجمة ملف .cpp واحد

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

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

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

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

ومع مترجم مايكروسوفت [1]، والمتغيرات الثابتة التي لا int مثل يمكن أيضا تعريف في ملف الرأس، ولكن خارج تعريف فئة، وذلك باستخدام __declspec(selectany) محددة مايكروسوفت.

class A
{
    static B b;
}

__declspec(selectany) A::b;

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

[1] في هذه الأيام، أكثر المجمعين من MSC __declspec(selectany) الدعم - على الأقل دول مجلس التعاون الخليجي ورنة. ربما حتى أكثر من ذلك.

int foo::i = 0; 

هل بناء الجملة الصحيح لتهيئة متغير، ولكن يجب أن تذهب في ملف مصدر (.CPP) وليس في الرأس.

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

وأنا لم يكن لديك ما يكفي من مندوب هنا لإضافة هذا كتعليق، ولكن IMO انها اسلوب جيد لكتابة رؤوس الخاص بك مع <لأ href = "http://en.wikipedia.org/wiki/Include_guard" يختلط = " noreferrer "> # تشمل حراس على أي حال، والتي كما لاحظ Paranaix قبل ساعات قليلة من شأنه أن يمنع حدوث خطأ متعددة التعريف. إلا إذا كنت تستخدم ملف CPP منفصل، فإنه ليس من الضروري استخدام واحد فقط لتهيئة أعضاء ثابتة غير متكاملة.

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

class foo
{
private:
    static bar i;
};

bar foo::i = VALUE;
#endif

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

إذا كنت تريد تهيئة نوع مركب (سلسلة f.e.) يمكنك أن تفعل شيئا من هذا القبيل:

class SomeClass {
  static std::list<string> _list;

  public:
    static const std::list<string>& getList() {
      struct Initializer {
         Initializer() {
           // Here you may want to put mutex
           _list.push_back("FIRST");
           _list.push_back("SECOND");
           ....
         }
      }
      static Initializer ListInitializationGuard;
      return _list;
    }
};

وكما ListInitializationGuard هو متغير ثابت داخل طريقة SomeClass::getList() سيتم بناؤها مرة واحدة فقط، وهو ما يعني أن منشئ يسمى مرة واحدة. وهذا initialize _list متغير إلى قيمة التي تحتاج إليها. فإن أي استدعاء لاحقة getList العودة ببساطة كائن _list تهيئته بالفعل.

وبالطبع كان لديك الوصول إلى الكائن _list دائما بالدعوة طريقة getList().

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

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

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

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

وأنا أتابع هذه الفكرة من كارل. أنا أحب ذلك، والآن أنا استخدامها كذلك. لقد تغيرت قليلا تدوين وإضافة بعض الوظائف

#include <stdio.h>

class Foo
{
   public:

     int   GetMyStaticValue () const {  return MyStatic();  }
     int & GetMyStaticVar ()         {  return MyStatic();  }
     static bool isMyStatic (int & num) {  return & num == & MyStatic(); }

   private:

      static int & MyStatic ()
      {
         static int mStatic = 7;
         return mStatic;
      }
};

int main (int, char **)
{
   Foo obj;

   printf ("mystatic value %d\n", obj.GetMyStaticValue());
   obj.GetMyStaticVar () = 3;
   printf ("mystatic value %d\n", obj.GetMyStaticValue());

   int valMyS = obj.GetMyStaticVar ();
   int & iPtr1 = obj.GetMyStaticVar ();
   int & iPtr2 = valMyS;

   printf ("is my static %d %d\n", Foo::isMyStatic(iPtr1), Foo::isMyStatic(iPtr2));
}

وهذه النواتج

mystatic value 7
mystatic value 3
is my static 1 0

منشئ ثابت النمط الذي يعمل على كائنات متعددة

واحدة لغة اقترح في: https://stackoverflow.com/a/27088552/895245 ولكن هنا يذهب نظافة النسخة التي لا تحتاج إلى إيجاد طريقة جديدة لكل عضو ، runnable سبيل المثال:

#include <cassert>
#include <vector>

// Normally on the .hpp file.
class MyClass {
public:
    static std::vector<int> v, v2;
    static struct _StaticConstructor {
        _StaticConstructor() {
            v.push_back(1);
            v.push_back(2);
            v2.push_back(3);
            v2.push_back(4);
        }
    } _staticConstructor;
};

// Normally on the .cpp file.
std::vector<int> MyClass::v;
std::vector<int> MyClass::v2;
// Must come after every static member.
MyClass::_StaticConstructor MyClass::_staticConstructor;

int main() {
    assert(MyClass::v[0] == 1);
    assert(MyClass::v[1] == 2);
    assert(MyClass::v2[0] == 3);
    assert(MyClass::v2[1] == 4);
}

جيثب المنبع.

انظر أيضا: ثابت المنشئات في C++?أنا بحاجة إلى تهيئة خاصة الأجسام الساكنة

اختبار مع g++ -std=c++11 -Wall -Wextra, دول مجلس التعاون الخليجي 7.3 ، أوبونتو 18.04.

وتعمل أيضا في ملف privateStatic.cpp:

#include <iostream>

using namespace std;

class A
{
private:
  static int v;
};

int A::v = 10; // possible initializing

int main()
{
A a;
//cout << A::v << endl; // no access because of private scope
return 0;
}

// g++ privateStatic.cpp -o privateStatic && ./privateStatic

وماذا عن طريقة 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
{
    // int& getObjectInstance() const {
    static int& getObjectInstance() {
        static int object;
        return object;
    }

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

وأردت فقط أن أذكر شيئا غريبا بعض الشيء بالنسبة لي عندما كنت لأول مرة واجهت هذه.

وأنا في حاجة إلى تهيئة عضو بيانات ثابتة الخاص في فئة القالب.

وفي .H أو .hpp، يبدو شيئا من هذا القبيل لتهيئة عضو بيانات ثابتة من فئة قالب:

template<typename T>
Type ClassName<T>::dataMemberName = initialValue;

واحد "المدرسة القديمة" وسيلة لتعريف الثوابت هي لتحل محلها من قبل enum:

class foo
{
    private:
        enum {i = 0}; // default type = int
        enum: int64_t {HUGE = 1000000000000}; // may specify another type
};

وهذه الطريقة لا تتطلب تقديم تعريف، والابتعاد عن جعل lvalue ، التي يمكن أن توفر لك بعض الصداع ، على سبيل المثال عندما كنت غير قصد ODR الاستخدام في هذا الشأن.

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

//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;
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top