Below is a minimal example of problem that occurred in a huge legacy project I am currently working on.
I have a main function that should be creating a new object ob a global object pointer B* B
.
Because of a configuration error this was not done and the non-static method b->do_something()
was called.
My expectation would be that this leads to a segmentation fault when doing so.
The code does create a segmentation fault but only when comparing a == nullptr
.
This was extremely confusing to me because I was looking for problems with the initialization of a
.
I was not aware that calling a non-static method of an object is possible even when this object was never initialized.
My question is how to prevent this confusion in the future? So how do I crash the execution when calling a non-static method of a NULL object?
Possible solutions that do not satisfy me yet:
- Wrapping every call to
b->do_something()
withif(b != nullptr)
- Very verbose
- Changing
do_something()
toif (&a == nullptr)
- Works but flagged by clang-tidy (CI check fails)
- According to this a
"A reference can not be NULL"
- Extending
do_something()
withif (this == nullptr || a == nullptr)
- Works but flagged by clang-tidy (CI check fails)
#include <stdio.h>
#include <stdexcept>
class A {};
class B {
A *a = nullptr;
public:
B(){
// Sometimes doing this
a = new A();
}
void do_something() {
if(a == nullptr){ // SegFault appears here because B was never created and accessing this->a evaluates to nullptr->a
throw std::runtime_error("Error");
} else {
printf("Doing Something");
}
}
};
// Global variable
B *b;
int main()
{
// Long code that should have called:
// b = new B();
b->do_something();
return 0;
}