Kompleksowy przegląd std::stack w C++ – implementacja LIFO, metody i zastosowania
Stos (std::stack) to fundamentalna struktura danych w bibliotece standardowej C++, realizująca zasadę LIFO (Last-In, First-Out). Jako adapter kontenera, std::stack abstrahuje operacje na bazowym kontenerze (domyślnie std::deque), oferując minimalny interfejs do efektywnego zarządzania danymi w scenariuszach wymagających sekwencyjnego przetwarzania w odwróconej kolejności.
Implementacja LIFO
Mechanizm LIFO w std::stack polega na ograniczeniu operacji do jednego końca struktury danych:
- Wstawianie (push) – nowe elementy dodawane są wyłącznie na wierzchołek stosu;
- Usuwanie (pop) – elementy usuwane są wyłącznie z wierzchołka stosu;
- Dostęp (top) – jedynym elementem dostępnym bez modyfikacji struktury jest element wierzchołkowy.
Ta semantyka odwzorowuje zachowanie stosu fizycznego (np. stosu talerzy), gdzie ostatnio dodany element jest pierwszym do usunięcia.
Metody i ich zastosowania
Interfejs std::stack składa się z sześciu kluczowych metod:
push(const Type& val)– dodaje element na wierzchołek stosu poprzez kopię lub przeniesienie;
std::stack<int> s;
s.push(42); // Wstawia 42 na wierzchołekemplace(Args&&... args)– konstruuje element w miejscu na wierzchołku stosu, unikając kopiowania;s.emplace(100); // Efektywniejsze niż push dla obiektów złożonychpop()– usuwa element z wierzchołka bez zwracania wartości. Wymaga wcześniejszego odczytutop();
s.pop(); // Usuwa wierzchołektop()– zwraca referencję do elementu na wierzchołku, pozwala na modyfikację wartości;s.top() = 99; // Modyfikuje wierzchołeksize()– zwraca liczbę elementów przechowywanych na stosie;cout << s.size(); // Np. 3 elementyempty()– sprawdza obecność elementów na stosie. Zwracatruedla stosu pustego;if (s.empty()) { /* ... */ } // Logika dla pustego stosu
Zaawansowane techniki
- Wybór kontenera bazowego – domyślnie
std::stackwykorzystujestd::deque, lecz możliwe jest zastosowanie alternatywnych kontenerów spełniających wymagania SequenceContainer (np.std::list,std::vector);std::stack<int, std::list<int>> customStack; // Stos z listą jako kontenerem - Optymalizacje – emplace vs push:
emplaceunika tworzenia tymczasowych obiektów, redukując narzuty pamięciowe, swap(): zamienia zawartości dwóch stosów w czasie stałym O(1) poprzez wymianę wskaźników kontenerów bazowych.
Przykład praktyczny: odwracanie sekwencji
using namespace std;
vector<int> reverseVector(const vector<int>& input) {
stack<int> s;
for (int elem : input) s.push(elem); // Wstawianie elementów
vector<int> reversed;
while (!s.empty()) {
reversed.push_back(s.top()); // Pierwszy element: ostatni dodany
s.pop();
}
return reversed; // Zwraca odwrócony wektor
}
Ograniczenia i ostrzeżenia
- Brak iteratorów –
std::stacknie udostępnia iteratorów, uniemożliwiając przeglądanie elementów bez modyfikacji struktury; - Bezpieczeństwo wyjątków – metoda
pop()nie zwraca wartości, by zagwarantować silną gwarancję wyjątków. Odczyt wymaga oddzielnego wywołaniatop().
Zastosowania w algorytmach
- Przeszukiwanie w głąb (DFS) – stos przechowuje wierzchołki do odwiedzenia;
- Ewaluacja wyrażeń – przetwarzanie notacji odwrotnej polskiej;
- Cofanie operacji – historia działania programu (np. mechanizm undo).
Podsumowanie
std::stack oferuje minimalistyczny, lecz potężny interfejs dla struktur LIFO, będąc niezastąpionym narzędziem w scenariuszach wymagających sekwencyjnego przetwarzania danych w odwróconej kolejności. Elastyczność w doborze kontenera bazowego oraz wydajność operacji emplace i swap czynią ją kluczowym komponentem biblioteki standardowej C++.
