C++ casting

Casting is a conversion process to change data from one type to another. C++ has two types of conversions:
  1. Implicit conversion : Conversions are performed automatically by the compiler without the programmer's intervention. They are automatically performed when a value is copied to a compatible type.   For example :
    short a=2000;
    int b;
    b=a;
    Below table showing possible implicit type conversion.
    Some of these conversions may imply a loss of precision, which can signal compiler warning. This warning can prevent explicit conversions. Constructor or operator conversions in applicable conversions, which affect classes that include specific constructors or operator functions to perform conversions. For example:
    class A {};
    class B { public: B (A a) {} };
    A a;
    B b=a;
    Here, an implicit conversion happened between objects of class A and class B, because B has a constructor that takes an object of class A as parameter. Therefore implicit conversions from A to B are allowed.

  2. Explicit conversion : Conversions are performed only when explicitly specified by the programmer. 
    short a=2000;
    int b;
    b = (int) a;    // c-like cast notation
    b = int (a);    // functional notation 
    Explicit conversion on class objects also possible using below method :
    // class type-casting
    #include <iostream>
    using namespace std;
    
    class Dummy {
        float i,j;
    };
    
    class Addition {
     int x,y;
      public:
     Addition (int a, int b) { x=a; y=b; }
     int result() { return x+y;}
    };
    
    int main () {
      Dummy d;
      Addition * padd;
      padd = (Addition*) &d;
      cout << padd->result();
      return 0;
    } 
    
    In this case result will produce either a run-time error or a unexpected result. To control this type of issue C++ provides four type of casting operator : dynamic_cast, reinterpret_cast, static_cast and const_cast.
Their format is to follow the new type enclosed between angle-brackets (<>) and immediately after, the expression to be converted between parentheses.
dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)

Lets discuss this one by one :

static_cast :


Format : 
static_cast<type>(expression);
Example : 
double d=3.14159265;
int i = static_cast<int>(d);

The static_cast tells the compiler to attempt to convert between two different data types. It will convert between built-in types, even when there is a loss of precision. In addition, the static_cast operator can also convert between related pointer types. static_cast used when developer knows type casting is possible, because run time compiler not checking casting worked properly or not. Therefore, it is up to the programmer to ensure that the conversion is safe.
int* pToInt = &iVariable;
float* pToFloat = &fVariable;
float* pResult = static_cast<float*>(pToInt); 
//Will not work as the pointers are not related 
//(they are of different types). 

static_cast also possible on class object by following way :
class Base {};
class Derived: public CBase {};
Base * a = new Base;
Derived * b = static_cast<Derived*>(a);
This would be valid, although b would point to an incomplete object of the class and could lead to run-time errors if de-referenced.

dynamic_cast: 


Format :
dynamic_cast<type>(expression);
Example : 
int iVariable = 10;
float fVariable = dynamic_cast<float>(iVariable); 
/*This statement converts iVariable which is of type int to float. */
dynamic_cast can be used only with pointers and references to objects. Its purpose is to ensure that the result of the type conversion is a valid complete object of the requested class. dynamic_cast requires the Run-Time Type Information (RTTI) to keep track of dynamic types. Some compilers support this feature as an option which is disabled by default. This must be enabled for runtime type checking using dynamic_cast to work properly.

dynamic_cast is always successful when we cast a class to one of its base classes:
class Base { };
class Derived: public Base { };

Base b; Base* pb;
Derived d; Derived* pd;

pb = dynamic_cast<base>(&d);     // ok: derived-to-base
pd = dynamic_cast<derived>(&b);  // wrong: base-to-derived
The second expression in this piece of code would produce a compilation error since base to derived conversions are not allowed with dynamic_cast unless the base class is polymorphic.

// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;

class Base { virtual void dummy() {} };
class Derived: public Base { int a; };

int main () {
  try {
    Base * pba = new Derived;
    Base * pbb = new Base;
    Derived * pd;

    pd = dynamic_cast<Derived*>(pba);
    if (pd==0) cout << "Null pointer on first type-cast" << endl;

    pd = dynamic_cast<Derived*>(pbb);
    if (pd==0) cout << "Null pointer on second type-cast" << endl;

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}
Output : Null pointer on second type-cast

dynamic_cast can also cast null pointers when casting performed between unrelated classes, and can also cast pointers of any type to void pointers (void*).

const_cast: 


Format :
const_cast<type>(expression);
Example :
// const_cast
#include <iostream>
using namespace std;

void print (char * str)
{
  cout << str << endl;
}

int main () {
  const char * c = "sample text";
  print ( const_cast<char *> (c) );
  return 0;
}
Output : sample text
const_cast does not cast between different types. Instead it changes the "constness" of the expression. It can make something const what was not const before, or it can make something volatile or changeable by getting rid of the const.


reinterpret_cast:


Format :
reinterpret_cast<type>(expression); 
Example :
class A {};
class B {};
A * a = new A;
B * b = reinterpret_cast<B*>(a);
reinterpret_cast converts any pointer type to any other pointer type, even of unrelated classes. The operation result is a simple binary copy of the value from one pointer to the other. All pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked.

typeid :


typeid allows to check the type of an expression: 

Format:
typeid (expression)
Example :
// typeid
#include <iostream>
#include <typeinfo>
using namespace std;

int main () {
  int * a,b;
  a=0; b=0;
  if (typeid(a) != typeid(b))
  {
    cout << "a and b are of different types:\n";
    cout << "a is: " << typeid(a).name() << '\n';
    cout << "b is: " << typeid(b).name() << '\n';
  }
  return 0;
}
Output:
a and b are of different types:
a is: int *
b is: int 

typeid  also used to keeping track of dynamic objects.When typeid is applied to an expression whose type is a polymorphic class, the result is the type of the most derived complete object:

// typeid, polymorphic class
#include <iostream>
#include <typeinfo>
#include <exception>
using namespace std;

class Base { virtual void f(){} };
class Derived : public Base {};

int main () {
  try {
    Base* a = new Base;
    Base* b = new Derived;
    cout << "a is: " << typeid(a).name() << '\n';
    cout << "b is: " << typeid(b).name() << '\n';
    cout << "*a is: " << typeid(*a).name() << '\n';
    cout << "*b is: " << typeid(*b).name() << '\n';
  } catch (exception& e) { cout << "Exception: " << e.what() << endl; }
  return 0;
}
Output:
a is: class Base *
b is: class Base *
*a is: class Base
*b is: class Derived

If the type typeid evaluates is a pointer preceded by the de-reference operator (*), and this pointer has a null value, typeid throws a bad_typeid exception.

So this all about type conversion, please add your valuable comments in comment section. 

C-Style array Vs std::array

In this article we will about study C-style array vs std::array. A std::array should have same run-time performance as a c-style array. In STL there are two type of arrays, first is fixed length array means std::array and other type is dynamic array means std::vector. 

Introduced in C++11, std::array has a unique characteristic among all sequential containers of the STL, you can not adjust it size during run-time.

Initialization of std::array


std::array initialized by following ways: 

// Required header file
#include <array> 
 
// Declare an integer array with length 4
std::array<int, 4> myArray; 
// Initialization list
std::array<int, 5> myArray = { 9, 8, 5, 3, 1 };
// Uniform initialization
std::array<int, 5> myArray { 9, 8, 5, 3, 1 }; 
// The 10 elements are not initialized.
std::array<int,10> myArray ; 
The 10 elements are value-initialized to zero.
std::array<int,10> myArray {};
// Legal, elements 3 and 4 are set to zero! 
std::array<int> myArray = { 9, 7, 5}; 
// Illegal, array length must be provided
std::array<int, > myArray = { 9, 8, 5, 3, 1 };
// Illegal, too many elements in initializer list!
std::array<int, >  myArray = { 0, 1, 2, 3, 4, 5 }; 

Index access


std::array supports the index access in 3 ways :

  1. myArray[n-1]:  Access to the nth element without a check of the array boundaries.
  2. myArray.at(n-1): Access to the nth element with a check of the array boundaries. Eventually, a std::range_error exception is thrown.
  3. std::get(myArray): Access to the nth element with a check of the array boundaries at compile time. The syntax is according to std::tuplestd::get(myArray) shows the relationship of std::array with std::tuplestd::array is a homogeneous container of fixed size while std::tuple is a heterogeneous container of fixed size.

Example: C-Style array Vs std::array

#include <iostream>
#include <array>
#include <vector>
 
 
int main()
{
  // c-style array
  int array1[3] = {1, 2, 3};
 
  array1[1] = 15;
 
  std::cout << "size: " << (sizeof(array1) / sizeof(*array1)) << std::endl;
 
  for (auto element : array1)
  {
    std::cout << element << std::endl;
  }
   
  // std-style array
  std::array<int,3> array2 = { 4, 5, 6 };
 
  array2[1] = 25;
 
  std::cout << "size: " <<array2.size() << std::endl;
 
  for (auto element : array2)
  {
    std::cout << element << std::endl;
  }
}
Here, array2.size() will return array size, no need of calculation like - (sizeof(array1) / sizeof(*array1)).


Array as method parameter:


Using below way you can pass std::array as string parameter : 
#include <iostream>
#include <vector>
#include <array>

void PrintArray(int* values, int size)
{
  std::cout << "size: " << size << std::endl;
 
  for (int i = 0; i < size; ++i)
  {
    std::cout << values[i] << std::endl;
  }
}
 
void PrintArray(const std::array<int,3> &values)
{
  std::cout << "size: " << values.size() << std::endl;
 
  for (auto& value : values)
  {
    std::cout << value << std::endl;
  }
}
 
int main()
{
  int array1[3] = { 1, 2, 3 };
  PrintArray(array1, 3);
   
  std::array<int,3> array2 = { 4, 5, 6 };
  PrintArray(array2);
}

Array as return value


std::array can be used as return value. Example -
#include <iostream>
#include <vector>
#include <array>

int* CreateArray()
{
  int* array = new int[3] { 1, 2, 3 };
  return array;
}
 
std::array<int,3> CreateStdArray()
{
  std::array<int,3> array = { 4, 5, 6 };
  return array;
}
 
int main()
{
  // c-style array
  int* array1 = CreateArray();
  int size = 3;   // you have to know the size of the array
 
  for (int i = 0; i < size; ++i)
  {
    std::cout << array1[i] << std::endl;
  }
 
  delete[] array1; // you have to delete the array
    
  // std-style array
  std::array<int,3> array2 = CreateStdArray();
 
  for (auto element : array2)
  {
    std::cout << element << std::endl;
  }
}
In this case called method should know data type and size of array before calling function, for variable length of array std::array is not useful.

Sort std::array


Using std::sort algorithm we can sort std::array elements. If any error occurred during execution will throw an out of range exception..

#include <iostream>
#include <vector>
#include <array>
#include <algorithm>

int main()
{
  // c-style array
  int array1[3] = {7, 5, 3};
   
  std::sort(std::begin(array1), std::end(array1));
 
  for (auto element : array1)
  {
    std::cout << element << std::endl;
  }
   
  // std-style array
  std::array<int,3> array2 = { 7, 5, 3 };
 
  std::sort(array2.begin(), array2.end());
   
  for (auto element : array2)
  {
    std::cout << element << std::endl;
  }
}

Multi-dimensional arrays


The std::array as well as the c-style array support multidimensions. These multidimensions are designed as arrays of nested arrays. 
#include <iostream>
#include <array>
int main()
{
  // c-style array
  int array1[2][3] = { { 1, 2, 3 }, { 4, 5, 6} };
 
  array1[0][1] = 10;
   
  for (auto &row : array1)
  {
    for (auto &element : row)
    {
      std::cout << element << std::endl;
    }
  }     
 
  // std-style array
  std::array<std::array<int,3>, 2> array2{ { { 7, 8, 9 }, { 10, 11, 12 } } };
 
  array2[0][1] = 20;
 
  for (auto &row : array2)
  {
    for (auto &element : row)
    {
      std::cout << element << std::endl;
    }
  }
}

Summary


The std::array is designed as zero-overhead wrapper for c-style arrays. Therefore, it comes with all the advantages of a c-style array and ads the additional advantages of C++ containers. I would recommend to use std::arrays except you have a rare special use case which comes with good reasons to use a c-style array.

Advantages of std::array
  • Perfectly matches with the strict type system concept of c++
  • Copy semantic / no pointer decay / the array doesn’t convert to a pointer unless you explicitly ask it to
  • It knows its size
  • Range check in debug builds
  • No memory overhead beyond what it needs to hold its elements
  • Same performance as a c-style array
  • Direct support of use in STL algorithms

Disadvantages of std::array
  • Slightly more awkward syntax for declaration of the array, especially when creating multi-dimensional arrays

C++ Singleton design pattern

The singleton pattern is simplest design pattern, it involves only one class which is responsible to instantiate itself, to make sure it creates not more than one instance. Also provides a global point of access to that instance. There two important use of singleton design pattern :
  • One instance of a class is created.
  • It provides a global point of access to the object.
Let see one simple example of singleton pattern :

#include <string>

class Logger{
public:
   static Logger* Instance();
   bool openLogFile(std::string logFile);
   void writeToLogFile();
   bool closeLogFile();

private:
   Logger(){};  // Private default constructor
   Logger(Logger const&){};             // copy constructor is private
   Logger& operator=(Logger const&){};  // assignment operator is private
   static Logger* m_pInstance;  //Static member 
};

// Global static pointer
Logger* Logger::m_pInstance = NULL;  

Logger* Logger::Instance()
{
   if (!m_pInstance)   // One instance of class to be generated.
      m_pInstance = new Logger;

   return m_pInstance;
}

bool Logger::openLogFile(std::string _logFile)
{
   ...
}
Explanation :

  • instance() function returns a pointer to a static variable and thus is declared static.
  • Only instance() can call the constructor. Public access to the constructor is denied.
  • The constructor, copy constructor and assignment operator are declared as private.
  • The life of the singleton is for the duration of the application.
Usage:
Logger::Instance()->openLogFile("logFile.txt");

Examples and usages :
  1. Logger Classes - Logger class is implemented using singleton pattern and all application is using same instance of class.
  2. Configuration Classes - In application configuration class also using singleton design pattern. only one time configuration setting is loaded by application.
  3.  Accessing resources in shared mode - A singleton with synchronized methods could be used to be used to manage all the operations on the serial port.

Why employees quit their job?

Why employees quit their job? If you ask this question to HR person you will get answers, because of they are getting good offer. This might be one of reason, But as a employee point of view their are many reasons because of employees leave their job.
1) Boring project: Most of employees fill project is boar after working in same project 2-3 years. Nowadays most of company not accept this reason to change project, because of this employees live their job.
2) Poor HR management : Most of company HR is not touch with employee if you working in billable project or client project. Most of cases, observed HR will try to contact if you loss project , but if you working in project then all discussions depends on manager.
3) Company policies : If you read company policies you will fill Company is good, if many companies are working on hidden policies, not on written policies. For example, Policy - if employee want to work near to his hometown and our branch there they company will try for your relocation. But in reality company always checking their business need first, if that person is more important in current location then they'll try to neglect your demands. Also my second and most well-known example to employees also. Policy is - If case reported on employee like harassment, company fraud , then there will be proper in investigation will happen , if employee found guilty then he will loss his job , But reality is not like that in such case company directly asking put resignation, because company don't want to waist money in doing investigation.
4) Poor Team bounding: Because of busy project schedule manager or HR not getting time to bound Team. Because of this reason employee filling alone while working. Suppose employee X working in some critical task from someday he wants some help from team but teams are not ready to help, because of this reason he is filling alone and he might try to leave company.
5) Working on Outdated Technologies: If employees is working on old technology and now outside  company this technology is not useful then employee will fill insecure about this feature and he will try to change job.
6) Health Issue : If company environment or company location is not good for health then employee will leave job though job is good. For example , employee observed skin infection because of chemical used in company. Second weather like Mumbai and Chennai is not suitable for many people.
7) Company restrictions : Some company always restrict to employees they should work with only one organization, But if he is not satisfied with current company salary and want to work outside. This might be reason of employee leave their job. Other example like employee X working in company also he like to some political work outside, But company dont want employee should have direct contact with politic. 
Above issues company can control some extent. But sometimes employee also have other issues because of this employee leave his job.
1) Children Education: If employee work location dont have any education facilities, for children education many employees trying to change job.
2) Parents Health: Employee leaving job if parents need help from child.
3) Other field interest : Sometimes employee not ready to continue job in same filed.
4) Husband wife working in different locations : if Husband wife are working of different locations then any one might change job.

Basic Vector Operations in c++

In our last article we learn about What is vector in C++ ? and How it is working ? So in this article we will try to explain different Vector Operations.

Different ways to Initialize a vector in c++


std::vector provides a constructor that accepts the size as an argument. It initialize the vector using default copy constructor. 

Example :
 
// Initialize vector with 3 integers
// Default value of all 3 ints will be 0.
std::vector<int> vecOfInts(3);
 
for(int x : vecOfInts)
 std::cout<<x <<std::endl;

Output:
0
0
0

Initialize a vector by filling similar copy of an element.

It's possible using overloaded constructor, it accepts the size of vector and an element as an argument, then it initializes the vector with second parameter value.

Example :

// Initialize vector to 3 string objects with value "Hi"
std::vector<std::string> vecOfStr(3, "Hi");
 
for(std::string str : vecOfStr)
 std::cout<<str<<std::endl;
Output:
Hi
Hi
Hi

Initialize a vector with an array

Vector provides an overloaded constructor, it accepts array pointer and array size as arguments.

Example:
// Create an array of string objects
std::string arr[] = {"first", "sec", "third"};

// Initialize vector with a string array
std::vector<std::string> vecOfStr(arr, arr + sizeof(arr)/sizeof(std::string));

for(std::string str : vecOfStr)
 std::cout<<str<<std::endl;
Output:

first
sec
third

Initialize a vector with std::list

Vector provides an overloaded constructor, it accepts list start pointer (iterator) and end pointer (iterator).

Example:
// Create an std::list of 3 string objects
std::list<std::string> listOfStr;
listOfStr.push_back("first");
listOfStr.push_back("sec");
listOfStr.push_back("third");


// Initialize a vector with std::list
std::vector<std::string> vecOfStr(listOfStr.begin(), listOfStr.end());

for(std::string str : vecOfStr)
  std::cout<<str<<std::endl;
Output:

first
sec
third

Initializing a vector with an other vector

Vector provides an copy constructor, it accepts other vector as arguments.

Example:
std::vector<std::string> vecOfStr;
vecOfStr.push_back("first");
vecOfStr.push_back("sec");
vecOfStr.push_back("third");

// Initialize a vector with other string object
std::vector<std::string> vecOfStr3(vecOfStr);

for(std::string str : vecOfStr3)
  std::cout<<str<<std::endl;
Output:

first
sec
third

Member Functions of Vector


push_backpush_back() is used for inserting an element at the end of the vector.

Example:
#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<int>  v;
  v.push_back(1);  //insert 1 at the back of v
  v.push_back(2);  //insert 2 at the back of v
  v.push_back(4);  //insert 3 at the back of v

  for(vector<int>::iterator i = v.begin(); i != v.end(); i++) {
   cout << *i <<" ";   // for printing the vector
  }
}
Output:
1 2 4

insertinsert(iterator, element) method inserts the element in vector before the position pointed by iterator

Example:
#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<int>  v;
  v.push_back(1);  //insert 1 at the back of v
  v.push_back(2);  //insert 2 at the back of v
  v.push_back(4);  //insert 3 at the back of v
  
  vector<int>::iterator i = v.begin();
  v.insert(i,3);
  for(vector<int>::iterator i = v.begin(); i != v.end(); i++) {
   cout << *i <<" ";   // for printing the vector
  }
}
Output:
3 1 2 4

Also insert function is overloaded by third argument count. This count parameter defines how many times the element is to be inserted before the pointed position.

Suppose v.insert(i,2,3); means insert 3 twice, Second parameter as count and third parameter as number which we want to insert.

Also possible to insert other vector elements using below call :
v.insert(i, v2.begin(), v2.end());
Above code will insert the elements from v2.begin() to v2.end() before index pointed by i.

pop_back : used to remove the last element from the vector.

Example:
#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<int> v1 {10,20,30,40};
  
  v1.pop_back();  

  vector<int>::iterator it;
  
  for(it = v.begin(); it != v.end(); it++) {
      cout << *it <<" ";   // for printing the vector
  }
}
Output:
10 20 30

eraseerase(itr_pos) removes the element pointed by the iterator itr_pos. erase method can also be overloaded with an extra iterator specifying the end point of the range to be removed, i.e erase(itr_start, itr_end).

Example:
#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vecto<int>v1 {10,20,30,40};
  vector<int>iterator:: it = v.begin();
  
  v.erase(it);   //removes first element from the vector

  v.erase(v1.begin(), v1.end() - 2 )  
  /*removes all the elements except last two */

  for(it = v.begin(); it != v.end(); it++) {
      cout << *it <<" ";   // for printing the vector
  }
}
Output:
30 40

resize: resize(size_type n, value_type value) method resizes the vector to n elements. If the current size of the vector is greater than n then the trailing elements are removed from the vector and if the current size is smaller than n than extra value elements are inserted at the back of the vector.

For example, If the size of the vector is 4 right now, with elements {10, 20, 30, 40} and we use resize method to resize it to size 5. Then by default a fifth element with value 0 will be inserted in the vector. We can specify the data to not be zero, by explicitly mentioning it as the value while calling the resize method.

swap:  It used interchanges value of two vectors.

If we have two vectors vec1 and vec2 and we want to swap the elements inside them, you just need to call vec1.swap(vec2), this will swap the values of the two vectors.

clear: clear() moves all the elements from the vector but do not delete the vector.

size: size() returns the size of the vector.

capacity: capacity() returns actual size allocated for vector.

at: at(i) returns the element at ith index in the vector.

front: front() returns the element at the front of the vector (i.e. leftmost element).

back: back() returns the element at the back of the vector (i.e. rightmost element).


Vector in C++

Vector is a template based container, some behavior like a Dynamic Array. It can expands its memory at run time and uses contiguous memory location to store elements just like Array. It stores any type of element in vector by specifying the type as template argument.

Lets see an example,
#include <vector>
int main()
{
   // This is a vector of int
    std::vector<int> vecOfInts;
 
    // While Adding it automatically adjusts it's size
    for(int i = 0; i < 10; i++)
        vecOfInts.push_back(i);
 
    std::vector<int>::iterator it = vecOfInts.begin();
    while(it != vecOfInts.end())
    {
        std::cout<<*it<<" , ";
        it++;
    }
    for(int i = 0; i < vecOfInts.size(); i++)
        std::cout<<vecOfInts[i]<<" , ";
    std::cout<<std::endl;
 
    return 0;
}

Important Points about std::vector : 


  1. Ordered Collection: In std::vector elements order will remain same in which they are inserted.
  2. Provides random access: Operator [] provides random access just like arrays. 
  3. Performance: It Performs better if insertion and deletion is in end only. It gives worst performance if insertion/deletion is at middle or at starting of vector. 
  4. Contains Copy: It always stores copy of the object not the same reference. So, if you are adding objects of user defined classes the you should define copy constructor and assignment operator in you class.

How does std::vector works internally ?


std::vector allocates a memory on heap & store all its elements in contiguous memory location. But what if memory it allocated initially is completely filled ?

First discuss about std::vector size and  std::vector capacity, Vector size means how many elements are present in vector and vector capacity means how much  continuous memory is allocated in heap for this vector, So vector capacity always greater than or equal to vector size. 

For example, let’s create a vector of integer  i.e. std::vector. Now suppose it’s initial capacity is to store 20 elements, but in our application we want to store 25 elements in it. Then what will happen when we will insert 21st element?

When std::vector’s internal memory (Capacity) completely finishes then it increases the size of its memory, by performs following steps,
  1. It will allocate a bigger chunk of memory on heap i.e. almost double the size of previously vector capacity.
  2. Then it copies all the elements from old memory location to new one. In case our elements are user defined objects then their copy constructor will be called. It makes this step quite heavy in terms of speed and performance.
  3. Then after successful copying it deletes the old memory.
Let;s check std::vector capacity and size with example :

#include <iostream>
#include <vector>
using namespace std;

struct Sample
{
    Sample(){}
    Sample(const Sample & obj)
    {
        std::cout<<"Sample copy constructor"<<std::endl;
    }
};
int main()
{
    std::vector<Sample> vecOfInts;
 
    std::cout<<"Capacity :: "<<vecOfInts.capacity()<<std::endl;
    std::cout<<"Size :: "<<vecOfInts.size()<<std::endl;
    int capcity = vecOfInts.capacity();
    for(int i = 0; i < capcity + 1; i++)
        vecOfInts.push_back(Samplstd::vector e());
 
    std::cout<<"Capacity :: "<<vecOfInts.capacity()<<std::endl;
        std::cout<<"Size :: "<<vecOfInts.size()<<std::endl;
 
    for(int i = 0; i < capcity + 1; i++)
            vecOfInts.push_back(Sample());
 
    std::cout<<"Capacity :: "<<vecOfInts.capacity()<<std::endl;
    std::cout<<"Size :: "<<vecOfInts.size()<<std::endl;
 
    return 0;
}


How to use vector efficiently in C++?

  1. Vector will be more efficient if elements are inserted or removed from the back-end only.

    As, vector internally stores all the elements in consecutive memory location. Therefore, if an element is added in middle, then vector right shifts all the right side elements of that location by 1. Also, if elements were user defined objects then copy constructors for all those elements are called.

    Similarly If element is erased from the middle, then vector left shifts all the right side elements of that location by 1. Also, if elements were user defined objects then copy constructors for all those elements are called.

    But if elements are inserted or deleted from the back-end only then this costly shifting will not happen.

  2.  Set the storage of vector initially using reserve() member function.

    As vector is a kind of container in which user can store unlimited elements. Internally it allocates storage to store the elements but during insertion if new memory requirement surpasses the current capacity then it allocates a bigger chunk of storage and copies all the existing elements there. It’s a huge burden for application because if elements in vector are user defined objects then in every new movement to new storage location copy constructor of elements will be called.

    We can avoid this if in our application by reserving the vector capacity initially by calling reserve() function. This reserve() function requests the vector capacity to be at least enough to contain n elements. It only increases the vector’s capacity, size remains same.

  3. Instead of adding single element in multiple calls, large set of elements is added in single call

    Adding single element can cause,
    1. Shifting of some elements in vector
    2. Allocation of new memory and movement of all elements on new location
    If we add a single element multiple times than all the above things can happen multiple times. Whereas, if we insert elements in together i.e. in a set than this shifting and copying can happen only once. vector can check if it has the capacity to store n elements or not or it needs to shift some elements by n location.