Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are members of structs in a vector initialized with zero in C++?

Tags:

c++

struct

vector

In C++, when I have a struct like

struct myStruct
{
  int i;
  bool b;
  MyClass *myobj;
};

and I then make a vector of this

std::vector<myStruct> myVector;

and I resize the vector with

myVector.resize(10);

Will the struct's members be initialized with zero's (including the pointer)? Can I not assume that and might there be random data in the struct's members?

like image 386
Bram Vaessen Avatar asked Dec 11 '22 04:12

Bram Vaessen


2 Answers

In this particular case, the answer is YES because of std::vector::resize()'s properties:

If the current size is less than count, additional elements are appended and initialized with copies of value.      (until C++11)

If the current size is less than count,  (since C++11)
    1) additional value-initialized elements are appended
    2) additional copies of value are appended

The answer will be NO for normal cases, e.g. myStruct s, this will leave them (s.i, s.bool, s.myobj) with indeterminate values.

If you still want them to be initialized as expected, create a constructor for the struct:

struct myStruct {
    int i;
    bool b;
    MyClass *myobj;

    myStruct():i(0),b(false),myobj(NULL) { }  // <- set them here by default
};
like image 160
herohuyongtao Avatar answered Dec 31 '22 01:12

herohuyongtao


I actually beg to differ. In this particular case, the standard appears to guarantee that the objects will be zero-initialized. (All standard references below are to N3936.)

vector::resize in this case is specified to append 10 "default-inserted elements to the sequence" (§23.3.6.3 [vector.capacity]/p12).

Default-insertion is in turn defined as (§23.2.1 [container.requirements.general]/p14; X is the container's type; m is an lvalue of type A, which is the container's allocator_type):

An element of X is default-inserted if it is initialized by evaluation of the expression

allocator_traits<A>::construct(m, p)

where p is the address of the uninitialized storage for the element allocated within X.

Since as written the code uses the default allocator, the allocator_traits::construct call simply calls construct(p) on an instance of std::allocator<myStruct> (§20.7.8.2 [allocator.traits.members]/p5), which is specified as (§20.7.9.1 [allocator.members]/p12)

template <class U, class... Args>
void construct(U* p, Args&&... args);

12 Effects: ::new((void *)p) U(std::forward<Args>(args)...)

Since the parameter pack is empty for construct(p), the effect of the construct() call is::new((void *)p) myStruct().

The standard provides that new-initializers, like the () in the placement new expression above, are to be interpreted "according to the initialization rules of 8.5 for direct initialization" (§5.3.4 [expr.new]/p17). In turn, §8.5 [dcl.init]/p11 specifies that "An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized."

Value-initialization is specified as (§8.5 [dcl.init]/p8)

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
  • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized.

In this case, myStruct is a "class type without a user-provided or deleted default constructor", which means it matches the second bullet point, which performs zero-initialization. Thus, value-initialization for an object of type myStruct means the object will be zero-initialized.

However, note that the rules here are quite convoluted, and the path to automatic zero-initialization is quite fragile. For example, if you give myStruct a default constructor like myStruct() { }, then that's a user-provided default constructor, which means that it will match the first bullet point for value initialization instead of the second, which in turn means that it won't be zero-initialized. Moreover, this may also not work if your vector uses a custom allocator, as its construct() might have different semantics than that of the default allocator.

Therefore, it is probably better to give myStruct a default constructor that explicitly zeroes its members.

like image 40
T.C. Avatar answered Dec 31 '22 00:12

T.C.