Coming Soon
This lesson is currently being developed
Introduction to std::vector and list constructors
Master dynamic arrays with the vector container.
What to Expect
Comprehensive explanations with practical examples
Interactive coding exercises to practice concepts
Knowledge quiz to test your understanding
Step-by-step guidance for beginners
Development Status
Content is being carefully crafted to provide the best learning experience
Preview
Early Preview Content
This content is still being developed and may change before publication.
16.2 — Introduction to std::vector and list constructors
In this lesson, you'll learn how to create and initialize std::vector objects using various constructors. You'll discover the flexibility that std::vector provides over C-style arrays and understand the different ways to set up vectors for your programs.
What is std::vector?
std::vector is a sequence container that represents a dynamic array. Unlike C-style arrays, std::vector can change its size during runtime, automatically managing memory allocation and deallocation.
Think of std::vector like a smart, resizable container:
- It starts empty or with initial values you specify
- It can grow to accommodate new elements
- It can shrink when elements are removed
- It always knows its current size
- It provides safe access to elements
Including std::vector
To use std::vector, you must include the <vector>
header:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers; // Now you can use std::vector
return 0;
}
Basic std::vector syntax
The basic syntax for declaring a std::vector is:
std::vector<element_type> vector_name;
For example:
std::vector<int> integers; // Vector of integers
std::vector<double> decimals; // Vector of double-precision numbers
std::vector<char> characters; // Vector of characters
std::vector<bool> flags; // Vector of boolean values
std::vector constructors
std::vector provides several constructors to create vectors in different ways. Let's explore each one:
1. Default constructor (empty vector)
Creates an empty vector with no elements:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers; // Empty vector
std::cout << "Size: " << numbers.size() << std::endl;
std::cout << "Empty: " << (numbers.empty() ? "Yes" : "No") << std::endl;
return 0;
}
Output:
Size: 0
Empty: Yes
2. Fill constructor (size with default value)
Creates a vector with a specified number of elements, each initialized to the default value:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers(5); // 5 integers, each initialized to 0
std::cout << "Size: " << numbers.size() << std::endl;
std::cout << "Elements: ";
for (int i = 0; i < numbers.size(); ++i)
{
std::cout << numbers[i] << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Size: 5
Elements: 0 0 0 0 0
3. Fill constructor (size with specific value)
Creates a vector with a specified number of elements, each initialized to a specific value:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> scores(7, 100); // 7 integers, each initialized to 100
std::vector<double> prices(3, 9.99); // 3 doubles, each initialized to 9.99
std::cout << "Scores (" << scores.size() << " elements): ";
for (int score : scores)
{
std::cout << score << " ";
}
std::cout << std::endl;
std::cout << "Prices (" << prices.size() << " elements): ";
for (double price : prices)
{
std::cout << "$" << price << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Scores (7 elements): 100 100 100 100 100 100 100
Prices (3 elements): $9.99 $9.99 $9.99
4. Initializer list constructor
Creates a vector from a list of values (C++11 and later):
#include <vector>
#include <iostream>
int main()
{
std::vector<int> primes = {2, 3, 5, 7, 11, 13};
std::vector<std::string> colors = {"red", "green", "blue"};
std::vector<double> temperatures = {23.5, 25.0, 22.8, 24.1};
std::cout << "Primes: ";
for (int prime : primes)
{
std::cout << prime << " ";
}
std::cout << std::endl;
std::cout << "Colors: ";
for (const std::string& color : colors)
{
std::cout << color << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Primes: 2 3 5 7 11 13
Colors: red green blue
5. Copy constructor
Creates a vector as a copy of another vector:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> original = {1, 4, 9, 16, 25};
std::vector<int> copy(original); // Copy constructor
std::cout << "Original: ";
for (int value : original)
{
std::cout << value << " ";
}
std::cout << std::endl;
std::cout << "Copy: ";
for (int value : copy)
{
std::cout << value << " ";
}
std::cout << std::endl;
// Modify the copy to show they're independent
copy[0] = 999;
std::cout << "After modifying copy:" << std::endl;
std::cout << "Original[0]: " << original[0] << std::endl;
std::cout << "Copy[0]: " << copy[0] << std::endl;
return 0;
}
Output:
Original: 1 4 9 16 25
Copy: 1 4 9 16 25
After modifying copy:
Original[0]: 1
Copy[0]: 999
6. Range constructor
Creates a vector from a range of elements (from another container):
#include <vector>
#include <iostream>
#include <array>
int main()
{
std::array<int, 5> arr = {10, 20, 30, 40, 50};
std::vector<int> vec(arr.begin(), arr.end()); // Range constructor
std::cout << "Array: ";
for (int value : arr)
{
std::cout << value << " ";
}
std::cout << std::endl;
std::cout << "Vector from array: ";
for (int value : vec)
{
std::cout << value << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Array: 10 20 30 40 50
Vector from array: 10 20 30 40 50
Comparing constructors
Here's a comprehensive example showing different construction methods:
#include <vector>
#include <iostream>
int main()
{
// Different ways to create vectors
std::vector<int> empty; // Empty vector
std::vector<int> sized(4); // 4 zeros: [0, 0, 0, 0]
std::vector<int> filled(3, 42); // 3 copies of 42: [42, 42, 42]
std::vector<int> listed = {1, 2, 3, 4, 5}; // From initializer list
std::vector<int> copied(listed); // Copy of 'listed'
std::cout << "Empty size: " << empty.size() << std::endl;
std::cout << "Sized: ";
for (int val : sized) std::cout << val << " ";
std::cout << std::endl;
std::cout << "Filled: ";
for (int val : filled) std::cout << val << " ";
std::cout << std::endl;
std::cout << "Listed: ";
for (int val : listed) std::cout << val << " ";
std::cout << std::endl;
std::cout << "Copied: ";
for (int val : copied) std::cout << val << " ";
std::cout << std::endl;
return 0;
}
Output:
Empty size: 0
Sized: 0 0 0 0
Filled: 42 42 42
Listed: 1 2 3 4 5
Copied: 1 2 3 4 5
List initialization vs direct initialization
There's an important distinction between list initialization and direct initialization:
#include <vector>
#include <iostream>
int main()
{
// These create different vectors!
std::vector<int> v1(3, 5); // Direct init: 3 elements, each with value 5
std::vector<int> v2{3, 5}; // List init: 2 elements with values 3 and 5
std::cout << "v1 (direct): ";
for (int val : v1) std::cout << val << " ";
std::cout << "(size: " << v1.size() << ")" << std::endl;
std::cout << "v2 (list): ";
for (int val : v2) std::cout << val << " ";
std::cout << "(size: " << v2.size() << ")" << std::endl;
return 0;
}
Output:
v1 (direct): 5 5 5 (size: 3)
v2 (list): 3 5 (size: 2)
Working with different element types
std::vector works with any type:
#include <vector>
#include <iostream>
#include <string>
int main()
{
// Vector of strings
std::vector<std::string> names = {"Alice", "Bob", "Charlie"};
// Vector of characters
std::vector<char> vowels = {'a', 'e', 'i', 'o', 'u'};
// Vector of booleans
std::vector<bool> answers = {true, false, true, true};
// Vector of doubles
std::vector<double> measurements(5, 0.0); // 5 zeros
std::cout << "Names: ";
for (const std::string& name : names)
{
std::cout << name << " ";
}
std::cout << std::endl;
std::cout << "Vowels: ";
for (char vowel : vowels)
{
std::cout << vowel << " ";
}
std::cout << std::endl;
std::cout << "Answers: ";
for (bool answer : answers)
{
std::cout << (answer ? "Yes" : "No") << " ";
}
std::cout << std::endl;
return 0;
}
Output:
Names: Alice Bob Charlie
Vowels: a e i o u
Answers: Yes No Yes Yes
Memory and performance considerations
Capacity vs size
std::vector maintains two important properties:
- Size: The number of elements currently stored
- Capacity: The number of elements that can be stored without allocating new memory
#include <vector>
#include <iostream>
int main()
{
std::vector<int> numbers = {1, 2, 3};
std::cout << "Size: " << numbers.size() << std::endl;
std::cout << "Capacity: " << numbers.capacity() << std::endl;
// Add more elements
numbers.push_back(4);
numbers.push_back(5);
numbers.push_back(6);
std::cout << "After adding elements:" << std::endl;
std::cout << "Size: " << numbers.size() << std::endl;
std::cout << "Capacity: " << numbers.capacity() << std::endl;
return 0;
}
Output:
Size: 3
Capacity: 3
After adding elements:
Size: 6
Capacity: 6
Constructor performance
Different constructors have different performance characteristics:
// Fast - no memory allocation needed
std::vector<int> empty;
// Fast - single memory allocation, elements default-initialized
std::vector<int> sized(1000);
// Fast - single memory allocation, elements copy-initialized
std::vector<int> filled(1000, 42);
// Fast - single memory allocation for known size
std::vector<int> listed = {1, 2, 3, 4, 5};
// Moderate - copy existing memory
std::vector<int> copied(other_vector);
Best practices for std::vector construction
1. Use initializer lists when you know the values
// Good - clear and concise
std::vector<std::string> weekdays = {"Mon", "Tue", "Wed", "Thu", "Fri"};
// Less clear
std::vector<std::string> weekdays;
weekdays.push_back("Mon");
weekdays.push_back("Tue");
// ... etc
2. Pre-size when you know the final size
// Good - avoids multiple reallocations
std::vector<int> numbers;
numbers.reserve(1000); // Pre-allocate space
// ... fill with data
// Or use fill constructor if all elements are the same
std::vector<int> zeros(1000, 0);
3. Be careful with parentheses vs braces
std::vector<int> v1(10, 5); // 10 elements, each value 5
std::vector<int> v2{10, 5}; // 2 elements: 10 and 5
4. Use const when the vector won't change
const std::vector<std::string> colors = {"red", "green", "blue"};
// colors cannot be modified
Common mistakes to avoid
1. Forgetting to include <vector>
// Error - missing include
// std::vector<int> numbers; // Won't compile
#include <vector>
std::vector<int> numbers; // OK
2. Confusing constructor syntax
std::vector<int> wrong(5, 10, 15); // Error - too many arguments
std::vector<int> correct(5, 10); // OK - 5 elements, each value 10
3. Assuming capacity equals size
std::vector<int> vec(5);
std::cout << vec.capacity(); // Might be 5, but could be larger
Summary
std::vector provides multiple constructors to create vectors in different ways:
- Default constructor: Creates an empty vector
- Fill constructor: Creates a vector with specified size and values
- Initializer list constructor: Creates a vector from a list of values
- Copy constructor: Creates a copy of another vector
- Range constructor: Creates a vector from a range of elements
Choose the appropriate constructor based on your needs:
- Use initializer lists when you know the exact values
- Use fill constructors when you need many copies of the same value
- Use the default constructor when you'll add elements later
- Use copy constructor when you need an independent copy
In the next lesson, you'll learn about the challenges of working with std::vector's size type and indexing.
Quiz
- What header file must you include to use std::vector?
- What's the difference between
std::vector<int> v(3, 5)
andstd::vector<int> v{3, 5}
? - How do you create a vector with 10 elements, each initialized to zero?
- What's the difference between a vector's size and capacity?
- Which constructor would you use to create a copy of an existing vector?
Practice exercises
Try these exercises to practice std::vector constructors:
-
Constructor comparison: Create vectors using each type of constructor and display their contents and sizes. Create vectors of integers, strings, and doubles.
-
Student grades: Create a vector to store test scores using the fill constructor (assume all students got 85). Then create another vector with specific scores using an initializer list. Display both vectors.
-
Data migration: Create a C-style array with some values, then create a std::vector using the range constructor to copy those values. Compare accessing elements from both.
-
Performance test: Create large vectors using different constructors and measure their creation time (you can use a simple timer or just observe). Try vectors with 100,000 elements using fill constructor vs adding elements one by one.
Explore More Courses
Discover other available courses while this lesson is being prepared.
Browse CoursesLesson Discussion
Share your thoughts and questions