假设以下代码:
Foo & foo = getFoo();
foo.attr; // 100% safe access?
如果foo
是指针,我会检查它是否为NULL
,但是因为它是引用,所以这种检查是不必要的。我想知道的是,是否有可能弄乱对象的引用,以致它会使其属性不安全。
我尝试了一些示例,例如尝试将NULL
转换为Foo
对象,但是我遇到了编译错误。我只是想确保上面的代码总是安全的,并且我不应该知道内部C++
黑魔法。
根据本杰明的回答,我可以制作一个示例代码,我从引用中得到segmentation fault,因此它回答了我的问题。我会粘贴我的代码,以防有人对未来感兴趣:
#include <iostream>
using namespace std;
class B
{
public:
int x;
B() {x = 5;}
};
class A
{
public:
void f()
{
b = *(B*)NULL;
}
B & getB()
{
return b;
}
B b;
};
int main()
{
A a;
a.f();
cout << a.getB().x << endl;
return 0;
}
答案 0 :(得分:28)
是的,这是可能的。
Foo& Fr = *(Foo*)nullptr;
从技术上讲,这是取消引用该指针的未定义行为。但它很可能不会导致任何可观察到的错误。这可能会:
Fr.attr = 10;
然而,正如Jonathan Wakely在评论中指出的那样,你没有理由检查这样的案例。如果函数返回无效引用,则该函数被破坏,需要修复。假设引用有效,您的使用代码不会被破坏。但是,正如David Schwartz的回答中所提到的,完全合法的代码中有效引用 无效(尽管不为空)。但是你没办法检查这个。您只需知道它可能发生的情况,然后停止使用该引用。
答案 1 :(得分:23)
引用必须在引用该引用时引用有效对象。这是一个C ++标准要求,违反它的任何代码都是UB,可以做任何事情。
然而,在参考坐下之后销毁参考引用的对象是完全合法的。此时,访问引用是非法的。例如:
std::vector<int> j;
j.push_back(3);
int& k = j.front(); // legal, object exists now
j.clear(); // legal, object may be destroyed while reference exists
k++; // illegal, destruction of object invalidates reference
这意味着返回引用的函数必须始终返回一个在返回时有效的引用。这就是在空向量上调用front
为UB的原因 - 引用必须在其就位时有效。但是,通常会有条件随后使该引用无效,如果您打算尝试存储引用并稍后访问它,则需要了解这些条件是什么。
通常,您应该假设存储返回的引用并在以后访问它是不安全的,除非您知道引用仍然有效。例如,std::vector
会仔细解释在什么条件下对容器的引用可能无效,并且包括对push_back
的后续调用。所以这被打破了:
std::vector<int> j;
j.push_back(3);
int &first = j.front();
j.push_back(4);
int &second = j.back();
if (first == second) // illegal, references into container are invalidated by push_back
但这很好:
std::vector<int> j;
j.push_back(3);
j.push_back(4);
int &first = j.front();
int &second = j.back();
if (first == second) // legal, references into container stay valid
答案 2 :(得分:17)
可以引用不良内存。所以foo.attr; // 100% safe access?
的答案是否定的。请考虑以下示例:
int &something() {
int i = 5, &j = i;
return j; // Return reference to local variable. This is destroyed at end of scope.
}
int main() {
int &k = something(); // Equivalent to getFoo()
std::cout << k << endl; // Using this reference is undefined behavior.
return 0;
}
引用k未指向合法内存。但这仍然会编译。然而,在程序员没有犯错的情况下,情况并非如此。在这种情况下,函数something()
写得不正确,需要修复。没有办法或理由检查这一点。如果一个函数返回一个错误的引用,那么你唯一可以(而且应该)做的就是修复有问题的函数。