В C++ это - хорошая форма для написания кода, который выполняется прежде основной ()?

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

7
задан Bruce 24 June 2010 в 14:38
поделиться

7 ответов

Это не обязательно плохая идея, но обычно да.

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

Во-вторых, C ++ не гарантирует порядок инициализации статических объектов, определенных в разных единицах перевода (файлы .cpp), поэтому, если они зависят друг от друга, у вас могут быть проблемы.

18
ответ дан 6 December 2019 в 06:35
поделиться

Да, это плохо. Поскольку у вас не будет возможности перехватывать исключения и обрабатывать их, будет использоваться обработчик по умолчанию. В C ++ это означает вызов terminate ...

Пример: contents a.cpp

#include <stdexcept>

int f() { throw std::runtime_error("boom"); return 42; }
int i = f();

int main(int argc, char* argv[])
{
  return 0;
}

Вывод: g ++ a.cpp && ./a.out

terminate called after throwing an instance of 'std::runtime_error'
  what():  boom
Aborted (core dumped)

Вы можете попробовать добавить попробуйте ... поймайте в вашем main, это не поможет.

Редактировать: Очки Джальфа тоже действительны. Послушайте его совет.

7
ответ дан 6 December 2019 в 06:35
поделиться

Использование глобальных / статических объектов с нетривиальными конструкторами и деструкторами ужасно . Я видел достаточно огромных программных проектов, которые оказались в катастрофе из-за неконтролируемого использования глобальных / статических объектов и синглтонов.

Проблема не в том, что это код, который выполняется вне main . Дело в том, что эти объекты создаются и уничтожаются в неконтролируемом порядке .

Кроме того, я считаю, что в любом случае использование глобальных переменных (даже обычных) - плохая практика, за некоторыми исключениями. Каждый фрагмент кода должен выполняться в четко определенном контексте, и все переменные должны принадлежать ему.

0
ответ дан 6 December 2019 в 06:35
поделиться

В дополнение к сомнительной форме - он не будет переноситься на некоторые платформы.

1
ответ дан 6 December 2019 в 06:35
поделиться

Как вы отметили, это разрешено . В последней компании, в которой я работал, когда возникала такая ситуация, мы установили политику добавления соответствующего комментария к main (), чтобы указать, к каким переменным это применимо. Если у вас плохая ситуация, постарайтесь извлечь из нее максимум пользы.

1
ответ дан 6 December 2019 в 06:35
поделиться

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

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

0
ответ дан 6 December 2019 в 06:35
поделиться

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

// the following invokes undefined behavior.
static bool success=cout<<"hello world\n";

Люди обычно не пишут код, подобный приведенному выше, но подумайте, если бы success был инициализирован возвращаемым значением другой функции. Теперь любой, кто соблазнится использовать cout или cerr или любой другой глобальный объект в этой функции, вызовет такое же неопределенное поведение.

Для пользовательских типов с конструкторами и деструкторами рассмотрим альтернативный метод, где доступ является инициализацией:

static Foo& safe_static()
{
    static Foo f;
    return f;
}

К сожалению, это также имеет проблемы с безопасностью потоков, поэтому для построения 'f' требуется какой-то механизм блокировки, если вы обращаетесь к safe_static параллельно.

Тем не менее, я считаю, что нужно стараться сохранять простоту. К сожалению, когда речь идет о пользовательских объектах, определенных в области видимости файла, слишком легко столкнуться с неопределенным поведением. Небольшие дополнительные усилия, необходимые для написания чего-то вроде safe_static выше, могут предотвратить много головной боли.

Исключения - это еще один момент. Если ваши статические объекты выбрасываются из своего конструктора, то у вас нет возможности перехватить исключение. Если вы хотите, чтобы ваше приложение было действительно надежным и даже обрабатывало ошибки при запуске, вам придется тщательно структурировать код (например: иметь блоки try/catch в конструкторах для объектов, создаваемых в области видимости файла, чтобы исключение не выбрасывалось за пределы ctor, а также избегать списков инициализаторов, которые выбрасывают).

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

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

0
ответ дан 6 December 2019 в 06:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: