43

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 */
    }
    /* The rest of the 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 it 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? That is, 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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
bcx_2000
  • 483
  • 1
  • 5
  • 5
  • 4
    This is a good question. Its a shame most of the folks answering don't understand what you are asking. I *think* Nilesh provided the correct answer; and I'm fairly certain it works even when crossing an application/shared object boundary *if* the shared object exports the null object. – jww Jan 20 '16 at 23:01

8 Answers8

40

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
  • 74,600
  • 47
  • 176
  • 233
  • 1
    It is hardly the who;e (or even the main) purpose of having references. –  Jan 20 '10 at 08:55
  • 1
    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 Jan 20 '10 at 09:04
  • 1
    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 Jan 20 '10 at 10:19
  • It is possible to pass a null object in a refence. E.g. – cppcoder Jun 21 '11 at 05:35
  • I think you missed the point of the question. Instead of focusing on the first code block and a `NULL` character pointer, loop back to the title: ***checking for a null object in C++*** and the second code block. That is, how does one signify a proverbially nil object used as an optional argument. He can call the function with a distinguished value that is a null object. – jww Jan 20 '16 at 23:02
18

A reference can not be NULL. The interface makes you pass a real object into the function.

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

Note you can still write a function that takes a pointer. 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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • +1, especially on the not throwing exceptions if a pointer is NULL. – David Rodríguez - dribeas Jan 20 '10 at 08:27
  • 2
    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 Jan 20 '10 at 08:33
  • Or when dealing with C strings, such as std::string requires non-null pointers in various places. –  Jan 20 '10 at 08:36
  • 1
    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 Jan 20 '10 at 09:39
  • 2
    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 Jan 20 '10 at 13:23
  • I think you missed the point of the question. Instead of focusing on the first code block and a `NULL` character pointer, loop back to the title: ***checking for a null object in C++*** and the second code block. That is, how does one signify a proverbially nil object used as an optional argument. – jww Jan 20 '16 at 22:52
  • @jww: I don't think I did. `Basically, all I'm trying to do is to prevent the program from crashing when some_cpp_function() is called with NULL.` If you use references there is no longer the need for this test as the function can not be called with a `NULL` – Martin York Jan 20 '16 at 23:34
  • @Loki - bcx_2000 appears to know he needs something more than a NULL object. Telling him he can't use NULL and its UB is not really helpful. He seems to be looking for a treatment of the answer provided by Nilesh below. That is, he wants a treatment of something akin to a distinguished, sentinel object that represents a nil. – jww Jan 20 '16 at 23:44
  • @jww: Sure that is one interpretation. I personally think you are wrong (but you are entitled to an opinion). If you use just the title to answer the question you might have an argument but I like to take the question as a whole to understand what the OP is actually asking for. And he makes it very clear with the statement: `Basically, all I'm trying to do is to prevent the program from crashing when some_cpp_function() is called with NULL` – Martin York Jan 21 '16 at 00:05
7

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
  • 204,818
  • 23
  • 294
  • 489
6

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 Singhal
  • 93,253
  • 21
  • 125
  • 158
  • You will read in some places that you can get a null reference by doing: `type &null = *(type*)0;`, 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 Jan 20 '10 at 09:23
  • @dribeas: You might inadvertantly do type `&null = *pointertotype;` and `pointertotype` might end up being `NULL`. But unless you use pointers you are completely safe. – JPvdMerwe Jan 20 '10 at 12:54
  • @dribas: Technically the above is __NOT__ de-referencing a NULL. The Unary * operator (sometimes incorrectly refereed to as the de-reference operator) returns an lvalue referring to the object or function to which the expression points 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 Jan 20 '10 at 14:15
  • 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 Jan 20 '10 at 14:57
  • @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 Jan 20 '10 at 15:03
  • 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 Jan 20 '10 at 16:19
5

You can use a special designated object as the null object in case of references as follows:

class SomeClass {
    public:
        int operator==(SomeClass &object) {
            return (this == &object);
        }

    static SomeClass NullObject;
};

SomeClass SomeClass::NullObject;

void print(SomeClass &val) {
    if(val == SomeClass::NullObject)
        printf("\nNULL");
    else
        printf("\nNOT NULL");
}
0009laH
  • 1,960
  • 13
  • 27
Nilesh Pawar
  • 3,895
  • 3
  • 21
  • 19
  • You can do that but it is hardly the clearest approach. Why invent null objects if you can just use pointers? A null pointer means it doesn't point to any regular object. – sharptooth Jan 21 '16 at 07:16
3

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ć
  • 26,745
  • 6
  • 48
  • 60
  • I think you missed the point of the question. Instead of focusing on the first code block and a `NULL` character pointer, loop back to the title: ***checking for a null object in C++*** and the second code block. That is, how does one signify a proverbially nil object used as an optional argument. – jww Jan 20 '16 at 22:52
  • @jww I don't understand you. I said that references can't be null and to use his original function, passing a pointer to some_type, if he wants it to be optional. – Nikola Smiljanić Jan 21 '16 at 23:14
2

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
  • 167,383
  • 100
  • 513
  • 979
  • I think you missed the point of the question. Instead of focusing on the first code block and a `NULL` character pointer, loop back to the title: ***checking for a null object in C++*** and the second code block. That is, how does one signify a proverbially nil object used as an optional argument. – jww Jan 20 '16 at 22:52
  • @jww My answer is exactly about the second code block. – sharptooth Jan 21 '16 at 07:15
2
  • 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.

  • Also note using `if (p)` and `if (!p)` instead of `!= NULL` and `== NULL`. –  Jan 20 '10 at 08:37