Конструктор С++: мусор при инициализации ссылки const
что не так с этим кодом, почему я получаю неправильный ответ:
class X
{
private:
const int a;
const int& b;
public:
X(): a(10) , b(20)
{
// std::cout << "constructor : a " << a << std::endl;
// std::cout << "constructor : b " << b << std::endl;
}
void display()
{
std::cout << "display():a:" << a << std::endl;
std::cout << "display():b:" << b << std::endl;
}
};
int
main(void)
{
X x;
x.display();
return 0;
}
Приведенный выше код даст мне результат как
display():a:10
display():b:1104441332
Но если я удаляю прокомментированные 2 строки внутри конструктора по умолчанию, он дает правильный результат, который
constructor : a 10
constructor : b 20
display():a:10
display():b:20
Пожалуйста, помогите, спасибо
Ответы
Ответ 1
Вы инициализируете b
как ссылку на временную.
Значение 20
создается и существует только для области действия конструктора.
Поведение кода после этого очень интересно - на моей машине я получаю разные значения от тех, которые вы отправили, но основное поведение по-прежнему остается недетерминированным.
Это связано с тем, что, когда значение, с которым ориентировочные точки выпадают из области видимости, начинает вместо этого ссылаться на память мусора, что дает непредсказуемое поведение.
См. Содержит ли ссылка на константу срок службы временного?; ответ fooobar.com/questions/50373/... ссылается на соответствующий раздел стандарта С++, в частности, следующий текст:
A temporary bound to a reference member in a constructor’s ctor-initializer
(12.6.2) persists until the constructor exits.
Вот почему вы всегда получаете правильное значение в печати внутри конструктора и редко (но, возможно, иногда!) после. Когда конструктор выходит, ссылка оборвалась и все ставки отключены.
Ответ 2
Я позволю своему компилятору ответить на этот вопрос:
$ g++ -std=c++98 -Wall -Wextra -pedantic test.cpp
test.cpp: In constructor 'X::X()':
test.cpp:9:26: warning: a temporary bound to 'X::b' only persists until the constructor exits [-Wextra]
$
Вы также должны включить предупреждения в свой компилятор.
Ответ 3
b
относится к временному. То, что вы прочитали (при печати), является недопустимым местоположением к моменту его считывания, поскольку временный 20
технически вышел из области видимости.
Чтобы объяснить непоследовательные результаты:
Это поведение undefined. То, что вы видите, может быть другим, если вы:
- изменить свой компилятор
- изменить настройки компилятора
- построить для другой архитектуры
- изменить макет члена вашего класса
- добавить или удалить вещи из области памяти рядом с экземпляром
x
- и др.
Вы всегда должны избегать поведения undefined.
Но почему изменилось значение? Ваша ссылка скорее всего относится к адресу стека, который был переписан (например, повторно использован) к моменту его печати.
Ответ 4
Вы привязываете const&
к временному, который не живет за вызовом конструктора. В стандарте С++ 03 уточняется, что "временная привязка к ссылочному элементу в конструкторах ctor-initializer (12.6.2) сохраняется до тех пор, пока конструктор не выйдет" (12.2/5 "Временные объекты" ).
Таким образом, ваш код имеет поведение undefined - вы можете получить бессмыслицу или что-то, что кажется "рабочим".
FWIW, MSVC 2010 дает следующее предупреждение в этом коде:
C:\temp\test.cpp(12) : warning C4413: 'X::b' : reference member is initialized to a temporary that doesn't persist after the constructor exits