9Jak `std::vector` zarządza pojemnością, powiększaniem, realokacją oraz stabilnością iteratorów?
`std::vector` przechowuje elementy w sposób ciągły (contiguously) i śledzi zarówno rozmiar (`size`), jak i pojemność (`capacity`). Rozmiar to liczba skonstruowanych elementów; pojemność to ilość zaalokowanej pamięci dostępnej na elementy przed koniecznością kolejnej alokacji. Gdy dodanie elementów przekroczyłoby pojemność, wektor alokuje większy blok pamięci, zazwyczaj używając zdefiniowanej przez implementację strategii wzrostu geometrycznego, przenosi lub kopiuje istniejące elementy, niszczy stare i zwalnia starą pamięć. Metoda `reserve(n)` zwiększa pojemność bez zmiany rozmiaru, podczas gdy `resize(n)` zmienia rozmiar poprzez konstruowanie lub niszczenie elementów. Realokacja unieważnia wszystkie iteratory, referencje i wskaźniki do elementów; nawet bez realokacji, operacje takie jak `insert` i `erase` mogą unieważnić pozycje w miejscu modyfikacji lub za nim.
Odpowiedz na to pytanie z trenerem AI
10Jakie zasady unieważniania (invalidation rules) należy znać w przypadku kontenerów standardowych o ciągłym ułożeniu w pamięci (contiguous) oraz tych opartych na węzłach (node-based)?
Zasady unieważniania zależą od użytego kontenera oraz samej operacji. Kontenery o ciągłym ułożeniu w pamięci, takie jak `vector` i `string`, mają wysoce wrażliwą stabilność iteratorów/referencji: zwiększenie rozmiaru może wymusić realokację, unieważniając tym samym wszystkie iteratory, referencje i wskaźniki, a operacje wstawiania i usuwania (insert/erase) mogą przesunąć elementy i unieważnić pozycje, począwszy od miejsca zmiany, nawet bez realokacji. Uporządkowane kontenery oparte na węzłach (node-based), takie jak `list`, `map`, `set` i ich warianty typu multi, podczas wstawiania zazwyczaj utrzymują stabilność iteratorów i referencji do istniejących, nieusuniętych elementów; z kolei usunięcie elementu unieważnia wyłącznie iterator i referencję do tego usuniętego elementu. Kontenery nieuporządkowane (unordered containers) również przechowują elementy w węzłach, więc referencje i wskaźniki na poszczególne elementy pozostają przeważnie stabilne w trakcie operacji `rehash`, lecz operacja ta unieważnia iteratory. Kontener `deque` posiada natomiast dedykowane reguły dla pamięci przechowywanej w segmentach. W praktyce należy zawsze sprawdzić zasady konkretnego kontenera i operacji przed przechowywaniem iteratorów lub referencji w obliczu zbliżających się modyfikacji.
Odpowiedz na to pytanie z trenerem AI
11Porównaj std::map, std::unordered_map oraz kontenery w stylu flat-map w kontekście tablic wyszukiwania (lookup tables) na backendzie.
std::map to uporządkowany kontener asocjacyjny, zazwyczaj oparty na drzewie, z logarytmicznym czasem wyszukiwania, wstawiania i usuwania; jest przydatny, gdy ważna jest iteracja po posortowanych elementach, zapytania o przedziały lub gwarancje dotyczące kolejności. std::unordered_map opiera się na tablicy mieszającej (hash table), oferując średnio stały czas operacji dla konkretnych kluczy, bez zachowania ich kolejności; często jest to dobry wybór domyślny dla dużych, modyfikowalnych tablic wyszukiwania, jeśli funkcja mieszająca jest odpowiednia. Kontener w stylu flat-map przechowuje posortowane pary klucz-wartość w sposób ciągły w pamięci, zapewniając dobrą lokalność pamięci podręcznej oraz szybką iterację i wyszukiwanie binarne, jednak czas wstawiania i usuwania elementów wewnątrz bywa liniowy. W przypadku tablic wyszukiwania na backendzie, wyboru należy dokonać na podstawie tego, czy obciążenie wymaga zachowania kolejności lub przedziałów, opiera się głównie na dokładnym wyszukiwaniu, wymaga częstych modyfikacji, przewidywalnych opóźnień (latency), w zależności od narzutu na pamięć oraz zachowania względem pamięci podręcznej (cache).
Odpowiedz na to pytanie z trenerem AI
12Wyjaśnij `std::optional` i typowe przypadki użycia w backendzie do reprezentowania braku wartości.
`std::optional<T>` reprezentuje albo zawartą wartość typu `T`, albo jej brak. Pusty stan jest reprezentowany przez `std::nullopt`; kod może sprawdzić jego obecność za pomocą `has_value()` lub użyć opcji w kontekście logicznym (boolean), uzyskać dostęp do wartości za pomocą `*` lub `value()` i dostarczyć wartość domyślną za pomocą `value_or()`. W kodzie backendowym jest to przydatne dla pól bazy danych mogących przyjmować wartość null, opcjonalnych pól w żądaniach i konfiguracji, braków trafień w pamięci podręcznej (cache miss) lub repozytorium, gdzie taki brak jest oczekiwany, oraz stanów domenowych, w których wartość strażnicza (sentinel) taka jak -1 lub pusty ciąg znaków byłaby niejednoznaczna. Modeluje to brak wartości, a nie polimorfizm czy rozbudowane informacje o błędach.
Odpowiedz na to pytanie z trenerem AI
13Czym są std::string_view i std::span oraz jakie zagrożenia dla czasu życia obiektów wprowadzają widoki nieprzejmujące własności (non-owning views)?
std::string_view to nieprzejmujący własności (non-owning) widok na ciągłą sekwencję znaków; std::span<T> to nieprzejmujący własności widok na ciągłą sekwencję elementów typu T. Są one użyteczne w przypadku parametrów i interfejsów API buforów bez kopiowania (zero-copy), ponieważ przechowują wskaźnik i długość bez alokacji czy przejmowania własności pamięci. Głównym zagrożeniem jest czas życia: wskazywany obszar pamięci musi żyć dłużej niż sam widok i nie może zostać unieważniony podczas jego używania. Zwrócenie lub zapisanie widoku odnoszącego się do obiektu tymczasowego, lokalnego, zniszczonego lub przealokowanego kontenera może doprowadzić do powstania wiszącego widoku (dangling view).
Odpowiedz na to pytanie z trenerem AI