In this tutorial, we will delve into two fundamental container types in the C++ Standard Template Library (STL): std::list and std::forward_list. These containers are part of the sequence container family and provide dynamic arrays that can grow or shrink as needed. The primary difference between them is their underlying data structure: std::list uses a doubly linked list, while std::forward_list uses a singly linked list. Understanding these differences will help you choose the right container for your specific needs.
A std::list is a sequence container that allows constant time insertions and deletions of elements. It provides bidirectional iterators, meaning you can traverse the list in both forward and backward directions. This makes it highly efficient for operations that require frequent modifications to the middle of the list.
Here are some basic operations you can perform on a std::list:
1#include <iostream>2#include <list>34int main() {5std::list<int> myList = {1, 2, 3};67// Insert an element at the end8myList.push_back(4);910// Insert an element at the beginning11myList.push_front(0);1213// Remove the last element14myList.pop_back();1516// Remove the first element17myList.pop_front();1819// Print all elements20for (int num : myList) {21std::cout << num << " ";22}2324return 0;25}
1 2 3
1#include <iostream>2#include <list>3#include <algorithm>45int main() {6std::list<int> myList1 = {3, 2, 1};7std::list<int> myList2 = {6, 5, 4};89// Splice elements from myList1 to myList210myList1.splice(myList1.begin(), myList2);1112// Sort the list13myList1.sort();1415// Remove duplicates16myList1.unique();1718// Print all elements19for (int num : myList1) {20std::cout << num << " ";21}2223return 0;24}
1 2 3 4 5 6
A std::forward_list is a sequence container that allows constant time insertions and deletions of elements. It provides only forward iterators, meaning you can traverse the list in one direction from beginning to end. This makes it more memory-efficient than std::list because each node contains only one pointer instead of two.
Here are some basic operations you can perform on a std::forward_list:
1#include <iostream>2#include <forward_list>34int main() {5std::forward_list<int> myList = {1, 2, 3};67// Insert an element at the end8myList.push_front(4);910// Insert an element at the beginning11myList.push_front(0);1213// Remove the first element14myList.pop_front();1516// Print all elements17for (int num : myList) {18std::cout << num << " ";19}2021return 0;22}
1 2 3 4
1#include <iostream>2#include <forward_list>3#include <algorithm>45int main() {6std::forward_list<int> myList1 = {3, 2, 1};7std::forward_list<int> myList2 = {6, 5, 4};89// Splice elements from myList1 to myList210myList1.splice_after(myList1.before_begin(), myList2);1112// Sort the list13myList1.sort();1415// Remove duplicates16myList1.unique();1718// Print all elements19for (int num : myList1) {20std::cout << num << " ";21}2223return 0;24}
1 2 3 4 5 6
| Feature | std::list | std::forward_list |
|---|---|---|
| Data Structure | Doubly Linked List | Singly Linked List |
| Iterators | Bidirectional | Forward |
| Memory Usage | More (each node has 2 pointers) | Less (each node has 1 pointer) |
| Insertion/Deletion | Constant time | Constant time |
| Reverse Traversal | Supported | Not supported |
Tip
std::list when you need bidirectional traversal and frequent modifications to the middle of the list. Use std::forward_list when memory efficiency is a priority and reverse traversal is not required.Let's create a practical example that demonstrates the use of both std::list and std::forward_list. We'll implement a simple program that merges two lists, sorts them, removes duplicates, and prints the results.
1#include <iostream>2#include <list>3#include <forward_list>4#include <algorithm>56int main() {7std::list<int> list1 = {3, 2, 1};8std::list<int> list2 = {6, 5, 4};910// Merge lists11list1.merge(list2);1213// Sort the list14list1.sort();1516// Remove duplicates17list1.unique();1819// Print all elements20std::cout << "Merged and sorted list: ";21for (int num : list1) {22std::cout << num << " ";23}24std::cout << std::endl;2526std::forward_list<int> forwardList1 = {3, 2, 1};27std::forward_list<int> forwardList2 = {6, 5, 4};2829// Merge lists30forwardList1.merge(forwardList2);3132// Sort the list33forwardList1.sort();3435// Remove duplicates36forwardList1.unique();3738// Print all elements39std::cout << "Merged and sorted forward list: ";40for (int num : forwardList1) {41std::cout << num << " ";42}43std::cout << std::endl;4445return 0;46}
Merged and sorted list: 1 2 3 4 5 6 Merged and sorted forward list: 1 2 3 4 5 6
std::list when you need bidirectional access and frequent modifications to the middle of the list.std::forward_list when memory efficiency is critical and reverse traversal is not required.In the next tutorial, we will explore another sequence container called std::deque, which stands for "double-ended queue." It provides dynamic arrays that can grow or shrink from both ends. This container is highly efficient for operations that require frequent insertions and deletions at both ends of the list.
Stay tuned for more advanced topics in C++ STL!