Инициализатор Ctor: само инициализация вызывает сбой?
Мне пришлось тяжело отлаживать крах при производстве. Просто хотел подтвердить, что у нас здесь семантика. У нас есть класс вроде...
class Test {
public:
Test()
{
// members initialized ...
m_str = m_str;
}
~Test() {}
private:
// other members ...
std::string m_str;
};
Кто-то изменил инициализацию, чтобы использовать списки инициализации ctor, которые являются разумно правильными в нашей семантике кода. Порядок инициализации и их начальное значение являются правильными между прочим. Итак, класс выглядит как...
class Test {
public:
Test()
: /*other inits ,,, */ m_str(m_str)
{
}
~Test() {}
private:
// other members ...
std::string m_str;
};
Но код внезапно начал сбой! Я выделил длинный список inits для этого фрагмента кода m_str(m_str)
. Я подтвердил это через текст ссылки.
Нужно ли это терпеть крах? Что говорит об этом стандарт? (Это поведение undefined?)
Ответы
Ответ 1
Первый конструктор эквивалентен
Test()
: m_str()
{
// members initialized ...
m_str = m_str;
}
то есть к тому времени, когда вы перейдете к назначению внутри конструктора, m_str
уже неявно инициализируется пустой строкой. Таким образом, присваивание себе, хотя и совершенно бессмысленное и лишнее, не вызывает проблем (поскольку std::string::operator=()
, как и любой хорошо написанный оператор присваивания, должен проверять самоопределение и ничего не делает в этом случае).
Однако во втором конструкторе вы пытаетесь инициализировать m_str
с самим собой в списке инициализаторов - в этот момент он еще не инициализирован. Таким образом, результат undefined.
Обновление:. Для примитивных типов это поведение undefined (в результате получается поле с размером мусора), но оно не падает (обычно - см. комментарии ниже для исключений), потому что примитивный типы по определению не имеют конструкторов, деструкторов и не содержат указателей на другие объекты.
То же самое верно для любого типа, который не содержит элементов указателя с семантикой собственности. std::string
, как показано, не является одним из следующих: -)
Ответ 2
m_str
строится в списке инициализации. Поэтому в то время, когда вы назначаете его себе, он не полностью построен. Следовательно, поведение undefined.
(Что должно делать это самозадача?)
Ответ 3
Исходная "инициализация" по назначению полностью лишняя.
Это не принесло никакого вреда, кроме потерь процессорных циклов, потому что во время назначения член m_str уже был инициализирован по умолчанию.
Во втором фрагменте кода инициализация по умолчанию переопределяется, чтобы использовать элемент un-isitialized, который еще не инициализирован для инициализации. Это Undefined Поведение. И это совершенно не нужно: просто удалите это (и не заново вводите исходное время, просто, удалите).
Подняв уровень предупреждения вашего компилятора, вы сможете получить предупреждения об этом и подобном тривиально некачественном коде.
К сожалению, проблема, с которой вы сталкиваетесь, не такая техническая, она гораздо более фундаментальная. Он, как рабочий в автомобиле factory, задает вопрос о квадратных колесах, которые они наносят на новый автомобиль. Тогда проблема заключается не в том, что квадратные колеса не работают, а потому, что многие инженеры и менеджеры принимали участие в решении использовать фантастические квадратные колеса, и никто из них не возражал - некоторые из них, несомненно, Не понимаю, что квадратные колеса не работают, но большинство из них, я подозреваю, просто боялись сказать, на что они были на 100% уверены. Так что это, скорее всего, проблема управления. Извините, но я не знаю, как это исправить...
Ответ 4
Undefined поведение не должно приводить к сбою - он может делать что угодно, от продолжения работы, как если бы не было никакой проблемы, немедленно рушиться, делать что-то действительно странное, что, по-видимому, вызывает не связанные проблемы позже. Каноническая претензия заключается в том, что она заставляет "демонов вылетать из вашего носа" (ака, "вызывает носовых демонов" ). В свое время у изобретателя фазы был (довольно крутой) веб-сайт, рассказывающий о ядерной войне, которая началась от кого-то, вызывающего поведение undefined в "DeathStation 9000".
Изменить: точная формулировка стандарта (§: 1.3.12):
1.3.12 undefined поведение [defns.undefined]
например, может возникнуть при использовании ошибочной конструкции программы или ошибочных данных, для которой это Международный стандарт не предъявляет никаких требований. undefined также можно ожидать, когда это В Международном стандарте отсутствует описание любого явного определения поведения. [Примечание: допустимо undefinedповедение варьируется от полного игнорирования ситуации с непредсказуемыми результатами, поведения во время перевод или выполнение программы в документально оформленной форме, характерной для окружающей среды (с или без выдача диагностического сообщения), до прекращения перевода или исполнения (с выдачей диагностическое сообщение).
Ответ 5
Это та же разница, что и между
std::string str;
str = str;
и
std::string str(str);
Первый работает (хотя это вздор), последний не делает, поскольку он пытается скопировать-построить объект из еще не построенного объекта.
Конечно, путь был бы
Test() : m_str() {}