Можно ли инициализировать статический член 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]);
}
Если это невозможно, какой тип переменной я должен использовать? Мне нужно инициализировать его во время выполнения, а также сохранить свойство константы.
Ответы
Ответ 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 здесь.
есть элемент данных, который вы хотите инициализировать во время выполнения в одноэлементном классе. Создается один экземпляр, и элемент данных правильно инициализирован, не будет никакого риска переписать его и изменить его.
Синглтон сохранит особенность ваших данных.
Надеюсь, что это поможет.