Ch21: STL & Ranges¶
TL;DR: STL Containers & Adaptors: C++ เตรียมโครงสร้างข้อมูลพื้นฐานหลากหลายรูปแบบ ทั้งจัดลำดับติดกันในหน่วยความจำ (
vector,array), รายการเชื่อมโยง (list,forward_list), โครงสร้างแบบต้นไม้และตารางแฮช (set,map) รวมถึงอแดปเตอร์แบบเข้าหลังออกก่อน LIFO/FIFO (stack,queue,priority_queue) | แตกต่างจากคอนเทนเนอร์แบบลำดับ (Sequence Containers เช่นvector,list) ที่จัดเก็บข้อมูลตามลำดับตำแหน่ง คอนเทนเนอร์แบบจับคู่เชื่อมโยง (Associative Containers เช่นmap,setรวมถึงกลุ่มที่ขึ้นต้นด้วยunordered_) จะจัดเก็บข้อมูลโดยใช้คีย์ (Key) ช่วยให้ค้นหาข้อมูลได้อย่างรวดเร็ว แลกกับการไม่มีการรับประกันลำดับการป้อนข้อมูล (สำหรับกลุ่มunordered_) หรือการเรียงลำดับข้อมูลเสมอ (สำหรับกลุ่มปกติที่เรียงลำดับ)
⚡ Quick Reference¶
#include <iostream>
#include <vector>
#include <array>
#include <deque>
#include <list>
#include <forward_list>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue>
#include <algorithm>
#include <ranges>
int main() {
std::vector<int> v = {10, 20, 30};
std::array<int, 3> arr = {1, 2, 3};
auto start = v.begin();
auto stop = v.end();
std::deque<int> d = {1, 2};
std::list<int> lst = {5, 6};
std::forward_list<int> fl = {7, 8};
std::set<int> s = {3, 1, 2};
std::map<int, std::string> m = {{1, "one"}};
std::ranges::sort(v);
auto lazy_view = v | std::views::filter([](int x){ return x > 10; })
| std::views::transform([](int x){ return x * 2; });
return 0;
}
คอนเทนเนอร์แบบจับคู่เชื่อมโยง (Associative Containers)¶
#include <iostream>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <string>
int main() {
// std::map<K, V> - เรียงตามคีย์ (โครงสร้างแบบ Red-Black Tree) ประสิทธิภาพการทำงานระดับ O(log n)
std::map<std::string, int> ages;
ages["Alice"] = 30;
ages.insert({"Bob", 25});
// .find() - ส่งคืนตัวอ้างอิงตำแหน่ง (Iterator) หรือ .end() หากหาไม่พบ (ไม่มีการแจ้งเตือน Exception)
if (auto it = ages.find("Alice"); it != ages.end()) {
std::cout << it->first << " is " << it->second << '\n';
}
// .at() - โยนข้อผิดพลาด std::out_of_range หากไม่พบคีย์ (ตรงข้ามกับ operator[] ที่จะป้อนค่าเริ่มต้นให้โดยอัตโนมัติ)
try {
std::cout << ages.at("Charlie") << '\n';
} catch (const std::out_of_range&) {
std::cout << "Charlie not found\n";
}
// std::set<T> - คอนเทนเนอร์เก็บข้อมูลเรียงลำดับและมีค่าไม่ซ้ำกันเท่านั้น
std::set<int> uniqueNums{5, 3, 5, 1, 3}; // ข้อมูลที่ซ้ำกันจะถูกละเลยโดยไม่แจ้งเตือน -> {1, 3, 5}
// std::unordered_map<K, V> - ตารางแฮช (Hash Table) ความเร็วการค้นหาเฉลี่ยระดับ O(1) ไม่รับประกันลำดับข้อมูล
std::unordered_map<std::string, int> fastLookup;
fastLookup["x"] = 1;
// std::unordered_set<T> - คอนเทนเนอร์แบบเซ็ตในรูปแบบตารางแฮช
std::unordered_set<int> fastUnique{5, 3, 5, 1};
// การวนลูปผ่าน map ด้วย Range-based for จะได้ข้อมูลเป็นคู่ Key-Value
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << '\n';
}
return 0;
}
🧠 Core Concepts¶
- STL Containers & Adaptors: C++ เตรียมโครงสร้างข้อมูลพื้นฐานหลากหลายรูปแบบ ทั้งจัดลำดับติดกันในหน่วยความจำ (
vector,array), รายการเชื่อมโยง (list,forward_list), โครงสร้างแบบต้นไม้และตารางแฮช (set,map) รวมถึงอแดปเตอร์แบบเข้าหลังออกก่อน LIFO/FIFO (stack,queue,priority_queue) - Ranges & Adaptor Views: คลัง Ranges ของ C++20 ช่วยอำนวยความสะดวกในการจัดระเบียบตรรกะประมวลผลแทนการระบุอ้างอิง iterator คู่ โดยใช้กลไกของ Views (
filter,transform,take,iota) เพื่อดึงผลลัพธ์แบบ Lazy (ดึงค่าเมื่อใช้) ผ่านตัวดำเนินการ Pipe (|) - Custom Iterator Protocols: ออบเจกต์ Iterator สำหรับคลาสของเราจะต้องประกาศสิทธิการเข้าถึง 5 แบบที่ระบุตัวแปรประเภทข้อมูล และทำโอเปอเรเตอร์อ้างอิง ดึงค่า เปลี่ยนตำแหน่ง เพื่อรองรับการนำไปใช้ร่วมกับฟังก์ชันมาตรฐานของ STL
- Ordered vs Unordered: คอนเทนเนอร์
std::map/std::setจะเรียงลำดับข้อมูลสมาชิกตามคีย์ (มักใช้โครงสร้างแบบ Red-Black Tree) มอบประสิทธิภาพการทำงานที่O(log n)และการวนรอบข้อมูลตามลำดับคีย์ ส่วนstd::unordered_map/std::unordered_setจะใช้ตารางแฮช (Hash Table) เพื่อประสิทธิภาพการทำงานเฉลี่ยที่O(1)แต่ไม่รับประกันลำดับข้อมูลสมาชิก - Key Uniqueness: คอนเทนเนอร์กลุ่ม
map/set/unordered_map/unordered_setบังคับให้คีย์ต้องไม่ซ้ำกันเท่านั้น — การเขียนคีย์ซ้ำจะถูกละเลยการทำงาน (ค่าเดิมยังคงอยู่) หากต้องการคีย์ซ้ำกันได้ให้เลือกใช้งานคลาสประเภทmultimap/multisetแทน operator[]vs.at(): การใช้operator[]บนmapหากไม่มีคีย์นั้นอยู่ระบบจะแอบสร้าง Default Constructor และแทรกคีย์ใหม่ให้ทันที — แม้จะสะดวกแต่มักเป็นต้นตอที่ซ่อนบั๊กเอาไว้ ส่วนคำสั่ง.at()จะโยนข้อผิดพลาดstd::out_of_rangeออกมาแทน ทำให้สามารถดักจับจุดบกพร่องเรื่องคีย์สูญหายได้ง่ายขึ้น- Structured Bindings with Maps: การวิ่งวนลูปใน
mapจะส่งคืนสมาชิกประเภทstd::pair<const Key, Value>ซึ่งการนำ Structured Bindings (ตั้งแต่ C++17 ขึ้นไป) มาใช้ เช่นfor (const auto& [key, value] : myMap)จะช่วยแกะคู่ข้อมูลออกมาใช้งานได้อย่างสวยงาม
⚠️ Pitfalls (Quick Scan)¶
| ข้อผิดพลาด | วิธีแก้ |
|---|---|
การประกาศขยายขนาดของ Vector ด้วยการใช้วงเล็บปีกกา (เช่น vector<int>{20, 55}) |
เปลี่ยนไปใช้วงเล็บธรรมดาในการป้อนคำสั่ง vector<int>(20, 55) เพื่อกำหนดขนาดช่อง |
ดึงข้อมูลหรือเลื่อนตำแหน่งของ Iterator ให้เลยช่วงขอบเขตสิ้นสุด end() |
ตรวจสอบเงื่อนไข it != container.end() ให้มั่นใจก่อนดึงค่าใช้งาน |
| แก้ไขหรือลบสมาชิกใน Vector ระหว่างที่มีลูปดึงข้อมูลจาก Iterator | จัดระบบพิกัดใหม่หรือดึงตำแหน่งขาส่งกลับมาจากฟังก์ชันลบแก้ไขเพื่อปรับค่า |
อ่านค่าจากคีย์ที่ไม่มีอยู่ใน std::map ผ่านตัวดำเนินการ operator[] |
ใช้เมธอด .find() หรือ .at() ในการอ่านค่า และสงวน operator[] สำหรับการอัปเดตข้อมูลเท่านั้น |
เรียกใช้คำสั่งคัดลอกข้อมูล std::copy หรือแปลงค่าไปยัง Container เป้าหมายที่ไม่มีการจองพื้นที่ |
ตั้งขนาดจองพื้นที่ของออบเจกต์ปลายทางล่วงหน้า หรือประยุกต์ใช้ std::back_inserter เพื่อช่วยขยายขนาดยามเขียน |
ตั้งเงื่อนไขเปรียบเทียบที่ไม่เข้มงวดพอ (เช่น ใช้เครื่องหมาย <=) ในฟังก์ชันคัสตอมสำหรับ std::sort |
เขียนตรรกะเปรียบเทียบโดยใช้เฉพาะเครื่องหมายน้อยกว่า < เสมอ |
ไม่ได้จำกัดขนาดความยาวของลำดับที่เจเนอเรตมาจาก std::views::iota |
ดักควบคุมขนาดข้อมูลโดยพ่วงด้วยฟังก์ชันควบคุม เช่น std::views::take |
| ลืมสร้างและส่งคืนค่าสำเนาออบเจกต์ในคำสั่ง Post-increment สำหรับ Custom Iterator | ทำการสำเนาออบเจกต์เดิมไว้ก่อน ขยับตำแหน่งปัจจุบันขึ้น แล้วส่งคืนตัวสำเนาตัวเดิมกลับไป |
| ไม่ได้แยกประเภทระหว่างตัวแปรอ่านแก้ไขได้ทั่วไปและตัวแปรแบบ const สำหรับ Custom Iterator | ออกแบบคลาส ConstIterator แยกออกมาต่างหาก หรือประยุกต์ใช้ raw pointer const T* เป็นตัวช่วยสืบค้น |
ใช้ operator[] เพื่อเช็คว่ามีคีย์อยู่หรือไม่ |
คำสั่งนี้จะส่งผลข้างเคียงโดยการสร้างและแทรกค่าเริ่มต้นให้ทันที — ให้ใช้ .find() หรือ .contains() แทน |
คาดหวังให้ลำดับการวนลูปของ unordered_map คงที่หรือเรียงตามลำดับที่ใส่เข้าไป |
ไม่มีการรับประกันลำดับข้อมูลใดๆ — ให้ใช้ std::map แทนหากลำดับมีความสำคัญ |
| คิดว่าการแทรกข้อมูลคีย์ซ้ำจะเขียนทับค่าเดิมที่มีอยู่ | คำสั่ง insert() จะไม่ทำงานเลยเมื่อคีย์ซ้ำ — ให้ใช้ operator[] หรือ insert_or_assign() เพื่อเขียนทับข้อมูลแทน |
ใช้ Struct ที่ปรับเปลี่ยนค่าได้เป็นสมาชิกใน std::set และแก้ไขค่าสมาชิกโดยตรง |
การแก้ไขข้อมูลตรงๆ จะทำลายระเบียบการเรียงลำดับของต้นไม้ในโครงสร้างข้อมูล — ให้ลบสมาชิกเดิมออกแล้วค่อยป้อนค่าใหม่เข้าไปแทน |
📖 Full Details¶
Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู)
* **การประกาศขยายขนาดของ Vector ด้วยการใช้วงเล็บปีกกา (เช่น `vector📎 Repo Files¶
43.StlContainersAndIterators/43.2StdVector/main.cpp43.StlContainersAndIterators/43.3StdArray/main.cpp43.StlContainersAndIterators/43.4Iterators/main.cpp43.StlContainersAndIterators/43.6ReverseIterators/main.cpp43.StlContainersAndIterators/43.7ContantIterators/main.cpp43.StlContainersAndIterators/43.9StdBeginStdEnd/main.cpp44.ZoomingOnSTLContainers/44.2Deque/main.cpp44.ZoomingOnSTLContainers/44.3ForwardList/main.cpp44.ZoomingOnSTLContainers/44.4List/main.cpp44.ZoomingOnSTLContainers/44.5Vector/main.cpp44.ZoomingOnSTLContainers/44.6Array/main.cpp44.ZoomingOnSTLContainers/44.8Pair/main.cpp44.ZoomingOnSTLContainers/44.9Set/main.cpp44.ZoomingOnSTLContainers/44.10Map/main.cpp44.ZoomingOnSTLContainers/44.11MultisetMultimap/main.cpp44.ZoomingOnSTLContainers/44.12UnorderedSetUnorderedMap/main.cpp44.ZoomingOnSTLContainers/44.14Stack/main.cpp44.ZoomingOnSTLContainers/44.15Queue/main.cpp44.ZoomingOnSTLContainers/44.16PriorityQueue/main.cpp45.StlAlgorithms/45.2AllOf/main.cpp45.StlAlgorithms/45.3ForEach/main.cpp45.StlAlgorithms/45.4.MaxEltMinElt/main.cpp45.StlAlgorithms/45.5FindAndFindIf/main.cpp45.StlAlgorithms/45.6Copy/main.cpp45.StlAlgorithms/45.7Sort/main.cpp45.StlAlgorithms/45.8Transform/main.cpp46.RangesLibraryInCpp20/46.2RangeAlgorithms/main.cpp46.RangesLibraryInCpp20/46.4Projections/main.cpp46.RangesLibraryInCpp20/46.5ViewsAndRangeAdaptors/main.cpp46.RangesLibraryInCpp20/46.6ViewCompositionAndPipeOperator/main.cpp46.RangesLibraryInCpp20/46.7RangeFactories/main.cpp47.BuildingIteratorsForCustomContainers/40.02.IteratorPowers/main.cpp47.BuildingIteratorsForCustomContainers/40.04CustomInputIterator/main.cpp47.BuildingIteratorsForCustomContainers/40.05CustomOutputIterator/main.cpp47.BuildingIteratorsForCustomContainers/40.06CustomForwardIterator/main.cpp47.BuildingIteratorsForCustomContainers/40.07CustomBidirectionalIteator/main.cpp47.BuildingIteratorsForCustomContainers/40.08CustomRandomAccessIterator/main.cpp47.BuildingIteratorsForCustomContainers/40.09CustomIteratorsWithViews/main.cpp47.BuildingIteratorsForCustomContainers/40.10ConstantIterators/main.cpp47.BuildingIteratorsForCustomContainers/40.11RawPointersAsIterators/main.cpp47.BuildingIteratorsForCustomContainers/40.12WrappingIteratorsFromOtherContainers/main.cpp43.StlContainersAndIterators/main.cpp44.ZoomingOnSTLContainers/main.cpp