Reading http://www.cprogramming.com/tutorial/references.html, it says:
In general, references should always be valid because you must always initi
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
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.
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:
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 ;)
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.
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).
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.