简体   繁体   中英

Safe pointer dereferencing in C++

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

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 :

  • No cost at runtime in release mode
  • Makes it crystal clear to the developer, that he is responsible for this to never happen.

Disadvantages of assert :

  • Potential undefined behavior in release mode

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM