Class and Objects
A class is a set of variables and functions that work together, where the variables do not have values assigned. When values are assigned to the variables, a class becomes an object. Different values given to the same class result in different objects; that is, different objects can be of the same class but have different values. Creating an object from a class is also known as instantiating the object.
The term vector describes a class. An object created from a vector has a name that is chosen by the programmer.
A function that belongs to a class is needed to instantiate an object from the class. In C++, that function has the same name as the name of the class. Different objects created (instantiated) from the class have distinct names given to each of them by the programmer.
Creating an object from a class means constructing the object; it also means instantiating the object.
The Vector Class
The vector class has already been defined and is in the library. To use the vector class, a programmer must include the vector header in the file with the following preprocessing directive:
Once the header is included, all the vector features (data members and member functions) become accessible. To use the count object to output data to the terminal (console), the object header must also be included. To write a program with the vector, as a minimum, the following headers must be included:
#include <vector>
Instantiating a Vector
Above is the declaration of an array with the name “foo” and the number of elements “10.” This is an array of integers. The declaration of a vector is similar. For a vector, the number of elements is optional, since the vector length can increase or decrease.
At this point in the program, the vector class has already been defined in the library, and the header has been included. The vector can be instantiated as follows:
Here, the vector is of the special constructor function. The type of data the vector will hold is “int,” in angle brackets. The term “vtr” is the name chosen by the programmer for the vector. Finally, “8,” in parentheses, is the tentative number of integers the vector will have.
The term “std” stands for standard namespace. This term must be followed by a double colon, in this context. Anybody can write their own vector class library and use it. However, C++ already has a standard library with standard names, including “vector.” To use a standard name, the standard name must be preceded by std:: . To avoid typing std:: each time in the program for a standard name, the program file can start as follows:
#include <vector>
using namespace std;
Overloading a Function
When two or more different function signatures have the same name, that name is said to be overloaded. When one function is called, the number and type of arguments determine which function is executed.
Constructing a Vector
Constructing a vector means instantiating (creating) a vector object. The constructor function is overloaded as follows:
vector <T> name
This creates a vector of length zero and type “T.” The following statement creates a vector of zero length of the type “float” with the name “vtr:”
vector <T> name (n)
This creates a vector with n elements of type “T.” A statement for this vector with four float elements is as follows:
vector <T> name (n, t)
This creates a vector of n elements initialized to the value t. The following statement creates a vector of 5 elements, where each element has the value 3.4:
Constructing with Initialization
A vector can be constructed (created) and initialized at the same time, in one of the following two ways:
Or
Note that there are no parentheses just after the object name. Parentheses used just after the object name should have the initializer list, as follows:
A vector can be constructed and initialized later with the initializer list. In this case, the parentheses will not be used:
vtr = {1.1, 2.2, 3.3, 4.4};
vector <T> V2 (V1)
This is a copy constructor. It creates a vector V2 as a copy of the vector V1. The following code illustrates this:
vector <float> vtr2(vtr1);
Assigning a Vector during Construction
During construction, an empty vector can be created while another one is assigned to it, as follows:
vector <float> vtr2 =vtr1;
The second statement is equivalent to:
const Vector
A const vector is a vector whose elements cannot be changed. The values in this vector are read-only. When created, the vector appears as follows:
In this vector type, no element can be added or removed. Moreover, no value can be changed.
Constructing with Iterator
A template provides a generic representation for a data type. An iterator provides a generic representation of scanning through the values of a container. The syntax to create a vector with an iterator is as follows:
vector(InputIterator first, InputIterator last,const Allocator& = Allocator());
This constructs a vector for the range [first, last) using the specified allocator, which will be discussed later in this article.
Destroying a Vector
To destroy a vector, simply allow it to go out of scope and destroy is handled automatically.
Vector Capacity
size_type capacity() const noexcept
The total number of elements the vector can hold without requiring reallocation is returned by the capacity member function. A code segment for this is as follows:
int num = vtr.capacity();
cout << num << '\n';
The output is 4.
reserve(n)
Memory space is not always freely available. Extra space can be reserved in advance. Consider the following code segment:
vtr.reserve(6);
cout << vtr.capacity() << '\n';
The output is 6. So, the extra space reserved is 6 – 4 = 2 elements. The function returns void.
size() const noexcept
This returns the number of elements in the vector. The following code illustrates this function:
float sz = vtr.size();
cout << sz << '\n';
The output is 4.
shrink_to_fit()
After giving extra capacity to a vector with the reserve() function, the vector can be sized down to fit to its original size. The following code illustrates this:
vtr.reserve(6);
vtr.shrink_to_fit();
int sz = vtr.size();
cout << sz << '\n';
The output is 4 and not 6. The function returns void.
resize(sz), resize(sz,c)
This resizes the vector. If the new size is smaller than the old size, then the elements towards the end are erased. If the new size is longer, then some default value is added towards the end. To have a particular value added, use the resize() function with two arguments. The following code segment illustrates the use of these two functions:
vtr1.resize(2);
cout << "New size of vtr1: " << vtr1.size() << '\n';
vector <float> vtr2{1.1, 2.2};
vtr2.resize(4, 8.8);
cout << "vtr2: "<< vtr2[0] <<" "<< vtr2[1] <<"
"<< vtr2[2] <<" "<< vtr2[3] << '\n';
The output is the following:
vtr2: 1.1 2.2 8.8 8.8
The functions return void.
empty() const noexcept
This function returns 1 for true if there are no elements in the vector and 0 for false if the vector is empty. If a vector has 4 locations for a particular type of data, such as float, without any float value, then that vector is not empty. The following code illustrates this:
cout << vtr.empty() << '\n';
vector <float> vt(4);
cout << vt.empty() << '\n';
vector <float> v(4,3.5);
cout << v.empty() << '\n';
The output is the following:
0
0
Vector Element Access
A vector can be sub-scripted (indexed) like an array. Index counting begins from zero.
vectorName[i]
The operation “vectorName[i]” returns a reference to the element at the ith index of the vector. The following code outputs 3.3 for the above vector:
float fl = vtr[2];
cout << fl << '\n';
vectorName[i] const
The operation “vectorName[i] const” is executed instead of “vectorName[i]” when the vector is a constant vector. This operation is used in the following code:
float fl = vtr[2];
cout << fl << '\n';
The expression returns a constant reference to the ith element of the vector.
Assigning a Value with Subscript
A value can be assigned to a non-constant vector, as follows:
vtr[2] = 8.8;
cout << vtr[2] << '\n';
The output is 8.8.
vectorName.at(i)
“vectorName.at(i)” is like “vectorName[i],” but “vectorName.at(i)” is more reliable. The following code shows how this vector should be used:
float fl = vtr.at(2);
cout << fl << '\n';
at() is a vector member function.
vectorName.at(i) const
“vectorName.at(i) const” is like “vectorName[i] const,” but “vectorName.at(i) const” is more reliable. “vectorName.at(i) const” is executed instead of “vectorName.at(i)” when the vector is a constant vector. This vector is used in the following code:
float fl = vtr.at(2);
cout << fl << '\n';
at() const is a vector member function.
Assigning a Value with the at() Function
A value can be assigned to a non-constant vector with the at() function, as follows:
vtr.at(2) = 8.8;
cout << vtr[2] << '\n';
The output is 8.8.
Problem with Sub-Scripting
The problem with sub-scripting (indexing) is that if the index is out of range, zero may be returned or an error may be issued at run-time.
front()
This returns a reference to the first element of the vector without removing the element. The output of the following code is 1.1.
float fl = vtr.front();
cout << fl << '\n';
The element is not removed from the vector.
front() const
When the vector construction is preceded by const, the expression “front() const” is executed instead of “front().” This is used in the following code:
float fl = vtr.front();
cout << fl << '\n';
A constant reference is returned. The element is not removed from the vector.
back()
This returns a reference to the last element of the vector without removing the element. The output of the following code is 4.4.
float fl = vtr.back();
cout << fl << '\n';
back() const
When the vector construction is preceded by const, the expression “back() const” is executed instead of “back().” This is used in the following code:
float fl = vtr.back();
cout << fl << '\n';
A constant reference is returned. The element is not removed from the vector.
Vector Data Access
data() noexcept; data() const noexcept;
Either of these returns a pointer such that [data(), data() + size()) is a valid range.
This will be covered in greater detail later in the article.
Returning Iterators and the Vector
An iterator is like a pointer but has more functionality than a pointer.
begin() noexcept
Returns an iterator that points to the first element of the vector, as in the following code segment:
vector<float>::iterator iter = vtr.begin();
cout << *iter << '\n';
The output is 1.1. Note that the declaration that receives the iterator has been declared. The iterator is dereferenced in a return expression to obtain the value in the same way that a pointer is dereferenced.
begin() const noexcept;
Returns an iterator that points to the first element of the vector. When the vector construction is preceded by const, the expression “begin() const” is executed instead of “begin().” Under this condition, the corresponding element in the vector cannot be modified. This is used in the following code:
vector<float>::const_iterator iter = vtr.begin();
cout << *iter << '\n';
The output is 1.1. Note that “const_iterator” has been used this time instead of just “iterator” to receive the returned iterator.
end() noexcept
Returns an iterator that points immediately beyond the last element of the vector. Consider the following code segment:
vector<float>::iterator iter = vtr.end();
cout << *iter << '\n';
The output is 0, which is meaningless, as there is no concrete element beyond the last element.
end() const noexcept
Returns an iterator that points immediately beyond the last element of the vector. When the vector construction is preceded by “const,” the expression “end() const” is executed instead of “end().” Consider the following code segment:
vector<float>::const_iterator iter = vtr.end();
cout << *iter << '\n';
The output is 0. Note that “const_iterator” has been used this time instead of just “iterator” to receive the returned iterator.
Reverse Iteration
It is possible to have an iterator that iterates from the end to just before the first element.
rbegin() noexcept
Returns an iterator that points to the last element of the vector, as in the following code segment:
vector<float>::reverse_iterator rIter = vtr.rbegin();
cout << *rIter << '\n';
The output is 4.4.
Note that the declaration that receives the reverse iterator has been declared. The iterator is dereferenced in a return expression to obtain the value in the same way that a pointer is dereferenced.
rbegin() const noexcept;
Returns an iterator that points to the last element of the vector. When the vector construction is preceded by “const,” the expression “rbegin() const” is executed instead of “rbegin().” Under this condition, the corresponding element in the vector cannot be modified. This feature is used in the following code:
vector<float>::const_reverse_iterator rIter = vtr.rbegin();
cout << *rIter << '\n';
The output is 4.4.
Note that the const_reverse_iterator has been used this time, instead of just the reverse_iterator, to receive the returned iterator.
rend() noexcept
Returns an iterator that points just before the first element of the vector. Consider the following code segment:
vector<float>::reverse_iterator rIter = vtr.rend();
cout << *rIter << '\n';
The output is 0, which is meaningless, as there is no concrete element just before the first element.
rend() const noexcept
Returns an iterator that points just before the first element of the vector. When the vector construction is preceded by “const,” the expression “rend() const” is executed instead of “rend().” Consider the following code segment:
vector<float>::const_reverse_iterator rIter = vtr.rend();
cout << *rIter << '\n';
The output is 0.
Note that the const_reverse_iterator has been used this time, instead of just the reverse_iterator, to receive the returned iterator.
Vector Modifiers
A modifier that modifies the vector can take or return an iterator.
a.emplace(p, args)
Inserts an object of type T constructed with std::forward<Args>(args)… before p.
insert(iteratorPosition, value)
Inserts a copy of the value at the iterator position of the vector. Returns the iterator (position) in the vector where the copy has been placed. The following code shows where the value has been placed:
vector<int>::iterator iter = vtr.begin();
++iter;
++iter;
vtr.insert(iter, 25);
cout << vtr[1] << ' ' << vtr[2]<< '
' << vtr[3] << '\n';
The output is: 20 25 30.
Note that the iterator was advanced (incremented) just like a pointer.
An initializer list can also be inserted, as the following code illustrates:
vector<int>::iterator iter = vtr.begin();
++iter;
++iter;
vtr.insert(iter, {25, 28});
cout << vtr[1] << ' ' << vtr[2]<< '
' << vtr[3]<< ' ' << vtr[4] << '\n';
The output is: 20 25 28 30.
erase(position)
Removes an element at the position pointed to by the iterator, then returns the iterator position. The following code illustrates this:
vector<int>::iterator iter = vtr.begin();
++iter;
++iter;
vtr.erase(iter);
cout << vtr[0] << ' ' << vtr[1] << '
' << vtr[2]<< '\n';
The output is: 10 20 40
push_back(t), push_back(rv)
Used to add a single element at the end of the vector. Use push_back(t) as follows:
vtr.push_back(5.5);
float fl = vtr[4];
cout << fl << '\n';
The output is 5.5.
pop_back()
Removes the last element without returning it. The size of the vector is reduced by 1. The following code illustrates this:
vtr.pop_back();
float sz = vtr.size();
cout << sz << '\n';
The output is 3.
a.swap(b)
Two vectors can be swapped, as illustrated in the following code segment:
vector <float> vtr2{10, 20};
vtr1.swap(vtr2);
cout << "vtr1: "<< vtr1[0] <<" "<< vtr1[1] <<"
"<< vtr1[2] <<" "<< vtr1[3] << '\n';
cout << "vtr2: "<< vtr2[0] <<" "<< vtr2[1] <<"
"<< vtr2[2] <<" "<< vtr2[3] << '\n';
The output is:
vtr2: 1.1 2.2 3.3 4.4
Note that the length of a vector is increased, if necessary. Also, values that did not have replacements are replaced by some default value.
clear()
Removes all elements from the vector, as the following code segment illustrates:
vtr.clear();
cout << vtr.size() << '\n';
The output is 0.
Equality and Relational Operators for Vectors
The == Operator
Returns 1 for true if the two vectors have the same size and the corresponding elements are equal; otherwise, it returns 0 for false. For example:
vector <int> V{4, 5, 6};
bool bl = U==V;
cout << bl << '\n';
The output is 0.
The != Operator
Returns 1 for true if the two vectors do not have the same size and/or the corresponding elements are not equal; otherwise, it returns 0 for false. For example:
vector <int> V{4, 5, 6};
bool bl = U!=V;
cout << bl << '\n';
The output is 1.
The < Operator
Returns 1 for true if the first vector is the initial subset of the second vector, with the elements of the two equal portions being the same and in the same order. If both vectors are of the same size and moving from left to right and an element is encountered in the first vector that is less than the corresponding element in the second vector, then 1 will still be returned. Otherwise, 0 for false is returned. For example:
vector <int> V{3, 2, 1};
bool bl = U<V;
cout << bl << '\n';
The output is 1. < does not include the case when the size and order are the same.
The > Operator
Returns !(U < V), where U is the first vector and V is the second vector, according to the above definitions.
The <= Operator
Returns U <= V, where U is the first vector and V is the second vector, according to the above definitions.
The >= Operator
Returns !(U <= V), where U is the first vector and V is the second vector, according to the above definitions.
Conclusion
A vector is an example of a sequence container. A vector is a “better” form of the ordinary array and is instantiated from a class. Vectors have methods that are classified under: construction and assignment, capacity, element access, data access, iterators, modifiers, and numerical overloaded operators.
There are other sequence containers, called list, forward_list, and array. If the task involves frequent insertions and deletions in the middle of the sequence, then a list or forward_list should be used. If the task involves frequent insertions and deletions at the beginning or end of the sequence, then a deque should be used. And so, vectors should be used only when these kinds of operations are not important.