Why don't I need to check if references are in

2019-01-31 00:19发布

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?

11条回答
Summer. ? 凉城
2楼-- · 2019-01-31 00:32

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.

查看更多
来,给爷笑一个
3楼-- · 2019-01-31 00:34

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:

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
查看更多
Anthone
4楼-- · 2019-01-31 00:36

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.

查看更多
手持菜刀,她持情操
5楼-- · 2019-01-31 00:38

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.

查看更多
太酷不给撩
6楼-- · 2019-01-31 00:47

I think you could benefit from a simple parallelism:

  • T & is similar to T * const
  • T const & is similar to T 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:

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

查看更多
走好不送
7楼-- · 2019-01-31 00:49

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.

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)

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.

#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.

查看更多
登录 后发表回答