Are there any other circumstances where they[destructors] will not be called?
- Long jumps: these interfere with the natural stack unwinding process and often lead to undefined behavior in C++.
- Premature exits (you already pointed these out, though it’s worth noting that throwing while already stack unwinding as a result of an exception being thrown leads to undefined behavior and this is why we should never throw out of dtors)
- Throwing from a constructor does not invoke the dtor for a class. This is why, if you allocate multiple memory blocks managed by several different pointers (and not smart pointers) in a ctor, you need to use function-level try blocks or avoid using the initializer list and have a try/catch block in the ctor body (or better yet, just use a smart pointer like scoped_ptr since any member successfully initialized so far in an initializer list will be destroyed even though the class dtor will not be called).
- As pointed out, failing to make a dtor virtual when a class is deleted through a base pointer could fail to invoke the subclass dtors (undefined behavior).
- Failing to call matching operator delete/delete[] for an operator new/new[] call (undefined behavior – may fail to invoke dtor).
- Failing to manually invoke the dtor when using placement new with a custom memory allocator in the deallocate section.
- Using functions like memcpy which only copies one memory block to another without invoking copy ctors. mem* functions are deadly in C++ as they bulldoze over the private data of a class, overwrite vtables, etc. The result is typically undefined behavior.
- Instantiation of some of smart pointers (auto_ptr) on an incomplete type, see this discussion