Подтвердить что ты не робот

Можно ли инициализировать статический член const во время выполнения на С++?

Можно ли инициализировать элемент static const моего класса во время выполнения? Эта переменная является постоянной во всей моей программе, но я хочу отправить ее как аргумент командной строки.

//A.h
class A {
public: 
    static const int T;
};

//in main method
int main(int argc,char** argv)
{
    //how can I do something like 
    A::T = atoi(argv[1]);
}

Если это невозможно, какой тип переменной я должен использовать? Мне нужно инициализировать его во время выполнения, а также сохранить свойство константы.

4b9b3361

Ответ 1

Я сожалею, что не согласен с комментариями и ответами о том, что символ static const не может быть инициализирован при запуске программы, а не во время компиляции.

На самом деле это возможно, и я использовал его много раз, НО я инициализирую его из файла конфигурации. Что-то вроде:

// GetConfig is a function that fetches values from a configuration file
const int Param1 = GetConfig("Param1");
const int MyClass::Member1 = GetConfig("MyClass.Member1");

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

С другой стороны, установка их из argv [] кажется очень сложной, если это когда-либо осуществимо, потому что, когда main() запускается, статические символы уже инициализируются.

Ответ 2

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

Однако вы можете инициализировать скрытую неконстантную переменную и предоставить ссылку const на нее, например:

struct A {
public: 
    // Expose T as a const reference to int
    static const int& T;
};

//in main.cpp

// Make a hidden variable for the actual value
static int actualT;
// Initialize A::T to reference the hidden variable
const int& A::T(actualT);

int main(int argc,char** argv) {
    // Set the hidden variable
    actualT = atoi(argv[1]);
    // Now the publicly visible variable A::T has the correct value
    cout << A::T << endl;
}

Демо-версия

Ответ 3

Нет, вы не можете этого сделать.

Если это невозможно сделать, какой тип переменной я должен использовать?

Вы можете использовать член не const.

class A 
{
   public: 
      static int T;
};

int A::T;

Другой вариант состоит в том, чтобы сделать T частным членом, сделать main другом, чтобы только он мог изменить значение, а затем выставить элемент через функцию.

#include <cstdlib>

class A 
{
   public: 
      static int getT() { return T; }
   private:
      static int T;
      friend int main(int argc, char** argv);
};

int A::T;

int main(int argc, char** argv)
{
   A::T = std::atoi(argv[1]);
   return 0;
}

Ответ 4

Не только вы не можете, вы не должны пытаться это сделать, возившись с const_cast. Статические константные члены имеют очень высокий шанс выйти в сегмент только для чтения, и любая попытка их изменения приведет к сбою программы.

Ответ 5

Обычно у вас будет более одного значения конфигурации. Поэтому помещаем их в структуру, а обычный глобальный доступ к ней - const.

const config* Config;
...
main (int argc, char* argv [])
{
Config= new config (argc, argv);
...
}

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

Файл заголовка предоставляет get_config () для всех, но способ его установить известен только для кода, который предназначен для этого.

Ответ 6

Нет, поскольку вы определили переменную как static и const, вы не можете изменить ее значение. Вам нужно будет установить его значение в самом определении или через конструктор, который вызывается при создании объекта класса A.

Ответ 7

Метод # 1: Инициализировать скрытую неконстантную переменную и предоставить ссылку const на нее (как показано в dasblinkenlight):

class A {
public: 
  static const int &T;
};

static int dummy = 0;
const int &A::T  = dummy;

int main() {
  dummy = 10;
  std::cout << A::T << std::endl;
}

Live Demo

Метод # 2: используйте статический член non const (как показано R Sahu):

class A {
public: 
  static int T;
};

int A::T = 0;

int main() {
  A::T = 10;
}

Live Demo

Метод №3: объявить скрытую неконстантную переменную как частный статический член вашего класса и предоставить статическую ссылку на константу для интерфейса. Определите функцию друга как инициализатор:

class A {
  friend void foo(int);
    static int dummy;
public: 
    static const int &T;
};

const int &A::T = A::dummy;
int A::dummy = 0;

void foo(int val) { A::dummy = val; }

int main() {
    foo(10);
    std::cout << A::T << std::endl;
}

Live Demo

Метод №4: объявить скрытую неконстантную переменную как частный статический член вашего класса и предоставить статическую ссылку на константу для интерфейса. Определите статическую функцию-член как инициализатор:

class A {
    static int dummy;
public: 
    static const int &T;
    static void foo(int val) { A::dummy = val; }
};

const int &A::T = A::dummy;
int A::dummy = 0;

int main() {
    A::foo(10);
    std::cout << A::T << std::endl;
}

Live Demo

Бонус:

Если вы хотите инициализировать только один раз, вы можете изменить вспомогательную функцию:

static void foo(int val) { 
  static bool init = true;
  if(init) A::dummy = val;
  init = false;
}

Live Demo

Ответ 8

N - O

Семантика того, что требуется, неверна, и вы не должны использовать для нее static-const.

A static - это объект или интегральный тип, который имеет статическую продолжительность хранения и внутреннюю привязку.

A const - это объект, который не меняет его значения на протяжении всего жизненного цикла приложения, любая попытка его изменения приводит к UD. (подавляющее большинство таких случаев - довольно четко определенный крах)

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

Помимо трудностей в поддержании такого кода, проблема остается в том, что заявленная семантика фактически не применяется.

Например, при сохранении значения const во время выполнения приложения можно взломать, выполнив const_cast<int &>(A::T) = 42, что совершенно верно, отлично определяет код, поскольку ссылочный тип не является константой.

То, что здесь запрашивается - это класс, который позволяет инициализировать только один раз во всем приложении, имеет внутреннюю привязку и время жизни приложения.

Итак, просто создайте класс шаблона, который делает это:

template<typename V> class fixation
{
  bool init = true;
 V val;
  public:

    fixation(V const & v) : init(true), val(v) {}

    fixation & operator=( fixation arg)
    {
      if(init )
      {
        this->val = arg.val;
      }
      this->init = false;
      return *this;
    }


    V get()
    {
      return val;
    }
};

struct A
{
    static fixation<int> T;
};

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

Ответ 9

Есть трюк, но вы, вероятно, должны его избегать! Вот пример из голых костей, иллюстрирующий принцип:

int const& foo(int i) {
    static const int j = (i == 0 ? throw 0 : i); 
    return j; 
}

int main() {
    try { 
        int x = foo(0); // oops, we throw        
    } catch(...) {}

    int x = foo(1); // initialized..
    int y = foo(0); // still works..     
}

Осторожно!

Ответ 10

В последнее время, столкнувшись с той же самой проблемой, я обнаружил, что ответ @ASH наиболее близок к идеальному, но тот факт, что переменные должны быть инициализированы так рано, может вызвать некоторые проблемы:

  • Не может использовать источники данных, которые еще не доступны, такие как argc и argv согласно вопросу.
  • Некоторые зависимости еще не инициализированы. Например, многие GUI-структуры не позволяют создавать текстовые поля, которые еще рано. Это проблема, потому что мы могли бы хотеть отобразить текстовое поле ошибки, если загрузка файла конфигурации не информирует пользователя.

Итак, я придумал следующее:

template <class T>
class StaticConfig
{
public:

    StaticConfig()
    {
        if (!mIsInitialised)
        {
            throw std::runtime_error("Tried to construct uninitialised StaticConfig!");
        }
    }

    const T*
    operator -> () const
    {
        return &mConfig;
    }

private:

    friend class ConfigHandler;

    StaticConfig(const T& config)
    {
        mConfig = config;
        mIsInitialised = true;
    }

    static T mConfig;
    static bool mIsInitialised;
};

template <class T>
T StaticConfig<T>::mConfig;
template <class T>
bool StaticConfig<T>::mIsInitialised = false;

Мы делаем наши данные статичными, но неконстантными, поэтому нам не нужно инициализировать их немедленно и мы можем назначить им правильные значения в более подходящее время. Доступ только для чтения предоставляется через перегрузку operator -> Конструктор по умолчанию проверяет, был ли StaticConfig этого типа уже загружен с действительными данными, и выдает, если это не так. Это никогда не должно происходить на практике, но служит средством отладки. Закрытый конструктор позволяет загружать тип с допустимыми данными. Класс ConfigHandler, отвечающий за загрузку данных, стал другом, чтобы он мог получить доступ к приватному конструктору.

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

Онлайн демонстрация.

Ответ 11

Используйте шаблон Singleton здесь. есть элемент данных, который вы хотите инициализировать во время выполнения в одноэлементном классе. Создается один экземпляр, и элемент данных правильно инициализирован, не будет никакого риска переписать его и изменить его.

Синглтон сохранит особенность ваших данных.

Надеюсь, что это поможет.