В C ++, это хорошая форма для написания кода, который выполняется перед main ()? - PullRequest
7 голосов
/ 24 июня 2010

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

Ответы [ 7 ]

18 голосов
/ 24 июня 2010

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

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

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

7 голосов
/ 24 июня 2010

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

Пример: содержание 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)

Вы можете попробовать добавить try ... catch в свою главную, это не поможет.

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

1 голос
/ 24 июня 2010

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

1 голос
/ 24 июня 2010

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

0 голосов
/ 24 июня 2010

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

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

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

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

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

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

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

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

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

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

0 голосов
/ 24 июня 2010

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

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

0 голосов
/ 24 июня 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...