sobota, 1 listopada 2014

Fundamental types initialization

Consider something as complicated as:

struct A
{
   int f; 
};

the problem is value of member 'f'.
Structure A is of course POD therefore rules for member initialization can be really tricky.
Differences comes from three sources:
- way of initalization,
- C++ standard version,
- compiler vendor.

Possible ways of initialization are:
A;
A();
A{};
which should be same to version with dynamic allocation:
new A;
new A();
new A{};

Note two things:
- it is impossible to use second case directly i.e.:
A a();
because in C++ this is function declaration (declaration can appear also inside function body).
To use second initialization method, syntax can be:
A a = A();
- third way of initialization is from C++11 and allows for:
A a{};

Initialization of fundamental type variables and PODs is subject of constant changes and obscurity between C++ standards.
In C++98 initialization of non-POD object without constructor (e.g. struct with desctructor only or inherited) does not initialize fundamental type fields to 0 (but as it is visible below it is not truth for current compilers).
This was changed in C++03.
Also compilers can introduce surprising behaviours. VSC++ (before version 2013) was famous of not initializing PODs with A() syntax (still visible for some cases).

Below are test results of initialization results for different types and compilers.
Cases are
  1. fundamental type,
  2. simplest POD struct,
  3. simplest non-POD struct (destructor added),
  4. non-POD struct with field default initialization in constructor,
  5. non-POD struct without field initialization in constructor,
  6. non-POD struct with inheritance and no constructors,
  7. non-POD struct with inheritance and field default initialization in base class constructor,
  8. non-POD struct with inheritance and field default initialization in derived class constructor.

Compilers used
  • gcc 4.8.2
  • clang 3.3
  • VisualStudio C++ 2013

1. fundamental type
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
int a; - - - - - - -
int a = int(); 0 0 0 0 0 0 0
int a{}; 0 n.a. n.a. 0 n.a. n.a. 0
int *a = new int; - - - - - - -
int *a = new int(); 0 0 0 0 0 0 0
int *a = new int{}; 0 n.a. n.a. 0 n.a. n.a. 0


2. simplest POD struct
struct A
{
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); 0 0 0 0 0 0 0
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; - - - - - - -
A *a = new A(); 0 0 0 0 0 0 0
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


3. simplest non-POD struct (destructor added)
struct A
{
   ~A() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); 0 0 0 0 0 0 -
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; - - - - - - -
A *a = new A(); 0 0 0 0 0 0 -
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


4. non-POD struct with field default initialization in constructor
struct A
{
   A() : f() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; 0 0 0 0 0 0 0
A a = A(); 0 0 0 0 0 0 0
A a{}; 0 n.a. n.a. 0 n.a. n.a. 0
A *a = new A; 0 0 0 0 0 0 0
A *a = new A(); 0 0 0 0 0 0 0
A *a = new A{}; 0 n.a. n.a. 0 n.a. n.a. 0


5. non-POD struct without field initialization in constructor
struct A
{
   A() {}
   int f; 
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; - - - - - - -
A a = A(); - - - - - - -
A a{}; - n.a. n.a. - n.a. n.a. -
A *a = new A; - - - - - - -
A *a = new A(); - - - - - - -
A *a = new A{}; - n.a. n.a. - n.a. n.a. -


6. non-POD struct with inheritance and no constructors
struct P
{
   int g;
};
struct A : public P
{
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:-
A a = A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:-
A a{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:-
A *a = new A; f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:- f:-, g:-
A *a = new A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0
A *a = new A{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:-


7. non-POD struct with inheritance and field default initialization in base class constructor
struct P
{
   P() : g() {}
   int g;
};
struct A : public P
{
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0
A a = A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:0
A a{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:0
A *a = new A; f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0 f:-, g:0
A *a = new A(); f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:0, g:0 f:-, g:0
A *a = new A{}; f:0, g:0 n.a. n.a. f:0, g:0 n.a. n.a. f:-, g:0


8. non-POD struct with inheritance and field default initialization in derived class constructor
struct P
{
   int g;
};
struct A : public P
{
   A() : f() {}
   int f;
};
init method g++ std=c++11 g++ std=c++03 g++ std=c++98 clang++ std=c++11 clang++ std=c++03 clang++ std=c++98 VSC++2013
A a; f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A a = A(); f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A a{}; f:0, g:- n.a. n.a. f:0, g:- n.a. n.a. f:0, g:-
A *a = new A; f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A *a = new A(); f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:- f:0, g:-
A *a = new A{}; f:0, g:- n.a. n.a. f:0, g:- n.a. n.a. f:0, g:-


Legend
  • "0" - initialized to 0
  • "-" - not initialized
  • "n.a." - available only in C++11

And what lesson comes form data above - to be really sure field is initialized, you have to explicitly use initialization construct for the field (in constructor initialization list).

Test application available for downlowad here. Please note placement new used to avoid false 0-positive answers. For stack allocated items it is not that easy - there is try with recursive function (to pollute stack with non 0 values) but it is not always enough (false 0 answers can happen).

Note that in VSC++2013 "__cplusplus" is defined as 199711 (like for C++98 standard) but compiler allows for C++11 usage (perhaps not complete and therefore "__cplusplus" is not defined as 201103).

Brak komentarzy:

Prześlij komentarz