Что происходит со ссылкой при удалении объекта?

Я провел небольшой эксперимент, пытаясь понять ссылки в C++:

#include <iostream>
#include <vector>
#include <set>

struct Description {
  int a = 765;
};

class Resource {
public:
  Resource(const Description &description) : mDescription(description) {}

  const Description &mDescription;
};

void print_set(const std::set<Resource *> &resources) {
    for (auto *resource: resources) {
        std::cout << resource->mDescription.a << "\n";
    }
}

int main() {
  std::vector<Description> descriptions;
  std::set<Resource *> resources;

  descriptions.push_back({ 10 });
  resources.insert(new Resource(descriptions.at(0)));

  // Same as description (prints 10)
  print_set(resources);

  // Same as description (prints 20)
  descriptions.at(0).a = 20;
  print_set(resources);

  // Why? (prints 20)
  descriptions.clear();
  print_set(resources);

  // Object is written to the same address (prints 50)
  descriptions.push_back({ 50 });
  print_set(resources);

  // Create new array
  descriptions.reserve(100);

  // Invalid address
  print_set(resources);

  for (auto *res : resources) {
      delete res;
  }
  
  return 0;
}

https://godbolt.org/z/TYqaY6Tz8

Я не понимаю, что здесь происходит. Я нашел этот отрывок из Часто задаваемые вопросы по С++:

Important note: Even though a reference is often implemented using an address in the underlying assembly language, please do not think of a reference as a funny looking pointer to an object. A reference is the object, just with another name. It is neither a pointer to the object, nor a copy of the object. It is the object. There is no C++ syntax that lets you operate on the reference itself separate from the object to which it refers.

Это вызывает у меня некоторые вопросы. Итак, если ссылка является самим объектом, и я создаю новый объект по тому же адресу памяти, означает ли это, что ссылка «становится» новым объектом? В приведенном выше примере векторы представляют собой линейные массивы; поэтому, пока массив указывает на один и тот же диапазон памяти, объект будет действительным. Однако это становится намного сложнее, когда используются другие наборы данных (например, наборы, карты, связанные списки), потому что каждый «узел» обычно указывает на разные части памяти.

Должен ли я рассматривать ссылки как неопределенные, если исходный объект уничтожен? Если да, есть ли способ определить, что ссылка уничтожена, кроме специального механизма, который отслеживает ссылки?

Примечание. Протестировано с помощью GCC, LLVM и MSVC.

Это зависит от того, как вы замените объект. С размещением new старые ссылки относятся к новому объекту (в большинстве случаев). Если вы clear() и push_back(), это технически Undefined Behavior, поскольку clear() делает недействительными все ссылки на элементы, даже если это, скорее всего, будет выглядеть так, как будто оно работает каждый раз, когда вы пытаетесь это сделать.

François Andrieux 18.03.2022 15:29

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

463035818_is_not_a_number 18.03.2022 15:30

связанные/обман: stackoverflow.com/questions/6438086/…

NathanOliver 18.03.2022 15:30

все еще не совсем точно, но, может быть, лучше «действительная ссылка - это объект».

463035818_is_not_a_number 18.03.2022 15:31

«Должен ли я рассматривать ссылки как неопределенные, если исходный объект уничтожен?» Да. "есть ли способ определить, что ссылка уничтожена" Нет.

Quimby 18.03.2022 15:32

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

Gasim 18.03.2022 15:37

ИМО, примечание больше вводит в заблуждение, чем разъясняет. Возможно, вам будет легче понять, если вы забудете об этом.

Passer By 18.03.2022 15:47

@PasserBy есть ли другой справочник, к которому я могу обратиться по поводу спецификации для ссылок?

Gasim 18.03.2022 15:47

@Gasim Я не знаю хорошей книги, чтобы узнать конкретно о ссылках. Но вы можете прочитать cppreference.

Passer By 18.03.2022 15:49

Спасибо, это то, что я искал. Есть даже раздел о висячих ссылках.

Gasim 18.03.2022 15:56

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

apple apple 18.03.2022 16:03

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

apple apple 18.03.2022 16:05

вы также можете увидеть en.cppreference.com/w/cpp/language/lifetime

apple apple 18.03.2022 16:09
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
13
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Заметка вводит в заблуждение, трактовка ссылок как синтаксического сахара для указателей хороша как ментальная модель. Во всех случаях, когда указатель может болтаться, ссылка также будет болтаться. Доступ к оборванным указателям/ссылкам является неопределенным поведением (UB).

int* p = new int{42};
int& i = *p;
delete p;

void f(int);
f(*p); // UB
f(i);  // UB, with the exact same reason

Это также распространяется на стандартные контейнеры и их правила об аннулировании указателя/ссылки. Причина любого неожиданного поведения в вашем примере — просто UB.

Другие вопросы по теме