C++ vector::push_back ,C++11 push_back function


The well known C++ vector ‘push_back‘ function sole purpose is to add element at the end of the vector.There are two version of ‘push_back’ function in vector.They are given below;the first one is added in C++11.(If you don’t know what is vector go to waht is STL vector container.

T : data type of the vector.

1 void push_back(T&& x) ; (C++11)
2 void push_back(const T& x);

The vector push_back help us in many ways by allowing easy appending of data to the current existing vector in case prior memory reserve is not made.In array the size of the storage is fixed and if we require enlarging the array size we must allocate another array with greater size and copy the content to the new array.This undoubtedly require extra code and also copying the content to the new array will consume lot of time if the process becomes repetitive and thus slow your program tremendously.To avoid all the unavoidable predicament that might happen in using array of fix size we can replace it with vector and call the ‘push_back’ function whenever we want to add extra data to the vector.You can rest easy cause the vector will take care of the memory adjustment however large it may get.A code example is given below.

Code example

int arr[3] ={2 ,34 , 900}; //arr can store only three integers

vector<int> vec={23};

vec.push_back( 34 ); //second element added

vec.push_back( 100 ); //third element added

vecc.push_back( 500 ); //fourth element added

/* ..you can keep adding data **/

for(auto elem:vec ) //accessing vector element using range-based for loop
{
cout<< elem << ” ” ;
}

Output,

23 34 100 500

You can see the vector will accept as many data as possible without worrying about running out of storage(assuming there is enough memory),vector in fact is a perfect substitution for an array.

Link :C++11 range-based for loop

As mentioned earlier there are two versions of ‘push_back’ function,a detail discussion of these two functions is given below.The discussion consist of what type of value they accept and also their effect on the ‘size’ and ‘capacity’ of the vector.



void push_back(T&& x) ; (C++11)


Parameters:
x – rvalue to be added at the end of the vector.

Return type
void

This function is called when the data to be added is a rvalue,for instance a literal.Since it accept a rvalue it moves the data instead of copying the data.

Code example

vector<int> vec ;

vec.push_back( 23 ) ; //calls this version

cout<< vec[0] ;

Output

23

Size and capacity

This function increase the capacity of the vector if the storage is not reserve previously.However,if the storage is reserve using the reserve() function ,calling this function only increases it’s size not it’s capacity.The capacity will increase when you add data more than the reserved size.

Link:vector::reserve function

vector<string> vecSt={“Angry” } ,
vecSt1 ;

cout<< “vecSt capacity before calling push_back=” << vecSt.capacity() ;

vecSt.push_back( “Sad” ) ;//calls this version

cout<< “vecSt capacity after calling push_back=” << vecSt.capacity( ) ;

vecSt1.reserve( 3 ) ; //Size reserve for three elements

vecSt1.push_back( “Jealousy” ) ;
vecSt1.push_back( “Hatred” );
vecSt1.push_back( “Crime” );

cout<< “vecSt1 capacity after calling push_back=” << vecSt1.capacity( ) ;

vecSt1.push_back(“Law”) ;

cout<< “vecSt1 capacity after adding 4th data=” << vecSt1.capacity( ) ;

Output

vecSt capacity before calling push_back= 1
vecSt capacity after calling push_back= 3
vecSt1 capacity after calling push_back=3
vecSt1 capacity after adding 4th data=6

How much capacity is increased will depend on the compiler,in this case the capacity is increase by two at the first pusch_back call and increased by 1 in each following push_back call.

Moving lvalue

If you pass a lvalue then the second push_back version is called.However if you want to call this function to move a lvalue to the vector,then use the function std::move( ).

vector<int> vec;
int val=90 ;

vec.push_back( val ) ; ///calls the second version

vec.push_back( std::move( val )); ///calls this version

Using push_back with user-defined type

When the type is a user-defined type:class or structure,and if the constructor accepts only one argument then you can simply use push_back to add an object of the type or you can pass a data of the argument type of the constructor.

class Data
{
int i;

public:
Data(int ii):i(ii) {}

~Data ( ) { }
};

vector<Data> vec;

vec.push_back( Data(78) ) ;///adding an object of Data

vec.push_back( 89 ); ///work fine,Data constructor accept integer

Passing raw data is acceptable because the push_back function will create a temporary object of the type,this temporary object is again copied into the vector.

If the vector type constructor accept more than one argument you can use push_back to add only an object of the type,you cannot pass raw data arguments.The push_back cannot accept two arguments,so it cannot create an object from the arguments passed.

class Type
{
int i;
string s ;

public:
Type(int ii ,string ss):i(ii),s(ss) { }

~Type( ) { }
};

vector<Type> vec;

vec.push_back( Type(78 , “Hello”) ) ;//adding an object of Type

vec.push_back( 89 ,”World” ); //Error!,raw data is passed






void push_back( const T& x ) ;


Parameters:
x – value to be added at the end of the vector.

Return type
void

This version of push_back is call when you pass a lvalue or a reference as the argument.In calling this function also the capacity remains the same if the the vector has reserve some storage else the capacity will increased along with it’s size.

Code example

vector<int> vec={23} , vec1 ;
int val=90 , &ref=val ;

vec1.reserve( 2 );

cout<< “vec capacity before push_back call=” << vec.capacity( ) << endl ;

vec.push_back( ref ) ;

cout<< “vec capacity after push_back call=” << vec.capacity( ) << endl ;

cout<< “vec capacity before push_back call=” << vec1.capacity( ) << endl ;

vec1.push_back( ref ) ;

cout<< “vec capacity after push_back call=” << vec1.capacity( ) << endl;

Output

vec capacity before push_back call=1
vec capacity after push_back call=3
vec1 capacity before push_back call=2
vec1 capacity after push_back call=2





push_back invalidates iterators,pointers and reference, referring to the vector if the size exceeds the current capacity.

Suppose a storage is not reserve for the vector and an iterator points to the vector.Calling push_back function will increment the size and it’s capacity,this will also invalidate the iterator.Consider the code example given below.

Code example

vector<char> vecC={‘B’ , ‘M’ } ;

vector<char>::iterator vecCIt=vecC.begin( ) ;

cout<< vecCIt[0] << ” ” << vecCIt[1] << endl;

vec.push_back( ‘L’ ) ;

cout<< vecCIt[0] ;//undefined ,vecCIt is invalidated

Output

B M
undefined value

However if you have reserve some storage then the iterator is valid as long as the element added is less than or equal to the reserve size.Once you add a data more than the current reserve size the iterator becomes invalid.

Code example

vector<int> vec ;

vec.reserve( 2 );

vector<int>::iterator vecIt=vec.begin( ) ;

vec.push_back(90);

cout<< vecIt[0] << vec[1] << endl; ///work fine

ve.push_back(45) ;

cout<< vecIt[0] << ” ” vecIt[1] << endl; ///work fine

vec.push_back( 505 ) ; //adds the 3rd data

cout<< vecIt[0] endl ; //undefined
<< vecIt[1] ;//undefined

Output

90
90 45
undefined value
undefined value

When the data added exceeds the reserve size a new allocation is made.With this reallocation the storage will have new addresses, but the iterator is still pointing to the previous storage which has now become invalid.Hence the iterator is invalidated.



Related links

->C++ emplace_back vector

->C++ pop_back vector

->C++ capacity vector