C++ 为什么我不需要检查引用是否无效/空?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2524870/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:48:40  来源:igfitidea点击:

Why don't I need to check if references are invalid/null?

c++referencenull

提问by jbu

Reading http://www.cprogramming.com/tutorial/references.html, it says:

阅读http://www.cprogramming.com/tutorial/references.html,它说:

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.

通常,引用应该始终有效,因为您必须始终初始化引用。这意味着除非出现一些奇怪的情况(见下文),您可以确定使用引用就像使用普通的旧非引用变量一样。您不需要检查以确保引用未指向 NULL,并且您不会被忘记为其分配内存的未初始化引用所困扰。

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?

任何人都可以透露一些信息吗?

回答by Brian R. Bondy

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:

您可以对指针和引用进行 NULL 检查,但通常您永远不会对引用进行 NULL 检查,因为没有人会写这样的代码:

int *p = 0;
int &r = *p;//no one does this
if(&r != 0)//and so no one does this kind of check
{
}


When to use a reference?

何时使用参考?

You probably want to use references in cases like this:

你可能想在这样的情况下使用引用:

//I want the function fn to not make a copy of cat and to use
// the same memory of the object that was passed in
void fn(Cat &cat)
{
   //Do something with cat
}

//...main...
Cat c;
fn(c);


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:

例如:

int *p;
if(true)
{
  int x;
  p = &x;
}

*p = 3;//runtime error

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:

例如:

int *p = new int;
*p = 3;
int &r = *p;
delete p;
r = 3;//runtime error

回答by jbu

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.

在 C++ 中,引用主要用作函数的参数和返回类型。对于参数,由于函数调用的性质,引用不能引用不再存在的对象(假设是单线程程序)。在返回值的情况下,应该将自己限制为返回生命周期长于函数调用的类成员变量,或传递给函数的引用参数。

回答by Edward Strange

You can't. You also can't with a pointer. Consider:

你不能。你也不能用指针。考虑:



struct X
{
  int * i;
  void foo() { *i++; }
};

int main()
{
  int *i = new int(5);
  X x = { i };
  delete i;
  x.foo();
}

Now, what code could you put in X::foo() to make sure that the i pointer is still valid?

现在,您可以在 X::foo() 中放入什么代码来确保 i 指针仍然有效?

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.

答案是没有标准检查。有一些技巧可能在调试模式下对 msvc 起作用(检查 0xfeeefeee 或其他什么),但没有什么能始终如一地工作。

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.

这就是为什么在使用指针和引用时需要非常小心所有权语义和生命周期管理的原因。

回答by jalf

My question is how do I know that the object's memory hasn't been freed/deleted AFTER you've initialized the reference.

我的问题是我怎么知道在初始化引用后对象的内存没有被释放/删除。

First, there is neverany 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".

至于引用,同样适用,因为您无法确定它是否引用了仍然有效的对象。但是,C++ 中没有“空引用”这样的东西,因此无需检查引用是否“为空”。

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.

当然,可以编写创建看起来像“空引用”的代码,并且该代码将被编译。但它不会是正确的。根据 C++ 语言标准,不能创建对 null 的引用。尝试这样做是未定义的行为。

What it comes down to is that I can't take this advice on faith and I need a better explanation

归根结底是我不能接受这个关于信仰的建议,我需要一个更好的解释

The better explanation is this: "a reference points to a valid object because youset 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.

真的就是这么简单。只要您不破坏它们指向的对象,引用就会保持有效。所以在不再需要引用之前不要销毁它指向的对象。

回答by Hassan Syed

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.

如果你去释放一些句柄,然后尝试使用所说的引用,你将阅读释放的内存。

回答by AraK

Because by the time you reach there you have made an undefined behavior for sure. Let me explain :)

因为当您到达那里时,您肯定已经做出了未定义的行为。让我解释 :)

Say you have:

说你有:

void fun(int& n);

Now, if you pass something like:

现在,如果你通过类似的东西:

int* n = new int(5);
fun(*n); // no problems until now!

But if you do the following:

但是,如果您执行以下操作:

int* n = new int(5);
...
delete n;
...
fun(*n); // passing a deleted memory!

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.

当您到达时fun,您将取消引用 *n,如果指针如上例所示被删除,这是未定义的行为。所以,没有办法,现在必须有办法,因为假设有效参数是引用的全部要点。

回答by Alex F

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.

没有语法来检查引用是否有效。您可以测试指针是否为 NULL,但没有针对引用的有效/无效测试。当然,引用的对象可能会被一些有缺陷的代码释放或覆盖。指针的情况相同:如果非空指针指向已释放的对象,则无法对此进行测试。

回答by Jerry Coffin

The short is that it couldhappen -- 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).

简而言之,它可能会发生——但如果发生了,你就有了一个严重的设计问题。你也没有真正的方法来检测它。答案是设计您的程序以防止它发生,而不是试图构建某种实际上不起作用的检查(因为它不能)。

回答by codenheim

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.

C++ 引用是别名。这样做的结果是对指针的取消引用不一定发生在它们出现的地方,而是发生在它们被评估的地方。获取对对象的引用不会评估该对象,而是对其进行别名。使用引用来评估对象。C++ 不能保证引用是有效的;如果是,则所有 C++ 编译器都已损坏。这样做的唯一方法是消除使用引用进行动态分配的所有可能性。在实践中,假设引用是一个有效的对象。由于 *NULL 是未定义且无效的,因此对于 p = NULL,*p 也是未定义的。C++ 的问题是 *p 将被有效地传递给一个函数,或者在其评估中延迟到实际使用引用的时间。争论它是未定义的不是提问者问题的重点。如果它是非法的,编译器将强制执行它,标准也是如此。也没有,我知道。

int &r = *j; // aliases *j, but does not evaluate j
if(r > 0) // evaluates r ==> *j, resulting in dereference (evaluate j) at this line, not what some expect
  ;

1) You can test a reference for aliasing a NULL pointer, &r is simply &(whatever r aliases to) (EDIT)

1) 您可以测试一个引用以将 NULL 指针别名化,&r 只是 &(任何 r 别名) (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.

2) 当传递一个“取消引用”的指针 (*i) 作为引用参数时,取消引用不会发生在调用点,它可能永远不会发生,因为它是一个引用(引用是别名,而不是评估)。那就是参考文献的优化。如果在调用站点对它们进行评估,要么编译器插入了额外的代码,要么是按值调用且性能不如指针。

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.

是的,引用本身不是 NULL,它是无效的,就像 *NULL 是无效的一样。延迟对取消引用表达式的评估与声称不可能有无效引用不一致。

#include <iostream>

int fun(int & i) {
   std::cerr << &i << "\n";
   std::cerr << i << "\n"; // crash here
}

int main() {
   int * i = new int();
   i = 0;
   fun(*i); // Why not crash here? Because the deref doesn't happen here, inconsistent, but critical for performance of references
}

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.

编辑:更改了我的示例,因为它被误解为测试参考的建议,而不是我想要演示的内容。我只想展示无效的参考。

回答by Matthieu M.

I think you could benefit from a simple parallelism:

我认为您可以从简单的并行性中受益:

  • T &is similar to T * const
  • T const &is similar to T const * const
  • T &类似于 T * const
  • T const &类似于 T const * const

References are very similar to constin their intent, they carry a meaning and thus help write clearer code, but don't provide different runtime behavior.

引用与const它们的意图非常相似,它们具有意义,因此有助于编写更清晰的代码,但不提供不同的运行时行为。

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.

现在回答您的问题:是的,引用可能为空或无效。您可以测试空引用 ( T& t = ; if (&t == 0)) 但它不应该发生 >> 根据合同,引用是有效的。

When to use reference vs pointer ? Use a pointer if:

何时使用引用 vs 指针?在以下情况下使用指针:

  • you wish to be able to change the pointee
  • you wish to express the possible nullity
  • 你希望能够改变指针
  • 你想表达可能的无效

In any other case, use a reference.

在任何其他情况下,请使用参考。

Some examples:

一些例子:

// Returns an object corresponding to the criteria
// or a special value if it cannot be found
Object* find(...); // returns 0 if fails

// Returns an object corresponding to the criteria
// or throw "NotFound" if it cannot be found
Object& find(...); // throw NotFound

Passing arguments:

传递参数:

void doSomething(Object* obj)
{
  if (obj) obj->doSomething();
}

void doSomething(Object& obj) { obj.do(); obj.something(); }

Attributes:

属性:

struct Foo
{
  int i;
  Bar* b; // No constructor, we need to initialize later on
};

class Foo
{
public:
  Foo(int i, Bar& b): i(i), b(b) {}
private:
  int i;
  Bar& b; // will always point to the same object, Foo not Default Constructible
};

class Other
{
public:
  Other(Bar& b): b(&b) {} // NEED to pass a valid object for init

  void swap(Other& rhs);  // NEED a pointer to be able to exchange

private:
  Bar* b;
};

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 ;)

从功能上讲,引用和指针扮演着相同的角色。这只是合同问题。不幸的是,如果您删除它们引用的对象,两者都可以调用未定义的行为,那里没有赢家;)