Initializing the Size of a C++ Vector

Initializing the size of a C++ vector

There are a few ways of creating a vector with n elements and I will even show some ways of populating a vector when you don't know the number of elements in advance.

But first

what NOT to do

std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book[i] = entry; // <-- !! Undefined Behaviour !!
}

The default constructed vector, as in the example above creates an empty vector. Accessing elements outside of the range of the vector is Undefined Behavior. And don't expect to get a nice exception. Undefined behavior means anything can happen: the program might crash or might seem to work or might work in a wonky way. Please note that using reserve doesn't change the actual size of the vector, i.e. you can't access elements outside of the size of the vector, even if you reserved for them.

And now some options analyzed

default ctor + push_back (suboptimal)

std::vector<Entry> phone_book;
for (std::size_t i = 0; i < n; ++i)
{
phone_book.push_back(entry);
}

This has the disadvantage that reallocations will occur as you push back elements. This means memory allocation, elements move (or copy if they are non-movable, or for pre c++11) and memory deallocation (with object destruction). This will most likely happen more than once for an n decently big. It is worth noting that it is guaranteed "amortized constant" for push_back which means that it won't do reallocations after each push_back. Each reallocation will increase the size geometrically. Further read: std::vector and std::string reallocation strategy

Use this when you don't know the size in advance and you don't even have an estimate for the size.

"count default-inserted instances of T" ctor with later assignments (not recommended)

std::vector<Entry> phone_book(n);
for (auto& elem : phone_book)
{
elem = entry;
}

This does not incur any reallocation, but all n elements will be initially default constructed, and then copied for each push. This is a big disadvantage and the effect on the performance will most likely be measurable. (this is less noticeable for basic types).

Don't use this as there are better alternatives for pretty much every scenario.

"count copies of elements" ctor (recommended)

std::vector<Entry> phone_book(n, entry);

This is the best method to use. As you provide all the information needed in the constructor, it will make the most efficient allocation + assignment. This has the potential to result in branchless code, with vectorized instructions for assignments if Entry has a trivial copy constructor.

default ctor + reserve + push_back (situational recommended)

vector<Entry> phone_book;
phone_book.reserve(m);

while (some_condition)
{
phone_book.push_back(entry);
}

// optional
phone_book.shrink_to_fit();

No reallocation will occur and the objects will be constructed only once until you exceed the reserved capacity. A better choice for push_back can be emplace_back.

Use this If you have a rough approximation of the size.

There is no magical formula for the reserve value. Test with different values for your particular scenarios to get the best performance for your application. At the end you can use shrink_to_fit.

default ctor + std::fill_n and std::back_inserter (situational recommended)

#include <algorithm>
#include <iterator>

std::vector<Entry> phone_book;

// at a later time
// phone_book could be non-empty at this time
std::fill_n(std::back_inserter(phone_book), n, entry);

Use this if you need to fill or add elements to the vector after its creation.

default ctor + std::generate_n and std::back_inserter (for different entry objects)

Entry entry_generator();

std::vector<Entry> phone_book;
std::generate_n(std::back_inserter(phone_book), n, [] { return entry_generator(); });

You can use this if every entry is different and obtained from a generator

Intializer list (Bonus)

Since this has become such a big answer, beyond of what the question asked, I will be remised if I didn't mention the initializer list constructor:

std::vector<Entry> phone_book{entry0, entry1, entry2, entry3};

In most scenarios this should be the default go-to constructor when you have a small list of initial values for populating the vector.


Some resources:

std::vector::vector (constructor)

std::vector::insert

standard algorithm library (with std::generate std::generate_n std::fill std::fill_n etc.)

std::back_inserter

How to initialize std::vector from C-style array?

Don't forget that you can treat pointers as iterators:

w_.assign(w, w + len);

How to initialize a vector in C++

With the new C++ standard (may need special flags to be enabled on your compiler) you can simply do:

std::vector<int> v { 34,23 };
// or
// std::vector<int> v = { 34,23 };

Or even:

std::vector<int> v(2);
v = { 34,23 };

On compilers that don't support this feature (initializer lists) yet you can emulate this with an array:

int vv[2] = { 12,43 };
std::vector<int> v(&vv[0], &vv[0]+2);

Or, for the case of assignment to an existing vector:

int vv[2] = { 12,43 };
v.assign(&vv[0], &vv[0]+2);

Like James Kanze suggested, it's more robust to have functions that give you the beginning and end of an array:

template <typename T, size_t N>
T* begin(T(&arr)[N]) { return &arr[0]; }
template <typename T, size_t N>
T* end(T(&arr)[N]) { return &arr[0]+N; }

And then you can do this without having to repeat the size all over:

int vv[] = { 12,43 };
std::vector<int> v(begin(vv), end(vv));

C++ vector set size in initialization list or resize

The latter will generally first default-construct the vector, then reallocate the vector's storage to accommodate the new length (if larger than the default) (see here for assembly code). In contrast, there will be only one allocation for the first variant (less assembly code).

Note that compiler optimizations may well see through this, but there is another reason (other than avoiding premature pessimization) to prefer the first: By explicitly initializing all members in the constructor initialization list (or inline), you can have tooling that informs you when you leave something uninitialized by accident.

how-to initialize 'const std::vectorT' like a c array

For C++11:

vector<int> luggage_combo = { 1, 2, 3, 4, 5 };

Original answer:

You would either have to wait for C++0x or use something like Boost.Assign to do that.

e.g.:

#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope

vector<int> v;
v += 1,2,3,4,5;

initialize an vector with a specified number of unitialized space

The problem is that you are creating a vector with three 0, then pushing back a further three elements. To instantiate a vector with values 1, 2 and 3, you can do this:

std::vector<int> myInts{1,2,3};

Alternatively, you can instantiate an empty vector, call reserve to avoid re-allocations, and then push back the values:

std::vector<int> myInts;
myInts.reserve(3);
myInts.push_back(1);
myInts.push_back(2);
myInts.push_back(3);

The call to reserve has the effect of allocating enough space for three elements, without initializing any elements.

Initializing variable length array

VLAs cannot be initialized by any form of initialization syntax. You have to assign the initial values to your array elements after the declaration in whichever way you prefer.

C11: 6.7.9 Initialization (p2 and p3):

No initializer shall attempt to provide a value for an object not contained within the entity being initialized.

The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.

Initializing a Vector (C++)

You really should pick up your textbook, and start reading, preferably yesterday. Nevertheless, I'll address to each section separately.

Your code (changed slightly)

double average(int a, int b, int c, int d, int e, int f, int g)
{
vector<double>aa;
aa[0]=int a;
aa[1]=int b;
aa[2]=int c;
// etc.
}

Has a few problems. The error you received was due to all the lines that looked like aa[0]=int a; The word int is not supposed to be there. If you remove them, it should compile, and crash on runtime, as aa still has a size of 0. You can either preallocate the vector (vector<double> aa(7);) or use aa.push_back(aa);.

In your second attempt

for(size_t j=0; j<=7; j++)
{
cout<<"Enter item # " <<(j+1);
cin>>a[j];
}

For starters, the first line should be for(size_t j=0; j<7; j++) (missing the =). a should be the vector<double> from the first attempt, and not the function argument.

Your lecturers code has the line

double average (const vector<int>& v)

The & before the v tells the function that it's not receiving a copy (by value) of the variable v, but rather the original copy (by reference). Often, that means that if the function changes its value, then the calling functions copy is changed. Here, however, there is that magical word const that disallows the function from changing the value(s).

Lastly, the static_cast<double> is a form of casting (changing) the variable type from an int to a double. The reason for this is that 3/2 == 1 whereas 3.0/2 == 1.5.

Double declaration and initialization of vector in a class, C++?

In your constructor:

Matrix::Matrix(int rows, int cols)
: m_nRows(rows),
m_nCols(cols)
{
std::vector <std::vector <double>> MATRIX(m_nRows, std::vector<double>(m_nCols, 0));
}

you define a brand new variable with the name MATRIX, which is totally distinct from the member variable Matrix::MATRIX.

To initialize the Matrix::MATRIX member variable you should do it in the member initializer list, jut like for the m_nRows and m_nCols variables:

Matrix::Matrix(int rows, int cols)
: m_nRows(rows),
m_nCols(cols),
MATRIX(m_nRows, std::vector<double>(m_nCols, 0))
{
}

Vector initialization in thrust

void Vector_Initialize (thrust::host_vector<double> *A, double lb, double delta)

should be replaced with

void Vector_Initialize (thrust::host_vector<double> &A, double lb, double delta)

*A means A is a pointer (may be to one vector or an array or vectors). &A means A is a reference.

If you still want to stick with *A (not recommended), replace

for (int i = 0; i < A.size(); i++)
A[i] = lb + i * delta;

with

for (int i = 0; i < A->size(); i++)
(*A)[i] = lb + i * delta;

and pass the address of h_V to Vector_Initialize function.



Related Topics



Leave a reply



Submit