Reputation: 25
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:
b->do_something()
with if(b != nullptr)
do_something()
to if (&a == nullptr)
"A reference can not be NULL"
do_something()
with if (this == nullptr || a == nullptr)
#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;
}
Upvotes: 0
Views: 166
Reputation: 122830
You cannot prevent a user from all possible mistakes and most of the time it is futile to even try (this isnt Java ;). Thou shall not call a member on a non-initialized pointer (actually in your code it is initialized because it is global, but with nullptr
). Thats not specific to your class, but something one has to consider always when using pointers.
However, there are ways to mitigate the problem. You could make B::do_something
private and be-friend a free function:
void do_something(B* b) {
if (b) b->do_something();
}
Its not very clear from your minimal example, but if do_something
can be implemented as non-member, then you do not need the member function in the first place.
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?
There is no portable way to do that. Calling a member function on a nullptr
is undefined behavior. Anything can happen, including it appears to work. If you remove the check for a
being nullptr
then do_something
is actually not using this
and most probably will appear to work with most compilers.
Checking this != nullptr
inside the method is also no help, because once the call is made, you are already in undefined behavior.
Upvotes: 2