Неявная инициализация переменных значением по умолчанию

valera_ee
valera_ee

Работая над большим проектом, который начал своё начало ещё до С++11, столкнулся с проблемами адаптации кода под новый стандарт. Все поля класса инициализируются в конструкторе или в списке инициализации, около 95% такой инициализации это присваивание переменным класса 0 или NULL. 

class ClassName
{
public:
    ClassName(ClassParent *p);
    ~ClassName();

    void setValue(int value);
    int value() const;

private:
    ClassParent *pParent;
    bool m_enable;
    int  m_value;
};

Сейчас можно прописать инициализацию в заголовочном файле:

class ClassName
{
public:
    ClassName(ClassParent *p);
    ~ClassName();

    void setValue(int value);
    int value() const;

private:
    ClassParent *pParent {nullptr};
    bool m_enable {false};
    int  m_value  {0};
};

 Возникает логичный вопрос, почему изначально не инициализировать базовые типы нулём, а указатели значением nullptr ? А там где требуется указать значение, отличное от значения по умолчанию, там прописывать вручную. 

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

-2
рейтинг
7 комментариев
BlackMat MATov

П - перформанс

BlackMat MATov
Дмитрий

> Был случай когда указатель забыли проинициализировать ...

Используйте smart pointer

Дмитрий
valera_ee

Дмитрий, сейчас, конечно только ими ипользуюсь, но что делать с сотнями тысяч строк старого кода? Их все разом переписать не получится, это процесс долгий и затратный.

valera_ee
al-mission-2016

При проектировании языка программирования приходится решать дилемму - производительность vs "меньше ошибок по невнимательности". Когда в конце 60х изобретали С, инициализация по-умолчанию была непозволительной роскошью.


Относительно инициализации дилемма такова:
[A] по-умолчанию ничего не инициализируется; безопасные начальные значения - это ответственность программиста;
[B] всё инициализируется по-умолчанию, за исключением случаев, когда явно указано, что инициализировать не надо.


В С++ очевидно принят подход [А] и это решение слишком фундаментально, чтобы его можно было поменять.
Можно только помочь бороться с последствиями. :)


Причины подхода [А] в С++:
1)  Совместимость с языком С - там нет инициализации по-умолчанию.
2)  Производительность = как основа философии С/С++.
    Например, в 3D-app требуется создать 1'000'000 треугольников. Нулевые значения смысла не имеют, всё равно они будут перезаписаны при загрузке сцены. Здесь нулевая инициализация - performance hit. Компилятор далеко не всегда может устранить лишние присваивания.
3)  требования обратной совместимости с предшествующими версиями С++, чтобы избежать изменения поведения старого кода, при компиляции в режиме С++11++.
4)  для пользовательских типов имеются: конструкторы и их списки инициализации, а в С++11/14 добавили инициализация членов-данных при их декларации.
    Этих инструментов достаточно, чтобы обеспечить значения по-умолчанию, где это необходимо.
    Кроме того, локальные переменные в С++ рекомендуется создавать как можно ближе к месту их использования и соответственно сразу инициализировать их разумными значениями.

Теперь предположим, что мы изменили С++, введя инициализацию по-умолчанию.
Одновременно потребуется добавлять механизм, позволяющий явно указать, как не инициализировать в случаях, когда важна производительность, а
значения по-умолчанию не подходят.
Напр.:
int x = void; // as in D-language

В результате придётся дорабатывать тот legacy-code, в котором инициализация по-умолчанию начнёт бить по производительности!
То есть решая проблему одних, мы создадим проблему другим.
Не стоит оно того. :)


> valera_ee
Попробуйте статические анализаторы кода.

al-mission-2016
valera_ee

al-mission-2016, это всё, конечно, хорошо, но вместо голых быстрых указателей мы начали использовать медленный, но безопасный вариант умных указателей, и производительность падает, вместо голых массивов std::vector и это всё замедляет программу, так о какой производительности тогда говорить? Сейчас вообще есть constexpr, мы получили возможность инициализировать константы на этапе компиляции, так , а что мешает делать тоже самое для полей класса у которых отсутствует явная инициализация? 

valera_ee
Fihtangolz

valera_ee, хз я вообше предлагаю просто выкинуть фундаментальные типы написать классы int, float, double, оставить только что то типа byte в качестве фундаментального типа, ну собственно дальше кому надо сделают инициализацию по умолчанию

Fihtangolz
al-mission-2016

> Fihtangolz,
в рамках собственного проекта можно делать что угодно, хоть на потолке спать. (: Только вряд ли стоит тащить в стандарт языка обратно-несовместимые вещи, как предложил Валерий. :)


> valera_ee,
ваше предложение означает поменять правила игры на лету, когда решение уже давно принято и куча легаси кода полагается на него.

> "что мешает делать тоже самое для полей класса у которых отсутствует явная инициализация?"
Мешает обратная совместимость. Часть кода (правда довольно специфичного) просто поломается.
Например, поломается код, который принципиально предполагает (в соответствии с нынешним стандартом), что если поле класса нигде явно не проинициализированно, то компилятор в него точно ничего не запишет. Это важно для embedded/firmware программирования.
Используя placement-new (и volatile) можно отобразить структуру, т.е. её члены-данные, на память, которая является пространством портов ввода-вывода. Теперь получаем, что "старые" версии компилятора ничего не писали в поля-порты без ведома программиста. А "новый" компилятор, инициализируя поля значениями по-умолчанию, запишет в порты нули!
В принципе, этого примера достаточно, чтобы отклонить ваше предложение.


По производительности. Да, компьютеры стали быстрее, память дешевле, можно позволить размен - чуть медленнее софт, зато потенциально чуть меньше ошибок, код пишется быстрее и т.д. Для многих проектов это вполне приемлемо. Но заметьте, std::vector, умные указатели и т.п. - это опция, у нас есть выбор. Если нужна максимальная производительность, можно обойтись без них. А ваше предложение - всегда инициализировать - принудительно.
Кстати, некоторые game-dev'ы, типа EA, писали в своё время собственные версии STL, когда их не устраивала производительность контейнеров общего назначения.

al-mission-2016
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения