On my last project, my team started working on implementing object-oriented concepts in C. Eventually we realized that we were reimplementing multiple C++ features. We admitted to ourselves that we would not implement our constructs more efficiently than the C++ compiler.
We were also very interested in the extra safety mechanisms implicit in C++: better type safety, smart pointers, nullptr
. We decided to jump in and give C++ a shot. I won’t lie and say it was easy. There are a lot of C habits that are hard to break, and it can be daunting to identify what features are actually useful to learn.
I am starting a series to help guide C programmers who are interested in using C++ into an easier transition. I aim to highlight useful features and specific behavioral changes for embedded C developers.
Today we’ll tackle an easy first topic: the nullptr
keyword.
Table of Contents:
nullptr
nullptr
is a new keyword introduced in C++11. nullptr
is meant as a replacement to NULL
. nullptr
provides a typesafe pointer value representing an empty (null) pointer.
The general rule of thumb that I recommend is that you should start using nullptr
whenever you would have used NULL
in the past.
NULL
What’s so wrong with our old friend NULL
, anyway?
Traditionally, the NULL
macro is an implementation defined constant representing a null pointer, usually the integer 0
. In C, the NULL
macro can have type void *
. However, in C++ this definition is invalid, as there is no implicit cast from a void *
type to any other pointer type (which C allows).
Since C++11, NULL
can be either an integer literal with value zero, or a prvalue of type std::nullptr_t
. Because of this ambiguity, I recommend switching exclusively to nullptr
. To quote cppreference with a code example:
//Possible implementation
#define NULL 0
//since C++11
#define NULL nullptr
NULL
vs nullptr
Let’s consider the following initialization statements:
int* x = nullptr;
int* y = NULL;
int* z = 0;
Interestingly, this code produces the same results. Quoting from cppreference:
A null pointer constant may be implicitly converted to any pointer type; such conversion results in the null pointer value of that type. If a null pointer constant has integer type, it may be converted to a
prvalue
of typestd::nullptr_t
.
If functionally they are the same for pointer assignments, what’s so important about distinguishing the two?
Types & Function Overloading
Recall our emphasis on NULL
(0
) being defined as an int
literal value. C++ supports better type checking and function overloading, so the type distinction is important.
The example case below is not a common use case, but it demonstrates the dangers of NULL
being defined as an int
. Consider the following two overloaded function prototypes for f
:
int f(int x);
int f(int * p);
If you invoke f(NULL)
, you will enter the f(int)
function rather than the f(int *)
function like you intended. The solution to such problems is to use f(nullptr)
instead.
Templating
Another benefit of using nullptr
is that you can specialize some of your templates for nullptr
cases.
Since the type of nullptr
is std::nullptr_t
, you can do something like this to handle nullptr
as a specific case:
template<typename T>
void foo(T * p); // template based on non-nullptr types
void foo(std::nullptr_t); //specialized overload for nullptr value
Putting it All Together
As I mentioned above, the general rule of thumb that I recommend is that you should start using nullptr
whenever you would have used NULL
in the past.
As a reminder, since C++11, NULL
can be either an integer literal with value zero, or a prvalue
of type std::nullptr_t
. Because of this ambiguity, I recommend switching exclusively to nullptr
.
nullptr
will make your code less error-prone than relying on the implementation-defined NULL
. But your code will not blow up if you stick to NULL
. Your existing code is probably fine to leave as it is.
Further Reading
Using C++ Without the Heap
Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.
Learn More on the Course Page