Variable assignment and initialization in C++

 Initialization

One downside of assignment is that it requires at least two statements: one to define the variable, and one to assign the value.

These two steps can be combined. When a variable is defined, you can also provide an initial value for the variable at the same time. This is called initialization. The value used to initialize a variable is called an initializer.

Initialization in C++ is surprisingly complex, so we’ll present a simplified view here.

There are 4 basic ways to initialize variables in C++:

int a; // no initializer
int b = 5; // initializer after equals sign
int c( 6 ); // initializer in parenthesis
int d { 7 }; // initializer in braces

You may see the above forms written with different spacing (e.g. int d{7};). Whether you use extra spaces for readability or not is a matter of personal preference.

Default initialization

When no initialization value is provided (such as for variable a above), this is called default initialization. In most cases, default initialization leaves a variable with an indeterminate value. We’ll cover this case further in lesson (1.6 -- Uninitialized variables and undefined behavior).

Copy initialization

When an initializer is provided after an equals sign, this is called copy initialization. Copy initialization was inherited from the C language.

int width = 5; // copy initialization of value 5 into variable width

Much like copy assignment, this copies the value on the right-hand side of the equals to the variable being created on the left-hand side. In the above snippet, variable width will be initialized with value 5.

Copy initialization is not used much in modern C++. However, you may still see it in older code, or in code written by developers who learned C first.

Direct initialization

When an initializer is provided inside parenthesis, this is called direct initialization.

int width( 5 ); // direct initialization of value 5 into variable width

Direct initialization was initially introduced to allow for more efficient initialization of complex objects (those with class types, which we’ll cover in a future chapter). However, like copy initialization, direct initialization is not used much in modern C++ (except for one specific case that we’ll cover when we get to it).

Brace initialization

The modern way to initialize objects in C++ is to use a form of initialization that makes use of curly braces: brace initialization (also called uniform initialization or list initialization).

Brace initialization comes in three forms:

int width { 5 }; // direct brace initialization of value 5 into variable width (preferred)
int height = { 6 }; // copy brace initialization of value 6 into variable height
int depth {}; // value initialization (see next section)

As an aside…

Prior to the introduction of brace initialization, some types of initialization required using copy initialization, and other types of initialization required using direct initialization. Brace initialization was introduced to provide a more consistent initialization syntax (which is why it is sometimes called “uniform initialization”) that works in most cases. Additionally, brace initialization provides a way to initialize objects with a list of values (which is why it is sometimes called “list initialization”).

Brace initialization has an added benefit: it disallows “narrowing conversions”. This means that if you try to brace initialize a variable using a value that the variable can not safely hold, the compiler will produce an error. For example:

int width { 4.5 }; // error: a number with a fractional value can't fit into an int

In the above snippet, we’re trying to assign a number (4.5) that has a fractional part (the .5 part) to an integer variable (which can only hold numbers without fractional parts).

Copy and direct initialization would simply drop the fractional part, resulting in the initialization of value 4 into variable width (your compiler may produce a warning about this, since losing data is rarely desired). However, with brace initialization, the compiler will generate an error instead, forcing you to remedy this issue before proceeding.

Conversions that can be done without potential data loss are allowed.

Best practice

Favor initialization using braces whenever possible.

Comments

Popular posts from this blog

Whiteboarding Interviews

Version Control with Git: A Beginner’s Guide

Callback function in JavaScript