Почему это работает? Возвращение константных ссылок в C++
Я дурачусь с C++ и константными ссылками и растерялся, почему этот код работает:
#include <iostream>
class A {
public:
A() : a_(50) {}
const int& getA() const { return a_; }
private:
const int a_;
};
int main(int argc, char* argv[])
{
A* a = new A();
const int& var = a->getA();
std::cout << var << std::endl;
delete a;
std::cout << var << std::endl;
}
Результат:
50
50
Вот мои мысли:
var хранит ссылку на a_.
когда a удаляется, a_ также должен быть удален.
при повторном доступе к var он больше не содержит действительной ссылки, и должна возникнуть ошибка сегментации.
Почему это работает? Я не верю, что я делаю временную копию.
4 ответа
В тот момент, когда вы удалили a
, доступ к var
стал вашей дверью в неопределенную страну поведения.
Это "работает" случайно. Пространство где var
был тот, кто имеет в виду, больше не ваш, но вы получили доступ к нему, на этот раз. Это могло привести к ошибке сегментации, возвращению числа, отличного от 50, или переформатированию жесткого диска.
Помните, что казаться работающим - это один из возможных способов проявить неопределенное поведение.
Удаление объекта не очищает память. Значение будет оставаться там до тех пор, пока память не будет использована для чего-то другого. Так что это может работать некоторое время....
Некоторые реализации C++ имеют "режим отладки", который устанавливает определенное значение для всей удаленной памяти, чтобы обнаруживать ошибки, подобные этой.
Когда вы удаляете a, вы освобождаете память и позволяете последним новым перезаписывать ее. До этого момента все переменные внутри вашего удаленного объекта все еще находятся в памяти, но могут быть переопределены в любое время.
Это довольно сложно из-за const
ключевое слово.
Правда, в этом случае вы могли бы читать неинициализированную память. Некоторые мысли по этому поводу:
- Вы не используете режим отладки: это, как правило, не очень хорошая идея, если код не был протестирован, но он оставляет две возможности:
- Диспетчер памяти режима освобождения не перезаписывает память, поэтому вы получаете доступ к последнему известному адресу, который все еще работает случайно
- ИЛИ Вся операция полностью оптимизирована, потому что компилятор знает, что вы не изменили значение, и оно не может измениться извне (хотя это может быть неверно из-за ограничений правильности const в C++)
- Вы находитесь в режиме отладки, но активированы оптимизации, поэтому применяется тот же аргумент
-
Содержание_a
, так как отмеченыconst
не распределяются ни в куче, ни в стеке, а находятся вDATA
раздел приложения, так что ссылка действительно может быть действительной, причем не только случайно.[РЕДАКТИРОВАТЬ]: Это может быть верно только дляstatic const
переменные.
Вы можете написать собственный менеджер памяти или исследовать поведение режима отладки вашего компилятора, потому что это очень, очень важно. Visual Studio установит переменные в 0xCDCDCDCD
, например. Вы также найдете забавные значения, такие как 0xDEADC0DE
в конце массивов.