Logo

Blog


Calling a C++ member function with a null object

In today's post, I'll show you what happens if you call a member function on a null object.

C++ is interesting, and sometimes it surprises me. Then, I forget and get surprised again...

What happened? In a code-review I approached the following code (compiler-explorer.com/z/8abxaKq3d):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class Apple {
  int mValue{4};

public:
  void Fun() { std::cout << "Hello\n"; }

  void Set(int v) { mValue = v; }
};

int main()
{
  Apple* alfred = nullptr;
  alfred->Fun();
}

To the programmer's surprise, this code did compile and even run! Oh, yes, it did. Even without crashing! Well, at least as long as nobody invokes Set on alfred.

The way member functions in C++ work is that each non-static member function has an implicit first parameter, the this pointer. Rings a bell, right?

The call to Fun internally looks like this:

1
Fun(alfred);

while Fun and Sets signature is:

1
2
3
void Fun(Apple* this) { std::cout << "Hello\n"; }

void Set(Apple* this, int v) { this->mValue = v; }

You can call a member function on a null-object because in the case of Fun, the this-pointer, which in our example is a nullptr, isn't used! Once you invoke Set the this-pointer is used for accessing the data member mValue inside Set.

As a side note, checking for the this-pointer being a nullptr is undefined behavior as everything from above is. Lifetime [basic.life p6.2] lists calling a member function with a nullptr among other things. Since [basic.life p6.2] clearly states that calling a member function with a nullptr is UB, the compiler can assume a non-null pointer inside any member function and hence is allowed to optimize away any nullptr check because it can only be constant false.

Of course, I knew all that, but seeing the example caught me by surprise, and I struggled for a moment :-)

Andreas