In today's lesson, we'll explore vectors in C++, a versatile and widely used dynamic array data structure. Unlike arrays, std::vector
allows dynamic resizing, making it a powerful tool for scenarios where the size of the dataset can change over time.
The beauty of std::vector
lies in its ability to manage storage automatically, providing efficient access and modification options. By the end of this lesson, you'll be able to create, manipulate, and understand the unique applications of vectors in C++.
A vector, stored as an instance of std::vector
in C++, holds multiple items of the same type and supports dynamic resizing. This makes it more flexible compared to arrays, which have a fixed size. Vectors manage their own memory allocation, and their size can be changed as needed.
Consider this C++ vector declaration as an example:
C++1#include <iostream> 2#include <vector> 3 4int main() { 5 std::vector<std::string> my_vector = {"apple", "banana", "cherry"}; 6 for (const auto& fruit : my_vector) { 7 std::cout << fruit << " "; 8 } 9 // Output: apple banana cherry 10 return 0; 11}
In C++, you can access vector elements using the []
operator or the at
method. Vectors can be modified by adding, removing, or changing elements.
The following is a simple example of inspecting and modifying vectors:
C++1#include <iostream> 2#include <vector> 3 4int main() { 5 std::vector<std::string> my_vector = {"apple", "banana", "cherry"}; 6 7 // Accessing elements 8 std::cout << my_vector[1] << std::endl; // Output: banana 9 std::cout << my_vector.at(2) << std::endl; // Output: cherry 10 11 // Modifying elements 12 my_vector[1] = "blueberry"; // Modifying the second element 13 std::cout << my_vector[1] << std::endl; // Output: blueberry 14 15 // Adding and removing elements 16 my_vector.push_back("durian"); // Adding a new element at the end 17 my_vector.erase(my_vector.begin() + 2); // Removing the third element ("cherry") 18 19 for (const auto& fruit : my_vector) { 20 std::cout << fruit << " "; 21 } 22 // Output: apple blueberry durian 23 24 return 0; 25}
In this example:
- Accessing elements:
my_vector[1]
gets the second element ("banana"
), andmy_vector.at(2)
gets the third element ("cherry"
). - Modifying elements:
my_vector[1] = "blueberry"
changes the second element from"banana"
to"blueberry"
. - Adding and removing elements:
my_vector.push_back("durian")
adds"durian"
at the end.my_vector.erase(my_vector.begin() + 2)
removes the third element ("cherry"
).
Iterators are objects that point to elements within a container (like std::vector
). They allow traversal through the container, similar to pointers, but also provide a level of abstraction and safety. Two common examples of functions returning iterators are begin()
, which returns an iterator to the first element, and end()
, which returns an iterator one past the last element.
In the example, my_vector.begin()
returns an iterator to the first element, and adding 2 to it moves the iterator to the third element, which is then removed by erase()
.
Iterators are fundamental to many operations in C++ containers, offering versatile and efficient ways to manipulate and interact with the elements.
Vectors support several operations such as concatenation, insertion, and resizing.
C++1#include <iostream> 2#include <vector> 3 4int main() { 5 std::vector<std::string> vec1 = {"apple", "banana"}; 6 std::vector<std::string> vec2 = {"cherry", "durian"}; 7 8 // Concatenation: Inserting vec2 elements into vec1 9 vec1.insert(vec1.end(), vec2.begin(), vec2.end()); 10 11 for (const auto& fruit : vec1) { 12 std::cout << fruit << " "; 13 } 14 // Output: apple banana cherry durian 15 16 std::cout << std::endl; 17 18 // Resizing 19 vec1.resize(6, "elderberry"); // Resizing to 6 elements, initializing new elements with "elderberry" 20 21 for (const auto& fruit : vec1) { 22 std::cout << fruit << " "; 23 } 24 // Output: apple banana cherry durian elderberry elderberry 25 26 std::cout << std::endl << (vec1.front() == "apple") << std::endl; // Output: 1 (true) 27 28 return 0; 29}
In the above example:
vec1.insert(vec1.end(), vec2.begin(), vec2.end())
concatenatesvec2
tovec1
. Theinsert
method takes an iterator pointing to the position where elements should be inserted, followed by a range defined by two iterators (vec2.begin()
andvec2.end()
).vec1.resize(6, "elderberry")
resizesvec1
to contain 6 elements. If the new size is greater than the current size, new elements are initialized with"elderberry"
.vec1.front()
returns a reference to the first element of the vector, which in this case is"apple"
. This is useful for accessing the first element directly without needing to use an index, which can make it easier to write more reusable code.
A vector can contain other vectors, resulting in nested vectors. Here's an example of creating a nested vector:
C++1#include <iostream> 2#include <vector> 3 4int main() { 5 std::vector<std::vector<std::string>> nested_vector = {{"apple", "banana"}, {"cherry", "durian"}}; 6 7 for (const auto& sub_vector : nested_vector) { 8 for (const auto& fruit : sub_vector) { 9 std::cout << fruit << " "; 10 } 11 std::cout << std::endl; 12 } 13 // Output: 14 // apple banana 15 // cherry durian 16 17 return 0; 18}
Structured bindings can also simplify vector usage, making it more intuitive to work with.
Excellent job! In this lesson, you've learned what C++ vectors are and how to create, inspect, and operate them. You've also learned some advanced concepts, such as nested vectors.
Going forward, our focus will be on meticulously designed practice exercises that solidify your understanding. Remember, the key to successful learning is practice. Reinvent these examples by breaking them and fixing them again. Let's dive deeper!