Initialization provides an object's initial value. The object's type,scope, storage duration, and context determine whether and how it isinitialized.
Static objects
Objects with static storage duration (i.e., variables declared at namespacescope, static
class members, and static
local variables) and no explicitinitializer are guaranteed to be zero-initialized:
bool b; // falsechar c; // '\0'int i; // 0double d; // 0.0void *p; // nullptrvoid f(){ static int i; // 0}class T { static int i; // declaration; definition required below};int T::i; // 0
Exactly how zero initialization occurs is implementation defined, butconventionally static objects are placed in the .bss
section of theexecutable image, where they occupy no space, and are zero-filled at programstart-up (by the operating system, program loader, or language run-time).
Static objects of class type with a default constructor, that are notexplicitly initialized, are default-initialized atrun-time, before first use. When, exactly, this happens depends on theobject's scope, and, in some cases, on the compiler. static
locals areinitialized the first time control passes through their declaration; globalsand static
members are ostensibly initialized at program start-up, beforemain()
, but compilers can defer initialization until first use:
#include <string>std::string s; // default-initialized before main() or first usevoid f(){ static std::string s; // default-initialized when f() is first called}
The order of initialization for static objects defined within a translationunit is defined (they're initialized in the order they're declared), but theorder of initialization of objects defined in different translation units isundefined. Further, the objects defined in a particular translation unit maynever be initialized if none of them is ever used by the program.
Initial values for static objects can be provided with an initializer:
#include <string>// constant initializationint i = 42;// zero initialization followed by list initializationstd::string s{"foo"};class T { constexpr T(int x) : x{x} {} int x;};// candidate for constant initializationT w = 42;
Compilers may be able to use constant initialization to construct theobject at compile-time and emit its representation in the initialized .data
section of the program image. That's the case for objects of built-in type andclass types with constexpr
constructors and constant constructor arguments.
For objects of class type without constexpr
constructors, the classconstructor is called at run-time, before first use, as described above fordefault initialization. This dynamic initialization stage occurs after thestatic zero initialization stage, which zero-fills the object's store atprogram start-up, before its constructor is invoked.
Automatic objects
Objects with automatic storage duration (i.e., local variables) areinitialized each time control reaches their declaration:
#include <string>void f(){ int i = 42; // initialized each time f() is called while (i--) { std::string s{"foo"}; // initialized each iteration }}
Without an explicit initializer, automatic objects are default-initialized.Objects of class type with a default constructor are default-initialized bycalling the default constructor. Objects of class type without a defaultconstructor and objects of built-in type are left uninitialized bydefault initialization. Their value is not defined; compilers should emitwarnings if such variables are used uninitialized. According to Stroustrup(TC++PL):
The only really good case for an uninitialized variable is a large inputbuffer.
Dynamic objects
Objects with dynamic storage duration (i.e., allocated with new
) areinitialized by operator new
after it allocates memory for the object:
int *pi = new int; // uninitializedint *pj = new int{42}; // direct-initializedT *pt = new T; // default-initializedT *pu = new T{42}; // list-initialized
As with automatic objects, dynamic objects are default-initialized ifno intializer is provided. Also like automatic objects, objects of built-intype are left uninitialized by default initialization; their values arenot defined.
Non-static members
Class constructors are responsible for initializing their non-static
members using an initializer list or in-class initializer:
class T { // Default constructor, default-initializes all members T() = default; // Constructor with initializer list explicit T(int x) : i{x} // direct-initialized , j{} // value-initialized {} int i = 42; // in-class initializer; overridden by initializer list int j; // uninitialized by default constructor int k; // uninitialized}
In-class initializers provide default values that can be overridden byconstructor initializer lists; if both are provided, the initializer listtakes precedence.
The generated default constructor default-initializes all non-static
members. As before, members of built-in type are left uninitialized bydefault initialization, and therefore will have undefined values.
Initializers
There are four syntactic styles of initialization:
T object{ arg1, arg2, ... };
T object = { arg1, arg2, ... };
T object(arg1, arg2, ... );
T object = arg;
What they mean depends on their context and the type T.
List Initialization
#include <string>#include <vector>std::string s{"foo"}; // direct list initializationstd::string t = {"foo"}; // copy list initializationstd::vector<int> v{1, 2, 3}; // direct list initializationstd::vector<int> w = {1, 2, 3}; // copy list initializationclass T { T(const std::string& s) : s{s} // direct list initialization {} std::string s; std::string t{"foo"}; // direct list initialization std::string u = {"foo"}; // copy list initialization};T* tp = new T{"foo"}; // direct list initializationT f(T t){ return {"foo"}; // copy list initialization}void g(){ f({"foo"}); // copy list initialization}
These are all forms of list initialization. The common element is thebraced-init-list, {...}
. List initialization is new in C++11, and, accordingto Stroustrup, is the preferred method because it's general (available in allcontexts) and safe (immune to narrowing conversions).
The two forms of list initialization, direct and copy, differ in whether theycan call explicit
constructors: direct list initialization can,copy list initialization cannot.
Generally, a list initialization of the form T t{arg1, arg2, ...}
will firstsearch for a constructor taking a std::initializer_list<>
as the onlynon-default argument, and failing to find a match will then search for aconstructor taking the number and type of arguments as specified in thebraced-init-list, allowing only non-narrowing conversions. Thus, after:
#include <vector>std::vector<int> v{100, 0};std::vector<int> w(100, 0);
v
contains exactly two elements (100
and 0
), while w
contains 100elements, all 0
, because std::vector<T>
has both kinds of constructor:
template<typename T>class vector { vector(std::initializer_list<T> init); explicit vector(size_type count, const T& value = T());}
List initialization reduces to value, aggregate, direct, orcopy initialization in certain cases:
- an empty braced-init-list denotes value initialization, which producesthe default value for the type
- a braced-init-list applied to an aggregate type denotesaggregate initialization, which initializes individual members of theaggregate
- a braced-init-list applied to a specialization of the
std::initializer_list
template denotes direct or copy initialization ofthatstd::initializer_list
object
Value Initialization
#include <string>bool b{}; // falsechar c{}; // '\0'int i{}; // 0double d{}; // 0.0std::string s{}; // ""
Value initialization is, in effect, zero initialization followed bydefault initialization. Because default initialization leaves objects ofbuilt-in type uninitialized, value initialization of built-in types isequivalent to zero initialization:
void f(){ int i{}; // 0 int j; // undefined}
Aggregate Initialization
struct T { int x; int y; int z;};T t{1, 2, 3}; // { 1, 2, 3 }T u = {1, 2, 3}; // { 1, 2, 3 }T v{1}; // { 1, 0, 0 }char a[] = {'f', 'o', 'o', '\0'};char b[] = "foo"; // equivalentchar c[10] = {}; // zero-filled
Aggregates are arrays and simple class types (typically struct
s andunion
s) with no bases, no private members, and no user-providedconstructors. The individual elements/members of the aggregate areinitialized, in order, from the elements in the braced-init-list. If notspecified, the array size is determined by the length of the initializer. Ifthe initializer contains fewer elements than the type requires, the remainingelements are zero-initialized. As a special case, character arrays can beaggregate-initialized from string literals.
Proving that C++ is not a superset of C, C++ does not allow designatedinitializers (until C++20, at least), which C has had since C99:
struct T { int x; int y; int z;};T t = { .x = 1, // oops! can't do this in C++ .y = 2, .z = 3,};
Direct Initialization
#include <string>#include <vector>int i(42);int j{42}; // special case for braced-init-list with built-in typesstd::string s("foo");std::string t(10, 'a'); // "aaaaaaaaaa"std::vector<int> v(10); // 10 0sstd::vector<int> v(10, 5); // 10 42s
Direct intialization searches for a compatible constructor, consideringexplicit
and non-explicit
constructors, and performing user-defined orstandard conversions, as necessary.
Direct initialization is susceptible to the most vexing parse:
T f(); // function declaration, not (empty) direct initialization
Copy Initialization
#include <string>int i = 42;std::string s = "foo";std::string f(std::string s) // s is copy-initialized from argument{ return s + "foo";}void g(){ std::string s = "foo"; std::string t = f(s); // t is copy-initialized from temporary}
Copy initialization is less permissive than direct initialization: it onlysearches non-explicit
constructors, and requires that the initializer beconvertible to the object type.