Конструктор C ++: мусор при инициализации константной ссылки - PullRequest
21 голосов
/ 04 февраля 2012

что не так с этим кодом, почему я получаю неправильный ответ:

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

, пожалуйста, помогите, спасибо

Ответы [ 4 ]

24 голосов
/ 04 февраля 2012

Вы инициализируете b как ссылку на временный .

Значение 20 создано и существует только для области видимости конструктора.

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

Это потому, что когда значение, на которое ссылаетсяТочки выпадают из области видимости, вместо этого они начинают ссылаться на мусорную память, что приводит к непредсказуемому поведению.

См. Продлевает ли ссылка на const жизнь временного? ;ответ https://stackoverflow.com/a/2784304/383402 ссылается на соответствующий раздел стандарта C ++, в частности на приведенный ниже текст:

A temporary bound to a reference member in a constructor’s ctor-initializer
(12.6.2) persists until the constructor exits.

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

19 голосов
/ 04 февраля 2012

Я позволю моему компилятору ответить на этот вопрос:

$ 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]
$

Вы должны также включить предупреждения на вашем компиляторе.

4 голосов
/ 04 февраля 2012

b относится к временному. То, что вы прочитали (при печати), является недопустимым местом к моменту его прочтения, поскольку временный 20 технически вышел из области действия.

Чтобы объяснить противоречивые результаты:

Это неопределенное поведение. То, что вы видите, может отличаться, если вы:

  • смените компилятор
  • изменить настройки компилятора
  • сборка для другой архитектуры
  • изменить макет члена вашего класса
  • добавить или удалить объекты из области памяти рядом с экземпляром x
  • и т.д.

Вы всегда должны избегать неопределенного поведения.

Но почему бы изменить значение? Ваша ссылка, вероятно, относится к адресу стека, который был переписан (например, повторно) к моменту его печати.

3 голосов
/ 04 февраля 2012

Вы связываете const& с временным, который не живет вне вызова конструктора. Стандарт C ++ 03 специально говорит, что «временная привязка к элементу ссылки в ctor-initializer конструктора (12.6.2) сохраняется до выхода из конструктора» (12.2 / 5 «Временные объекты»).

Таким образом, ваш код имеет неопределенное поведение - вы можете получить чепуху или что-то вроде "работающего".

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
...