tags:

views:

837

answers:

7

Hi all,

I've mostly only worked with C, and am running into some unfamiliar issues in C++. Let's say that I have some function like this in C, which would be very typical:

int some_c_function(const char* var)
{
    if (var == NULL) {
        /* exit early so we don't dereference a null pointer */
    }
    /* rest of code */
}

and let's say that I'm trying to write a similar function in C++:

int some_cpp_function(const some_object& str)
{
    if (str == NULL)  // this doesn't compile, probably because some_object doesn't overload the == operator

    if (&str == NULL) // this compiles, but doesn't work, and does this even mean anything?
}

Basically, all I'm trying to do is to prevent the program from crashing when some_cpp_function() is called with NULL.

  • what is the most typical/common way of doing this with an object C++ (that doesn't involve overloading the == operator)?

  • is this even the right approach? ie. should I not write functions that take an object as an argument, but rather, write member functions? (but even if so, please answer the original question)

  • between a function that takes a reference to an object, or a function that takes a C-style pointer to an object, are there reasons to choose one over the other?

+2  A: 

You should use NULL only with pointers. Your function accepts a reference and they can't be NULL.

Write your function just like you would write it in C.

Nikola Smiljanić
+9  A: 

Basically, all I'm trying to do is to prevent the program from crashing when some_cpp_function() is called with NULL.

It is not possible to call the function with NULL. One of the purpose of having the reference, it will point to some object always as you have to initialize it when defining it. Do not think reference as a fancy pointer, think of it as an alias name for the object itself. Then this type of confusion will not arise.

Naveen
It is hardly the who;e (or even the main) purpose of having references.
anon
Actually it is possible to call `some_cpp_function(NULL)`, if `some_object` has a non-explicit constructor which takes a pointer (or anything which can be implicitly converted from 0). If for instance `some_object` is a typedef for `std::string`, the program will crash in the constructor of the temporary `std::string` object, but that happens outside of `some_cpp_function`.
dalle
It is possible to create a "NULL reference" by dereferencing a NULL pointer (which leads to undefined behavior itself). I suppose this could happen in practice, but then it is a serious error in the code and should be dealt with more radically, e.g using assert.
visitor
+1  A: 

C++ references naturally can't be null, you don't need the check. The function can only be called by passing a reference to an existing object.

sharptooth
+5  A: 

Reference can not be NULL.
The interface makes you pass a real objecdt into the function.

So there is no need to test for NULL.
This is one of the reasons that reference were introduced into C++.

Note you can still write a function that takes apointer. In this situation you still need to test for NULL. If the value is NULL then you return early just like in C. Note: You should not be using exceptions when a pointer is NULL. If a parameter should never be NULL then you create an interface that uses a reference.

Martin York
+1, especially on the not throwing exceptions if a pointer is NULL.
David Rodríguez - dribeas
Sometimes it is appropriate for an interface to take a non-NULL pointer and not a reference. For example, if the interface is going to take ownership of the object being pointed to.
Omnifarious
Or when dealing with C strings, such as std::string requires non-null pointers in various places.
Roger Pate
Omnifarious - personally I disagree and I don't believe that taking a pointer in that situation gains you anything except incorrectly indicating to callers that the parameter is optional and could be null when it's not and can't. IMHO, ownership transfer semantics and optionality are orthogonal.
Len Holgate
Omnifarious: If you want to indicate ownership transfer of a pointer a more appropriate parameter would be a std::auto_ptr. This explicitly indicates that the function is taking ownership of the parameter.
Martin York
+1  A: 

A C++ reference is not a pointer nor a Java/C# style reference and cannot be NULL. They behave as if they were an alias to another existing object.

In some cases, if there are bugs in your code, you might get a reference into an already dead or non-existent object, but the best thing you can do is hope that the program dies soon enough to be able to debug what happened and why your program got corrupted.

That is, I have seen code checking for 'null references' doing something like: if ( &reference == 0 ), but the standard is clear that there cannot be null references in a well-formed program. If a reference is bound to a null object the program is ill-formed and should be corrected. If you need optional values, use pointers (or some higher level construct like boost::optional), not references.

David Rodríguez - dribeas
+1 for being the only answer that mentions (broken) references to already deleted heap objects etc.
Christian
+2  A: 

As everyone said, references can't be null. That is because, a reference refers to an object. In your code:

// this compiles, but doesn't work, and does this even mean anything?
if (&str == NULL)

you are taking the address of the object str. By definition, str exists, so it has an address. So, it cannot be NULL. So, syntactically, the above is correct, but logically, the if condition is always going to be false.

About your questions: it depends upon what you want to do. Do you want the function to be able to modify the argument? If yes, pass a reference. If not, don't (or pass reference to const). See this C++ FAQ for some good details.

In general, in C++, passing by reference is preferred by most people over passing a pointer. One of the reasons is exactly what you discovered: a reference can't be NULL, thus avoiding you the headache of checking for it in the function.

Alok
You will read in some places that you can get a null reference by doing: `type `, but dereferencing the null pointer is incorrect according to the standard, that explicitly states that there can be no null references within a well formed program.
David Rodríguez - dribeas
@dribeas: You might inadvertantly do type `` and `pointertotype` might end up being `NULL`. But unless you use pointers you are completely safe.
JPvdMerwe
@dribas: Technically the above is __NOT__ de-referencing a NULL. The Unary * operator (sometimes incorrectly refereed to as the de-reference operator) returns <quote>an lvalue referring to the object or function to which the expression points</quote> The keyword here is 'referring'. There is no mention of the address being de-referenced. See Section 5.3.1/1 of the standard. But I agree that a well formed program can not have NULL references and anybody doing the above definitely should check the pointer is not NULL before applying the unary * operator.
Martin York
The problem there is that dereferencing a null pointer is 'undefined behavior' in the standard. And again, at that point, the best thing that can happen is the application dying with an invalid memory access so that you can try to debug, it does not make any sense to test against 'null reference' as that cannot happen in well-formed programs. The problem is that in most people's mind references are pointers and as such it is considered that the assignment there is not actually dereferencing the pointer. I am not saying that you cannot get that behavior, but when it happens it is an error.
David Rodríguez - dribeas
@Martin: The standard explicitly considers that: [dcl.ref]/4: "Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior"
David Rodríguez - dribeas
I also seem to remember getting a "NULL reference" by accidentally initializing a reference with itself :) - Of course, any random garbage could be the result of this.
UncleBens
+1  A: 
  • What is the most typical/common way of doing this with an object C++ (that doesn't involve overloading the == operator)?
  • Is this even the right approach? ie. should I not write functions that take an object as an argument, but rather, write member functions? (But even if so, please answer the original question.)

No, references cannot be null (unless Undefined Behavior has already happened, in which case all bets are already off). Whether you should write a method or non-method depends on other factors.

  • Between a function that takes a reference to an object, or a function that takes a C-style pointer to an object, are there reasons to choose one over the other?

If you need to represent "no object", then pass a pointer to the function, and let that pointer be NULL:

int silly_sum(int const* pa=0, int const* pb=0, int const* pc=0) {
  /* Take up to three ints and return the sum of any supplied values.

  Pass null pointers for "not supplied".

  This is NOT an example of good code.
  */
  if (!pa && (pb || pc)) return silly_sum(pb, pc);
  if (!pb && pc) return silly_sum(pa, pc);
  if (pc) return silly_sum(pa, pb) + *pc;
  if (pa && pb) return *pa + *pb;
  if (pa) return *pa;
  if (pb) return *pb;
  return 0;
}

int main() {
  int a = 1, b = 2, c = 3;
  cout << silly_sum(&a, &b, &c) << '\n';
  cout << silly_sum(&a, &b) << '\n';
  cout << silly_sum(&a) << '\n';
  cout << silly_sum(0, &b, &c) << '\n';
  cout << silly_sum(&a, 0, &c) << '\n';
  cout << silly_sum(0, 0, &c) << '\n';
  return 0;
}

If "no object" never needs to be represented, then references work fine. In fact, operator overloads are much simpler because they take overloads.

You can use something like boost::optional.

Roger Pate
Also note using `if (p)` and `if (!p)` instead of `!= NULL` and `== NULL`.
Roger Pate