In our code base we have many constructions like this:
auto* pObj = getObjectThatMayVeryRarelyBeNull();
if (!pObj) throw std::runtime_error("Ooops!");
// Use pObj->(...)
In 99.99% of cases this check is not triggered. I'm thinking about the following solution:
auto& obj = deref_or_throw(getObjectThatMayVeryRarelyBeNull());
// Use obj.(...)
Where deref_or_throw
is declared as follows:
template<class T> T& deref_or_throw(T* p) {
if (p == nullptr) { throw std::invalid_argument("Argument is null!"); }
return *p;
}
That code is much clearer and works as I need.
The question is: am I reinventing the wheel? Is there some related solution in standard or boost? Or do you have some comments on the solution?
PS. Related question (with no satisfying answer): Is there a C++ equivalent of a NullPointerException
Dereferencing is used to access or manipulate data contained in memory location pointed to by a pointer. *(asterisk) is used with pointer variable when dereferencing the pointer variable, it refers to variable being pointed, so this is called dereferencing of pointers.
The unary operator * is used to declare a pointer and the unary operator & is used to dereference the pointer.. In both cases, the operator is “unary” because it acts upon a single operand to produce a new value.
Some Interesting Facts: 1) void pointers cannot be dereferenced.
That said, we can dereference the pointer without ever accessing the value it points to. For example: char *p = NULL; *p; We dereferenced the NULL pointer without accessing its value.
There are two ways of dealing with the "rare case of null-pointer" issue. First, it's the proposed exception solution. For this, the method deref_or_throw
is a good thing, though I would prefer to throw a runtime_error
, rather than an invalid_argument
exception. You may also consider naming it NullPointerException
, if you prefer that.
However, if the ptr != nullptr
case is actually a precondition to the algorithm, you should try your best to achieve 100% safety in having this non-null case. An assert
is the appropriate thing in this case.
Advantages of assert
:
Disadvantages of assert
:
You should also consider writing a method getObjectThatMayNeverBeNullButDoingTheSameAsGetObjectThatMayVeryRarelyBeNull()
: A method that is guaranteed to return a non-null pointer to an object that is otherwise completely equivalent to your original method. However, if you can go the extra mile, I would strongly suggest to not returning a raw pointer anyway. Embrace C++11 and return objects by value :-)
You can, and probably should, design with null
cases in mind. For example, in your problem domain, when does it make sense for class methods to return null
? When does it make sense to call functions with null
arguments?
Broadly speaking, it can be beneficial to remove null
references from code whenever possible. In other words, you can program to the invariant that "this will not be null here... or there". This has many benefits. You won't have to obfuscate code by wrapping methods in deref_or_throw
. You may achieve more semantic meaning from code, because, how often are things null
in the real world? Your code may be more readable if you use exceptions to indicate errors rather than null
values. And finally, you reduce the need for error-checking and also reduce the risk of run time errors (the dreaded null pointer dereference).
If your system was not designed with null
cases in mind, I'd say it's best to leave it alone and not go crazy wrapping everything with deref_or_throw
. Perhaps take an Agile approach. As you are coding, inspect the contracts that your class objects offer as services. How often can these contracts reasonably be expected to return null
? What is the semantic value of null
in these cases?
You could probably identify the classes in your system that might reasonably be expected to return null
. For these classes, null
may be valid business logic, rather than fringe cases that are indicative of implementation errors. For the classes that might be expected to return null
, the additional check may be worth the safety. In this sense, checking for null
feels more like business logic rather than low level implementation details. Across the board, though, systematically wrapping all pointer deference in deref_or_throw
might be a cure that is its own poison.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With