Tip of the Week #61: Default Member Initializers

Originally posted as Totw #61 on Nov 12, 2013

by Michael Chastain ([email protected])

Updated October, 2016

Declaring Default Member Initialization

A default member initializer declares a default value for a member upon construction and looks like this:

class Client {
 private:
  int chunks_in_flight_ = 0;
};

This default initializer propagates into all constructors for that class, even constructors that C++ synthesizes. Initializing members in this way is useful for classes with lots of data members, especially for types such as bool, int, double, and raw pointers. Non-static data members of these fundamental types often slip through the cracks and end up uninitialized. Non-static data members of any type may have initializers, though.

Default member initializers are also useful for declarations of simple structs with no user-written constructor:

struct Options {
  bool use_loas = true;
  bool log_pii = false;
  int timeout_ms = 60 * 1000;
  std::array<int, 4> timeout_backoff_ms = { 10, 100, 1000, 10 * 1000 };
};

Member Initialization Overrides

If a class constructor initializes a data member that already has a default initializer, the initializer in the constructor supersedes the default:

class Frobber {
 public:
  Frobber() : ptr_(nullptr), length_(0) { }
  Frobber(const char* ptr, size_t length)
    : ptr_(ptr), length_(length) { }
  Frobber(const char* ptr) : ptr_(ptr) { }
 private:
  const char* ptr_;
  // length_ has a non-static class member initializer
  const size_t length_ = strlen(ptr_);
};

This code is equivalent to the older code:

class Frobber {
 public:
  Frobber() : ptr_(nullptr), length_(0) { }
  Frobber(const char* ptr, size_t length)
    : ptr_(ptr), length_(length) { }
  Frobber(const char* ptr)
    : ptr_(ptr), length_(strlen(ptr_)) { }
 private:
  const char* ptr_;
  const size_t length_;
};

Note that the first and second Frobber constructors have initializers for their non-static variables; these two constructors will not use the default initializer for length_. The third Frobber constructor, however, does not have an initializer for length_ so this constructor will use the default initializer for length_.

As always in C++, all non-static variables are initialized in the order of their declaration.

In the first 2 of the 3 Frobber constructors, the constructor provides an initializer for length_. The constructor initializer supersedes the default member initializer – the non-static class member initializer does not contribute to code generation for these constructors.

Note: Older documentation may refer to default member initializers as non-static data member initializers, abbreviated to NSDMIs.

Conclusion

Default member initializers won’t make your program any faster. They will help reduce bugs from omissions, especially when someone adds a new constructor or a new data member.

Be careful not to confuse a non-static class member initializer with a static class member initializer:

class Alpha {
 private:
  static int counter_ = 0;
};

This is an older feature. counter_ is static and this is a static declaration with an initializer. This is different from a non-static class member initializer, just as static member variables are different from non-static member variables.


Subscribe to the Abseil Blog