Thursday, March 26, 2009

The Beauty of Destruction (Pete Isensee, Microsoft)

C++ Destructor Definition

  • One
  • Special
  • Deterministic –> called at well defined times
  • Automatic –> object out of scope or delete
  • Symmetric –> constructor fits
  • Member –> part of a class
  • Function
  • With
    • A special name (~)
    • No parameters
    • No return type
  • Designed to
    • Give last rites
    • Before object death

C# uses finalizer different (called by GC) non-deterministic, same in Java

When destructors are called

  • Global or static object, called when terminates
  • Arrays destructed in reverse way
  • STL container , unspecified order
  • delete operator
  • out of scope
  • temp objects
  • exception thrown (stack unwinding)
  • explicitly
  • exit()
  • abort (does not call destructor)

Order of destruction

  • Rule of thumb: Reverse order of construction
  • Specifically
    • Destructor body
    • Data members in reverse order of declaration
    • Direct non-virtual base classes in reverse order
    • Virtual base classes in reverse order

Implicit Destructors

  • not specified by programmer
  • inline by default
  • public
  • recommended for struct-like POD-only objects
  • for everything else, avoid implicit destructors
    • better debugging
    • improved perf analysis

Trivial Destructors

  • Implicit
  • Not virtual
  • All direct base classes have trivial dtors
  • All non-static members have trivial dtors
  • Destructors that never do anything

Virtual Destructors

  • Guarantee that derived classes get cleaned up
  • Rule of thumb: if class has virtual functions, dtor should be virtual
    • if delete on Base* could ever point to a Derived*
  • Perf: Obj with any virtual funcs includes a vtable ptr
  • Idiom exceptions: mixin classes
  • Pure signals abstract class (virtual ~T() = 0{})

Partial Construction & Destruction

  • Dtors are only called for fully constructed objects
  • if a ctor throws, obj was not fully constructed
    • obj dtor will not be called
    • but fully constructed subobjects will be destroyed
  • Always use RAII with ctors
    • Resource Acquisition Is Initialization

Virtual Functions in Destructors

  • Virtual functions are not virtual inside dtors

C++ Exception Handling

  • Destructors : Exceptions :: Spock : Kirk
  • Wrap any function that acquires a resource in a class where dtor releases the resource
  • Never allow an exception to exit a dtor
    • Best: don’t throw in dtor
    • OK: wrap throwing code in a try/catch
  • Good advice even if you don’t use C++EH

Multithreading

  • You are responsible for protecting objects and their contents
  • Sharing an object across threads
    • Use shared_ptr
    • or some other reference counting
    • or otherwise ensure only one thread can destroy
  • Protect shared memory (global counters, ref counts) in dtor

delete and Destructors

  • delete p is a two-step process

Explicit Destructors

  • Destructors can be called directly
  • Avoid 99.9% of the time
  • Very powerful for custom memory scenarios
  • Examples
    • w / placement new
    • STL allocators

std::allocator

  • Allocators enable custom STL container memory
  • Two key destructive functions

shared_ptr

  • Templated non-intrusive deterministically referenced-counted smart pointer

shared_ptr deleters

  • Deleter : a functor called on the stored raw pointer when ref count hits zero

Performance

  • Destructors are called a LOT
  • they are invisible in code
  • streamline common dtors
  • the best dtor is empty
  • inlining
  • profile

No comments: