указатель - Как инициализировать частные статические члены в C++?




указатель на статический метод класса c++ (12)

Вы также можете включить назначение в файл заголовка, если используете средства защиты заголовков. Я использовал этот метод для библиотеки C ++, которую я создал. Другим способом достижения такого же результата является использование статических методов. Например...

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

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

В приведенном выше коде есть «бонус», не требующий файла CPP / source. Опять же, метод, который я использую для своих библиотек C ++.

Каков наилучший способ инициализации частного статического члена данных на 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;

Это связано с тем, что в вашей программе может быть только один экземпляр foo::i . Это своего рода эквивалент extern int i в файле заголовка и int i в исходном файле.

Для константы вы можете поместить значение прямо в объявление класса:

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

Если вы хотите инициализировать некоторый составной тип (fe string), вы можете сделать что-то вроде этого:

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() он будет SomeClass::getList() только один раз, что означает, что конструктор вызывается один раз. Это приведет к initialize _list переменной initialize _list до initialize _list значения. Любой последующий вызов getList просто возвращает уже инициализированный объект _list .

Конечно, вы должны всегда обращаться _list объекту _list , вызывая getList() .


Как насчет 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 переменная была бы инициализирована.

Это не противоречит остальным комментариям, на самом деле он следует тому же принципу инициализации переменной в глобальной области, но, используя этот метод, мы делаем его явным (и понятным для понимания) вместо определения от переменной висит там.


Объявление класса должно быть в файле заголовка (или в исходном файле, если не используется совместно).
Файл: 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;
};

Один способ «старой школы» определить константы - заменить их enum :

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

Этот способ не требует предоставления определения и позволяет избежать постоянной lvalue , которая может сэкономить вам некоторые головные боли, например, когда вы случайно используете ODR-use .


Проблема компоновщика, с которой вы столкнулись, вероятно, вызвана:

  • Предоставляя определение класса и статического члена в файле заголовка,
  • Включая этот заголовок в двух или более исходных файлах.

Это обычная проблема для тех, кто начинает с C ++. Статический член класса должен быть инициализирован в единице перевода, то есть в файле с одним источником.

К сожалению, статический член класса должен быть инициализирован вне тела класса. Это усложняет запись только кода заголовка, и поэтому я использую совершенно другой подход. Вы можете предоставить свой статический объект с помощью статической или нестатической функции класса, например:

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

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

С помощью компилятора Microsoft [1] статические переменные, которые не являются int like, также могут быть определены в файле заголовка, но вне объявления класса, используя специфический для Microsoft __declspec(selectany) .

class A
{
    static B b;
}

__declspec(selectany) A::b;

Заметьте, что я не говорю, что это хорошо, я просто говорю, что это можно сделать.

[1] В наши дни больше компиляторов, чем MSC, поддерживают __declspec(selectany) - по крайней мере, gcc и clang. Может быть, даже больше.


Это служит вашей цели?

//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;
}

Я придерживаюсь идеи Карла. Мне это нравится, и теперь я использую его также. Я немного изменил обозначение и добавил некоторые функции

#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://.com/a/27088552/895245 но здесь идет более чистая версия, которая не требует создания нового метода для каждого члена и пример выполнения:

#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 , GCC 7.2, Ubuntu 17.10.


int foo::i = 0; 

Является правильным синтаксисом для инициализации переменной, но она должна идти в исходном файле (.cpp), а не в заголовке.

Поскольку это статическая переменная, компилятор должен создать только одну копию. У вас должна быть строка «int foo: i», где в вашем коде, чтобы сообщить компилятору, куда его поместить, иначе вы получите сообщение об ошибке. Если это в заголовке, вы получите копию в каждом файле, который содержит заголовок, поэтому получите многократно определенные ошибки символов из компоновщика.





static-members