删除对象时引用会发生什么情况?
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。
我做了一些实验来尝试理解 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。