3

The codebase I work on has a bunch of code where an object might be deleted while one of its methods is running, with the method guaranteeing that it has stopped using the object's members by the time the delete happens. The most obvious example is a self-deleting object:

class SomeResource {
 public:
  SomeResource() = default;

  void Close() {
    // Read state before deleting.
    const uint64_t id = id_;
    delete this;

    // Use what we read before.
    std::cout << "Closed resource with ID " << id;
  }

 private:
  // Delete via Close instead.
  ~SomeResource() = default;

  const uint64_t id_ = MakeSomeID();
}

It seems intuitively fine to do this, since the delete is sequenced after the access to the member variable. But similar intuition might lead you to say it's okay to call a method via a null pointer as long as the method has null checks for this, but that's not okay.

Less obvious situations include calling a function in another translation unit that causes the object to be deleted, or communicating with another thread, causing the object to be deleted.

Does the C++ standard say anything about whether it is or isn't allowed to delete an object while a member function is running? I'm looking for answers in standardese.

jacobsa
  • 5,719
  • 1
  • 28
  • 60
  • Post a complete program, please. [MCVE] – Jive Dadson Apr 01 '18 at 00:06
  • @jogojapan: Unfortunately most of the answers there revolve around the dangers of ensuring that the object was created dynamically. I take it that that's not what the OP cares about, rather, he wants a formal argument about object lifetimes. – Kerrek SB Apr 01 '18 at 00:11
  • That's uh... what's the word? `delete this;` scares me. Run away! One of the main reasons for using C++ classes is to relieve the class-user from having to know things like, "This must be allocated on the heap with operator `new`. And not in a new[] array. And be sure to call Close once and only once, and don't track mud across my nice, clean kitchen floor, and sit up straight when I talk to you, and ..." – Jive Dadson Apr 01 '18 at 00:12
  • @jacobsa: It's clearly not OK to call a member function on anything but a live object instance, so any question about whether a destructor can race with anything else usually indicates a logic error in the program. On the other hand, member functions certainly *can* access an object that's either not yet or no longer alive, most notable among those are constructors and the destructor. And constructors and destructors can certainly call member functions. – Kerrek SB Apr 01 '18 at 00:14
  • @Kerreck - Constructors and destructors are not member functions. – Jive Dadson Apr 01 '18 at 00:15
  • Otherwise, see [class.cdtor] for what you *can't* do. By omission, your code seems fine. (You don't access any non-static data members after the `delete`.) – Kerrek SB Apr 01 '18 at 00:16
  • Smells like undefined behavior to me -- since it's possible to delete an object in one thread and call methods on it in a different thread, I don't see how the C++ standard could forbid such a situation from occurring. – MrEricSir Apr 01 '18 at 00:20
  • 4
    Correct, I don’t care about ensuring it was allocated dynamically; please assume this is ensured elsewhere. I also don’t care to discuss whether this is bad style. Self-deleting is the less interesting example, and I don’t get to control the style anyway. The [FAQ entry](https://isocpp.org/wiki/faq/freestore-mgmt#delete-this) posted in the other question is the answer I’m looking for, but without standard citations. – jacobsa Apr 01 '18 at 00:24
  • I'd say the correctness follows by omission. If you're not doing things forbidden by [class.cdtor], you're OK. For example, it's quite normal to start a thread in a class constructor, and the thread function would be accessing class members before the constructor has returned, i.e. before the object's lifetime has begun. But the object is "good enough" at that point (provided your code is correct). – Kerrek SB Apr 01 '18 at 00:36

0 Answers0