nullptr in C++
/ 3 min read
Table of Contents
nullptr is C++11’s answer to the problems with NULL and 0 for null pointers. It’s type-safe and eliminates a whole class of bugs.
What nullptr Actually Is
nullptr is a keyword that represents a null pointer constant. Unlike NULL (which is usually just 0), nullptr has its own type: std::nullptr_t.
int* p1 = nullptr; // Clear intentint* p2 = NULL; // Old way, potentially problematicint* p3 = 0; // Even worse - just an integerThe Problem with NULL
NULL is typically defined as 0, which creates ambiguity:
void func(int x);void func(char* p);
func(NULL); // Which function gets called? Usually the int version!func(0); // Definitely calls func(int)func(nullptr); // Definitely calls func(char*)This ambiguity has caused countless bugs in C and older C++ code.
Why nullptr Is Better
- Type safety:
nullptronly converts to pointer types:
int x = nullptr; // Compilation errorbool b = nullptr; // Compilation error (good)int* p = nullptr; // OK- Template compatibility: Works correctly with templates:
template<typename T>void process(T value) { if (value == nullptr) { // Only works if T is a pointer type std::cout << "Null pointer" << std::endl; }}- Overload resolution: Chooses the right function:
void handle(int value) { std::cout << "Integer: " << value << std::endl; }void handle(void* ptr) { std::cout << "Pointer" << std::endl; }
handle(0); // Calls handle(int)handle(NULL); // Usually calls handle(int) - confusing!handle(nullptr); // Always calls handle(void*)When I Actually Use nullptr
Everywhere I used to use NULL:
- Pointer initialization:
int* data = nullptr;MyClass* obj = nullptr;- Pointer comparisons:
if (ptr != nullptr) { // Safe to dereference ptr}
// This pattern is so common, you can just write:if (ptr) { // ptr is not null}- Function parameters:
void process_data(const char* filename = nullptr) { if (filename == nullptr) { filename = "default.txt"; }}
process_data(nullptr); // Use default filename- Smart pointer resets:
std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();ptr.reset(nullptr); // Reset to null - though reset() with no args works tooThe Consistency Rule
I use nullptr everywhere, even where NULL would technically work:
// Consistent stylechar* buffer = nullptr;if (buffer == nullptr) { allocate_buffer(&buffer);}return buffer != nullptr ? buffer : get_default_buffer();Backward Compatibility
nullptr works fine with existing code:
// Legacy function expecting NULLvoid legacy_func(void* p);
legacy_func(nullptr); // Works perfectlyThe Pattern I Follow
Always use nullptr for null pointers, never NULL or 0. It’s clearer, safer, and more explicit about intent:
// Clear intentstd::unique_ptr<Resource> resource = nullptr;Resource* raw_ptr = nullptr;
// Check for nullif (resource == nullptr) { resource = std::make_unique<Resource>();}Real World Example
Here’s how it helps in practice:
class DatabaseConnection { void* handle_ = nullptr; // Clear initialization
public: bool connect(const char* connection_string = nullptr) { if (connection_string == nullptr) { connection_string = get_default_connection(); }
handle_ = establish_connection(connection_string); return handle_ != nullptr; }
void disconnect() { if (handle_ != nullptr) { close_connection(handle_); handle_ = nullptr; // Clear state } }};nullptr is a simple change that makes code more robust and easier to understand. There’s no reason to use NULL anymore.