Что происходит, когда ссылка на С++ оставляет эту область видимости?

Если я правильно интерпретирую ссылки на С++, они похожи на указатели, но с гарантированной целостностью данных (нет NULL, no (int *) 0x12345). Но что происходит, когда область ссылочного объекта выделена? Если нет волшебства (и, вероятно, это не так), ссылочный объект будет уничтожен за моей спиной.

Я написал фрагмент кода, чтобы проверить это:

#include <iostream>
using namespace std;

class A {
public:
        A(int k) { _k = k; };
        int get() { return _k; };
        int _k;
};

class B {
public:
        B(A& a) : _a(a) {}
        void b() { cout << _a.get(); }
        A& _a;
};

B* f() {
        A a(10);
        return new B(a);
}

int main() {
        f()->b();
}

Для проверки существования фрейма стека была вставлена ​​переменная экземпляра _k.

Это неожиданно не segfault и вместо этого печатает "10" правильно, в то время как я полагаю, что A выделяется в стеке, и кадр стека f() будет перезаписан по меньшей мере вызовом cout<<.

Ответы

Ответ 1

это поведение undefined, и вам просто повезло, что память для a еще нигде не использовалась. В более сложном сценарии вы почти наверняка получите мусор. На моей машине я получаю случайный мусор с этим кодом. Для меня это, вероятно, потому, что я использую 64-битную машину, которая использует соглашение о вызове регистра. Регистры повторно используются гораздо чаще, чем основная память (в идеале...).

Итак, чтобы ответить на ваш вопрос "что происходит". В этом случае ссылка скорее всего будет немного больше, чем ограниченный указатель с более дружественным синтаксисом:-). Под капотом сохраняется адрес a. Позже объект a выходит за пределы области видимости, но ссылка объекта B на a не будет обновляться автоматически, чтобы отразить это. Следовательно, теперь у вас есть неверная ссылка.

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


EDIT: Благодаря Omnifarious, я думал об этом. В С++ есть правило, которое в основном говорит о том, что если у вас есть ссылка const на временную, то время жизни временного не меньше длины ссылки const. Что ввело новый вопрос.

РЕДАКТИРОВАТЬ: Переместился на отдельный вопрос для заинтересованных (ссылка const на временную нечетность)

Ответ 2

В языке С++ время жизни объекта, к которому привязана ссылка, никак не привязано к времени жизни самой ссылки, с единственным исключением (см. ниже).

Если объект разрушен до ссылки, ссылка становится недействительной (например, указатель висячего). Любые попытки доступа к этой ссылке приводят к поведению undefined. Это то, что происходит в вашем примере.

Ссылка заканчивает свое время жизни до того, как объект делает, тогда... ну, ничего не происходит. Ссылка исчезает, объект продолжает жить.

Единственное исключение, о котором я говорил выше, - это когда инициализируется ссылка на константу с помощью временного временного объекта. В этом случае время жизни временного объекта привязывается к времени жизни ссылки. Когда эталонная матрица умирает, объект умирает с ней

{
   const std::string& str = "Hello!"; 
   // A temporary `std::string` object is created here...
   ...
   // ... and it lives as long as the reference lives
   ...
} // ... and it dies here, together with the reference

P.S. Вы неправильно используете термин scope. Область видимости - это область видимости для идентификатора. Объем сам по себе не имеет ничего общего с временем жизни объекта. Когда что-то "оставляет сферу" в общем случае, что что-то не разрушается. Популярным заблуждением является использование формулировки "оставляет видимость" для обозначения точки уничтожения объекта.