What does the below C++ program do?

#include <iostream>
#include <memory>
#include <string>

void someFunction(
  const std::string& name
) {
  static const std::string* pCachedName = nullptr;

  if (pCachedName == nullptr) {
    pCachedName = &name;
    std::cout << "in someFunction set pCachedName to value: " << *pCachedName << std::endl;
  } else {
    std::cout << "in someFunction pCachedName has value: " << *pCachedName << std::endl;
  }

}

int main() {
  std::shared_ptr<std::string> name;
  name.reset(new std::string("alice"));

  std::cout << "before someFunction name = '" << *name << "'" << std::endl;

  someFunction(*name);

  name.reset(new std::string("bob"));

  someFunction(*name);

  std::cout << "after someFunction name = '" << *name << "'" << std::endl;

  return 0;
}

name is declared in main as a std::shared_ptr to a string. We pass name as a constant reference to someFunction assuming we do not need to worry about it being mutated or stored.

In someFunction (for crazy-fast performance?) we have a static pointer pCachedString. On the first call to someFunction we store the pointer to name.

After this main calls name.reset() which deletes the previous name pointer. At this point the pCachedString is invalid and points to a deleted object.

The second call to someFunction tries to use the invalid pointer. This is undefined behavior, all bets are off as to what will happen.

A modern c++ compiler is happy to compile the above code without warnings, I used g++ 13.3.0. Even with -Wall -Wextra options no warnings are produced:

$ g++ -Wall -Wextra test.cpp

$ ./a.out
before someFunction name = 'alice'
in someFunction set pCachedName to value: alice

Segmentation fault (core dumped)

When we run the code we get a core dump due to use of deleted value in second call to someFunction.

This is a major weakness of C++, we can store references or pointers anywhere. Hope springs eternal that these objects are not deleted and then used again.

Scenarios like above are a major reason garbage collection exists. Languages having a GC will automatically keep reference counts for all objects and only delete unreachable objects. GC has it’s own costs of performance and complexity, but it is unlikely our programs will core dump.

We need a memory safe language.