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.

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.

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.

As mentioned earlier there are two versions of ‘push_back’ function, a detail discussion of these two functions is given below. The discussion consists 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.

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

 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 push_back call and increased by 1 in each of the 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 type accepted by 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.

 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

In the second call (line 16) two arguments are passed but push_back cannot accept two arguments hence it is an error.



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 but if it hasn’t the capacity will increased along with it’s size.

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 say another iterator points to the vector.In this case calling push_back function will increment the size and it’s capacity,this will also invalidate the iterator.Consider the code example given below.

 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 reserved 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.

 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.

Emplace_back is similar to push_back function you can read about it here C++11 Vector emplace_back




Leave a Reply

Your email address will not be published. Required fields are marked *