为什么我不需要检查引用是否无效/为空?
阅读http://www.cprogramming.com/tutorial/references.html,它说:
一般来说,参考文献应该始终 是有效的,因为你必须始终 初始化一个引用。这意味着 除非有一些奇怪的事情 情况(见下文),您可以 确定使用参考只是 就像使用普通的旧非参考 多变的。您无需检查 确保参考不是 指向 NULL,你不会得到 被未初始化的引用咬住 你忘记分配内存了 对于。
我的问题是我如何知道在初始化引用后该对象的内存尚未被释放/删除。
归根结底,我不能凭信心接受这个建议,我需要更好的解释。
任何人都可以透露一些信息吗?
Reading http://www.cprogramming.com/tutorial/references.html, it says:
In general, references should always
be valid because you must always
initialize a reference. This means
that barring some bizarre
circumstances (see below), you can be
certain that using a reference is just
like using a plain old non-reference
variable. You don't need to check to
make sure that a reference isn't
pointing to NULL, and you won't get
bitten by an uninitialized reference
that you forgot to allocate memory
for.
My question is how do I know that the object's memory hasn't been freed/deleted AFTER you've initialized the reference.
What it comes down to is that I can't take this advice on faith and I need a better explanation.
Can anyone shed some light?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
您无法知道引用是否无效:
除非注意如何使用引用,否则无法知道您的引用是否引用了有效内存。例如,如果您不确定何时删除内存,则您不想使用在堆上创建的内容的引用。
您也永远无法知道您使用的指针是否指向有效的内存。
您可以对指针和引用执行 NULL 检查,但通常您永远不会对引用执行 NULL 检查,因为没有人会编写这样的代码:
何时使用引用?
您可能想要使用在这样的情况下引用:
使用引用搬起石头砸自己的脚是很困难的:
使用引用搬起石头砸自己的脚比使用指针要困难得多。
例如:
您不能使用引用执行此类操作,因为引用必须使用其值进行初始化。并且您只能使用您范围内的值来初始化它。
你仍然可以通过参考文献搬起石头砸自己的脚,但你必须真正尝试去做。
例如:
You can't know if references are invalid:
There is no way to know if your reference is referencing valid memory except by taking care of how you use references. For example you don't want to use a reference with something created on the heap if you are unsure when the memory will be deleted.
You also can never know whether the pointer you are using is pointing to valid memory or not as well.
You can do NULL checks with both pointers and references but typically you would never do a NULL check with a reference because no one would ever write code like this:
When to use a reference?
You probably want to use references in cases like this:
Shooting yourself in the foot is hard with references:
It's much harder to shoot yourself in the foot with references than it is with pointers.
For example:
You can't do this sort of thing with references since a reference must be initialized with it's value. And you can only initialize it with values that are in your scope.
You can still shoot yourself in the foot with references, but you have to REALLY try to do it.
For example:
你不能。你也不能用指针。考虑一下:
现在,您可以在 X::foo() 中放入什么代码来确保 i 指针仍然有效?
答案是没有标准检查。有一些技巧可能在调试模式下的 msvc 上起作用(检查 0xfeeefeee 或其他),但没有什么可以始终有效。
如果您需要某种对象来确保指针不会指向已释放的内存,您将需要比引用或标准指针更智能的东西。
这就是为什么在使用指针和引用时需要非常小心所有权语义和生命周期管理。
You can't. You also can't with a pointer. Consider:
Now, what code could you put in X::foo() to make sure that the i pointer is still valid?
Answer is that there is no standard check. There are some tricks that might work on msvc in debug mode (checking for 0xfeeefeee or whatever), but there's nothing that will consistently work.
If you need some sort of object that makes sure the pointer does not point at freed memory you'll need something much smarter than a reference or standard pointer.
This is why you need to be pretty darn careful with ownership semantics and lifetime management when working with pointers and references.
在 C++ 中,引用主要用作函数的参数和返回类型。在参数的情况下,由于函数调用的性质,引用不能引用不再存在的对象(假设是单线程程序)。在返回值的情况下,应该限制自己返回生命周期比函数调用长的类成员变量,或者引用传递给函数的参数。
In C++, references are primarily intended to be used as the parameters and return types of functions. In the case of a parameter, a reference cannot refer to an object that no longer exists (assuming a single threaded program) because of the nature of a function call. In the case of a return value, one should restrict oneself to either returning class member variables whose lifetimes are longer than the function call, or reference parameters that are passed in to the function.
首先,没有任何方法可以检测内存位置是否已被释放/删除。这与它是否为 null 无关。对于指针也是如此。给定一个指针,您无法确保它指向有效的内存。您可以测试指针是否为空,但仅此而已。非空指针仍可能指向已释放的内存。或者它可能指向一些垃圾位置。
至于引用,这同样适用,因为您无法确定它是否引用了仍然有效的对象。然而,C++ 中不存在“空引用”这样的东西,因此不需要检查引用是否“为空”。
当然,可以编写创建看起来像“空引用”的代码,并且该代码将被编译。但它不会正确。根据C++语言标准,不能创建对null的引用。尝试这样做是未定义的行为。
更好的解释是这样的:“一个引用指向一个有效的对象,因为你将它设置为指向一个有效的对象”。你不必凭信心接受它。您只需查看创建引用的代码即可。它要么指向当时的有效对象,要么没有。如果没有,则代码不正确,应更改。
并且该引用仍然有效,因为您知道它将被使用,因此您已确保不会使其引用的对象无效。
真的就是这么简单。只要您不破坏它们指向的对象,引用就会保持有效。因此,在不再需要引用之前,不要销毁它指向的对象。
First, there is never any way to detect if a memory location has been freed/deleted. That has nothing to do with whether or not it is null. The same is true for a pointer. Given a pointer, you have no way to ensure that it points to valid memory. You can test whether a pointer is null or not, but that's all. A non-null pointer may still point to freed memory. Or it may point to some garbage location.
As for references, the same applies in that you have no way of determining whether it references an object that is still valid. However, there is no such thing as a "null reference" in C++, so there is no need to check if a reference "is null".
Of course, it is possible to write code that creates what looks like a "null reference", and that code will compile. But it won't be correct. According to the C++ language standard, references to null can not be created. Attempting to do so is undefined behavior.
The better explanation is this: "a reference points to a valid object because you set it to point to a valid object". You don't have to take it on faith. You just have to look at the code where you created the reference. It either pointed to a valid object at that time, or it didn't. If it didn't, then the code is incorrect and should be changed.
And the reference is still valid because you know it is going to be used, so you have made sure not to invalidate the object it references.
It's really that simple. References stay valid as long as you don't destroy the object they point to. So don't destroy the object it points to until the reference is no longer needed.
您需要保持变量的完整性 - 即,如果您知道函数作用域不会比您的引用/指针寿命更长,则仅将引用/指针传递给某个函数。
如果你去释放一些句柄,然后尝试使用所述引用,你将读取释放的内存。
You need to maintain sanity of your variables -- i.e., only pass a reference/pointer to some function if you know the function scope will not outlive you reference/pointer.
If you go and free some handle and then try to use said reference you will be reading free'd memory.
因为当你到达那里时,你肯定已经做出了未定义的行为。让我解释一下:)
假设你有:
现在,如果你传递类似的东西:
但是如果你执行以下操作:
当你到达
fun
时,你将取消引用*n,如果指针被删除,如上例所示。所以,没有办法,而且现在实际上必须有办法,因为假设有效参数就是引用的全部要点。Because by the time you reach there you have made an undefined behavior for sure. Let me explain :)
Say you have:
Now, if you pass something like:
But if you do the following:
By the time you reach
fun
, you will be dereferencing *n which is undefined behavior if the pointer is deleted as in the example above. So, there is no way, and there must be now way actually because assuming valid parameters is the whole point of references.没有语法来检查引用是否有效。您可以测试指针是否为 NULL,但没有对引用进行有效/无效测试。当然,引用的对象可能会被一些有缺陷的代码释放或覆盖。同样的情况也适用于指针:如果非 NULL 指针指向已释放的对象,则无法测试它。
There is no syntax to check whether reference is valid. You can test pointer for NULL, but there is no valid/invalid test for a reference. Of course, referenced object can be released or overwritten by some buggy code. The same situation is for pointers: if non-NULL pointer points to released object, you cannot test this.
简而言之,它可能发生——但如果确实发生,你就会遇到严重的设计问题。您也没有真正的方法来检测它。答案是设计你的程序来防止它发生,而不是试图构建某种不会真正起作用的检查(因为它不能)。
The short is that it could happen -- but if it does, you have a serious design problem. You also have no real way of detecting it. The answer is to design your program to prevent it from happening, not trying to build some sort of check that won't really work (because it can't).
我认为您可以从简单的并行性中受益:
T &
类似于T * const
T const &
类似于T const * const
引用的意图与 const 非常相似,它们带有含义,因此有助于编写更清晰的代码,但不提供不同的运行时行为。
现在回答您的问题:是的,引用可能为空或无效。您可以测试空引用(
T& t = ; if (&t == 0)
),但它不应该发生>>>根据合同,参考是有效的。何时使用引用与指针?如果满足以下条件,请使用指针:
在任何其他情况下,请使用引用。
一些示例:
传递参数:
属性:
在功能上,引用和指针扮演着完全相同的角色。这只是一个合同问题。不幸的是,如果您删除它们引用的对象,两者都会调用未定义行为,那里没有赢家;)
I think you could benefit from a simple parallelism:
T &
is similar toT * const
T const &
is similar toT const * const
References are very similar to
const
in their intent, they carry a meaning and thus help write clearer code, but don't provide different runtime behavior.Now to answer your question: yes it is possible that a reference be null or invalid. You can test for a null reference (
T& t = ; if (&t == 0)
) but it should not happen >> by contract a reference is valid.When to use reference vs pointer ? Use a pointer if:
In any other case, use a reference.
Some examples:
Passing arguments:
Attributes:
Functionally references and pointers play the very same role. It's just a matter of contract. And unfortunately, both can invoke Undefined Behavior if you delete the object they refer to, there's no winner there ;)
C++ 引用是别名。这样做的结果是,对指针的取消引用不一定发生在它们出现的地方,而是发生在它们被求值的地方。获取对对象的引用不会评估该对象,而是为其指定别名。使用引用来评估对象。 C++ 不能保证引用有效;如果是这样,所有 C++ 编译器都会崩溃。这样做的唯一方法是消除所有使用引用进行动态分配的可能性。实际上,假设引用是有效对象。由于 *NULL 未定义 &无效,因此对于 p = NULL,*p 也是未定义的。 C++ 的问题是 *p 将被有效地传递给函数,或者延迟其计算,直到实际使用引用为止。争论它是未定义的并不是提问者问题的重点。如果它是非法的,编译器将强制执行它,标准也是如此。据我所知,也没有。
1) 您可以测试对 NULL 指针别名的引用, &r 只是 &(无论 r 别名如何) (编辑)
2) 当传递“取消引用”指针 (*i) 作为引用参数时,取消引用不会不会在调用站点发生,它可能永远不会发生,因为它是一个引用(引用是别名,而不是评估)。这就是参考文献的优化。如果在调用站点对它们进行求值,则要么编译器插入了额外的代码,要么它将是按值调用并且性能低于指针。
是的,引用本身不是NULL,它是无效的,就像*NULL 是无效的一样。延迟解引用表达式的求值与声称不可能有无效引用不一致。
编辑:更改了我的示例,因为它被误解为测试参考的建议,而不是我想要演示的内容。我只想演示无效的引用。
C++ references are aliases. The effect of this is that dereferences to pointers don't necessarily happen where they appear, they happen where they are evaluated. Taking a reference to an object doesn't evaluate the object, it aliases it. Using the reference is what evaluates the object. C++ cannot guarantee references are valid; if it does, all C++ compilers are broken. The only way to do so is to eliminate all possiblity of dynamic allocation with references. In practice, the assumption is that a reference is a valid object. Since *NULL is undefined & invalid, it follows that for p = NULL, *p is also undefined. The problem with C++ is *p will be effectively passed to a function, or delayed in its evaluation until which time the reference is actually used. Arguing that it is undefined is not the point of the asker's question. If it were illegal, the compiler would enforce it, and so would the standard. Neither does, that I am aware of.
1) You can test a reference for aliasing a NULL pointer, &r is simply &(whatever r aliases to) (EDIT)
2) When passing a "dereferenced" pointer (*i) as a reference parameter, the dereference doesn't happen at the callsite, it may never happen, because it is a reference (references are aliases, not evaluations). That is the optimization of references. If they were evaluated at the callsite, either the compiler is inserting extra code, or it would be a call by value and less performant than a pointer.
Yes, the reference itself is not NULL, it is invalid, just as *NULL is invalid. It is the delaying of evaluation of dereference expressions that is not consistent with claiming it is impossible to have an invalid reference.
EDIT: Changed my example as it has been misconstrued as suggestion for testing references, not what I wanted to demonstrate. I only wanted to demonstrate the invalid reference.
我认为这“取决于”。我知道,这不是一个答案,但这确实取决于。我认为防御性编码是一个值得遵循的好习惯。现在,如果您的堆栈跟踪有 10 层深,并且跟踪下的任何故障都会导致整个操作失败,那么请务必在顶层进行检查,并让任何异常都上升到顶层。但如果您可以从向您传递空引用的人那里恢复,请在适当的情况下进行检查。根据我的经验,我必须将代码与其他公司整合在一起,检查(并记录)公共 API 级别的所有内容可以让您转移当集成未按预期进行时发生的指责。
I think it "depends". I know, that is not an answer, but it does really depend. I think coding defensively is a good practice to follow. Now if your stack track is 10 levels deep and any failure down the trace causes the entire operation to fail, then by all means, check at the top level and let any exceptions rise to the top. but if you can recover from someone passing you a null reference, the check where appropriate. In my experience, where I have to bring code together with other companies to integrate together, checking ( and logging ) everything at the public api level lets you deflect the finger pointing that happens when integration does not go as expected.