删除对象时引用会发生什么情况?

What happens to a reference when the object is deleted?

我做了一些实验来尝试理解 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

我不明白这是怎么回事。我从 C++ FAQ:

中找到了这段摘录

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 对此进行了测试

该注释具有误导性,将引用视为指针的语法糖作为心智模型很好。在指针可能悬挂的所有方式中,引用也会悬挂。访问悬挂 pointers/references 是未定义的行为 (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

这也扩展到标准容器及其关于 pointer/reference 失效的规则。您的示例中发生任何令人惊讶的行为的原因仅仅是 UB。