第28章 标准库详解
标准库概述
C++标准库是C++语言的核心组成部分,提供了丰富的工具和组件,用于简化常见编程任务,提高代码质量和可维护性。标准库的设计遵循以下原则:
- 高性能:组件设计注重效率,避免不必要的开销
- 可移植性:跨平台兼容,确保代码在不同环境下的一致性
- 安全性:提供类型安全的接口,减少运行时错误
- 可扩展性:设计灵活,支持用户自定义扩展
标准库的组织结构
C++标准库可分为以下几个主要部分:
| 组件 | 描述 | 头文件示例 |
|---|
| 核心语言支持 | 语言特性的直接支持 | <cstddef>, <limits> |
| 容器库 | 数据结构实现 | <vector>, <map>, <unordered_map> |
| 算法库 | 通用算法实现 | <algorithm>, <numeric> |
| 迭代器库 | 迭代器类型和操作 | <iterator> |
| 工具库 | 通用工具和辅助类 | <utility>, <functional>, <memory> |
| 字符串库 | 字符串处理 | <string>, <string_view> |
| 流库 | 输入/输出操作 | <iostream>, <fstream> |
| 本地化库 | 国际化支持 | <locale> |
| 正则表达式 | 模式匹配 | <regex> |
| 文件系统 | 文件和目录操作 | <filesystem> |
| 线程支持 | 并发编程 | <thread>, <mutex> |
| 原子操作 | 无锁编程 | <atomic> |
| 时间库 | 时间和日期处理 | <chrono> |
| 随机数 | 随机数生成 | <random> |
| 数学库 | 数学函数 | <cmath> |
| 范围库 | 范围操作(C++20+) | <ranges> |
核心语言支持库
核心语言支持库提供了C++语言基本特性的支持,包括类型定义、宏和函数。
类型和常量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <cstddef> #include <cstdint> #include <limits> #include <climits> #include <cfloat>
int main() { std::cout << "size_t: " << sizeof(std::size_t) << " bytes" << std::endl; std::cout << "ptrdiff_t: " << sizeof(std::ptrdiff_t) << " bytes" << std::endl; std::cout << "int32_t: " << sizeof(std::int32_t) << " bytes" << std::endl; std::cout << "uint64_t: " << sizeof(std::uint64_t) << " bytes" << std::endl; std::cout << "int min: " << std::numeric_limits<int>::min() << std::endl; std::cout << "int max: " << std::numeric_limits<int>::max() << std::endl; std::cout << "double epsilon: " << std::numeric_limits<double>::epsilon() << std::endl; return 0; }
|
错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <cassert> #include <cerrno> #include <system_error>
int main() { int x = 5; assert(x > 0 && "x must be positive"); errno = 0; try { throw std::system_error(std::error_code(ENOENT, std::generic_category()), "File not found"); } catch (const std::system_error& e) { std::cout << "Error: " << e.what() << std::endl; std::cout << "Error code: " << e.code() << std::endl; } return 0; }
|
容器库
容器库提供了各种数据结构的实现,用于存储和组织数据。
序列容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <vector> #include <list> #include <deque> #include <array> #include <forward_list>
int main() { std::vector<int> v = {1, 2, 3, 4, 5}; v.push_back(6); v.pop_back(); std::list<int> l = {1, 2, 3}; l.push_front(0); l.push_back(4); std::deque<int> d = {1, 2, 3}; d.push_front(0); d.push_back(4); std::array<int, 5> a = {1, 2, 3, 4, 5}; std::forward_list<int> fl = {1, 2, 3}; fl.push_front(0); return 0; }
|
关联容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <set> #include <map> #include <unordered_set> #include <unordered_map> #include <multiset> #include <multimap>
int main() { std::set<int> s = {3, 1, 4, 1, 5, 9}; std::map<std::string, int> m; m["one"] = 1; m["two"] = 2; std::unordered_set<int> us = {3, 1, 4, 1, 5, 9}; std::unordered_map<std::string, int> um; um["one"] = 1; um["two"] = 2; std::multiset<int> ms = {3, 1, 4, 1, 5, 9}; std::multimap<std::string, int> mm; mm.insert({"one", 1}); mm.insert({"one", 10}); return 0; }
|
容器适配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <stack> #include <queue> #include <priority_queue>
int main() { std::stack<int> st; st.push(1); st.push(2); st.pop(); std::queue<int> q; q.push(1); q.push(2); q.pop(); std::priority_queue<int> pq; pq.push(3); pq.push(1); pq.push(4); return 0; }
|
算法库
算法库提供了大量通用算法,可用于各种容器和数据结构。
查找和搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <algorithm> #include <vector>
int main() { std::vector<int> v = {1, 3, 5, 7, 9}; auto it = std::find(v.begin(), v.end(), 5); if (it != v.end()) { std::cout << "Found: " << *it << std::endl; } bool found = std::binary_search(v.begin(), v.end(), 5); auto lower = std::lower_bound(v.begin(), v.end(), 4); auto upper = std::upper_bound(v.begin(), v.end(), 5); return 0; }
|
排序和修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <algorithm> #include <vector>
int main() { std::vector<int> v = {5, 2, 9, 1, 5, 6}; std::sort(v.begin(), v.end()); std::stable_sort(v.begin(), v.end()); std::partial_sort(v.begin(), v.begin() + 3, v.end()); std::sort(v.begin(), v.end()); auto last = std::unique(v.begin(), v.end()); v.erase(last, v.end()); std::reverse(v.begin(), v.end()); std::rotate(v.begin(), v.begin() + 1, v.end()); return 0; }
|
数值算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <numeric> #include <vector>
int main() { std::vector<int> v = {1, 2, 3, 4, 5}; int sum = std::accumulate(v.begin(), v.end(), 0); int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>()); std::vector<int> diffs(v.size() - 1); std::adjacent_difference(v.begin(), v.end(), diffs.begin()); std::vector<int> prefix_sums(v.size()); std::partial_sum(v.begin(), v.end(), prefix_sums.begin()); return 0; }
|
范围算法(C++20+)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <algorithm> #include <ranges> #include <vector>
int main() { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; auto result = v | std::views::filter([](int x) { return x % 2 == 0; }) | std::views::transform([](int x) { return x * 2; }); for (int x : result) { std::cout << x << " "; } return 0; }
|
工具库
工具库提供了各种通用工具和辅助类,支持标准库的其他部分。
通用工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <utility> #include <tuple> #include <optional> #include <variant> #include <any>
int main() { std::pair<int, std::string> p(1, "one"); std::tuple<int, std::string, double> t(1, "one", 1.1); int first = std::get<0>(t); std::optional<int> opt = 42; if (opt.has_value()) { std::cout << "Value: " << opt.value() << std::endl; } std::variant<int, std::string> var = 42; var = "hello"; std::any a = 42; a = std::string("hello"); return 0; }
|
函数对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include <functional>
int add(int a, int b) { return a + b; }
class Multiply { public: int operator()(int a, int b) const { return a * b; } };
int main() { int (*func_ptr)(int, int) = add; Multiply multiply; auto add5 = std::bind(add, std::placeholders::_1, 5); std::function<int(int, int)> f = add; f = multiply; return 0; }
|
内存管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <memory>
class Resource { public: Resource() { std::cout << "Resource created" << std::endl; } ~Resource() { std::cout << "Resource destroyed" << std::endl; } void use() { std::cout << "Resource used" << std::endl; } };
int main() { std::unique_ptr<Resource> up1(new Resource()); std::unique_ptr<Resource> up2 = std::make_unique<Resource>(); std::shared_ptr<Resource> sp1(new Resource()); std::shared_ptr<Resource> sp2 = std::make_shared<Resource>(); std::shared_ptr<Resource> sp3 = sp2; std::weak_ptr<Resource> wp = sp2; if (auto locked = wp.lock()) { locked->use(); } return 0; }
|
字符串库
字符串库提供了字符串处理的功能,包括std::string和C++17引入的std::string_view。
字符串操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <string> #include <string_view>
int main() { std::string s1 = "Hello"; std::string s2(5, 'a'); std::string s3(s1); s1 += " World"; s1.append("!"); size_t pos = s1.find("World"); if (pos != std::string::npos) { std::cout << "Found at position: " << pos << std::endl; } std::string sub = s1.substr(6, 5); int num = std::stoi("42"); double d = std::stod("3.14"); std::string num_str = std::to_string(42); std::string_view sv = s1; std::string_view sv2 = "Hello"; return 0; }
|
流库
流库提供了输入/输出操作的功能,包括标准输入/输出、文件操作和字符串流。
标准输入/输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <iostream> #include <iomanip>
int main() { std::cout << "Hello, World!" << std::endl; std::cout << std::fixed << std::setprecision(2); std::cout << "Pi: " << 3.14159 << std::endl; int x; std::cout << "Enter a number: "; std::cin >> x; if (std::cin.fail()) { std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout << "Invalid input!" << std::endl; } return 0; }
|
文件操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <fstream>
int main() { std::ofstream outfile("example.txt"); if (outfile.is_open()) { outfile << "Hello, File!" << std::endl; outfile.close(); } std::ifstream infile("example.txt"); if (infile.is_open()) { std::string line; while (std::getline(infile, line)) { std::cout << line << std::endl; } infile.close(); } std::ofstream binfile("data.bin", std::ios::binary); int data = 42; binfile.write(reinterpret_cast<char*>(&data), sizeof(data)); binfile.close(); return 0; }
|
字符串流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <sstream>
int main() { std::ostringstream oss; oss << "Hello, " << "String Stream!" << std::endl; std::string str = oss.str(); std::string input = "123 456 789"; std::istringstream iss(input); int a, b, c; iss >> a >> b >> c; std::string num_str = "12345"; std::istringstream iss2(num_str); int num; iss2 >> num; return 0; }
|
文件系统库
C++17引入的文件系统库提供了跨平台的文件和目录操作功能。
文件系统操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <filesystem> #include <iostream>
namespace fs = std::filesystem;
int main() { fs::path current_path = fs::current_path(); std::cout << "Current path: " << current_path << std::endl; fs::path dir_path = current_path / "test_dir"; if (!fs::exists(dir_path)) { fs::create_directory(dir_path); } fs::path nested_path = dir_path / "subdir1" / "subdir2"; fs::create_directories(nested_path); for (const auto& entry : fs::directory_iterator(current_path)) { std::cout << entry.path() << " " << (fs::is_directory(entry.status()) ? "[dir]" : "[file]") << std::endl; } fs::path file_path = current_path / "example.txt"; if (fs::exists(file_path)) { std::cout << "File size: " << fs::file_size(file_path) << " bytes" << std::endl; std::cout << "Last write time: " << fs::last_write_time(file_path).time_since_epoch().count() << std::endl; } fs::path old_path = dir_path / "old.txt"; fs::path new_path = dir_path / "new.txt"; if (fs::exists(old_path)) { fs::rename(old_path, new_path); } if (fs::exists(new_path)) { fs::remove(new_path); } if (fs::exists(dir_path)) { fs::remove_all(dir_path); } return 0; }
|
时间库
C++11引入的时间库提供了时间和日期处理的功能。
时间点和时长
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <chrono> #include <iostream>
int main() { using namespace std::chrono; auto now = system_clock::now(); std::time_t now_c = system_clock::to_time_t(now); std::cout << "Current time: " << std::ctime(&now_c); seconds sec(1); milliseconds ms = sec; microseconds us = ms; nanoseconds ns = us; auto future = now + hours(1); auto duration = future - now; std::cout << "Duration in seconds: " << duration_cast<seconds>(duration).count() << std::endl; std::this_thread::sleep_for(milliseconds(500)); return 0; }
|
时钟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| #include <chrono>
int main() { using system_clock = std::chrono::system_clock; using steady_clock = std::chrono::steady_clock; using high_resolution_clock = std::chrono::high_resolution_clock; auto start = steady_clock::now(); for (int i = 0; i < 1000000; ++i) { } auto end = steady_clock::now(); auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start); return 0; }
|
随机数库
C++11引入的随机数库提供了高质量的随机数生成功能。
随机数生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <random> #include <iostream>
int main() { std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> dist(1, 100); std::uniform_real_distribution<> real_dist(0.0, 1.0); std::normal_distribution<> norm_dist(0.0, 1.0); for (int i = 0; i < 5; ++i) { std::cout << "Random int: " << dist(gen) << std::endl; std::cout << "Random real: " << real_dist(gen) << std::endl; std::cout << "Random normal: " << norm_dist(gen) << std::endl; } return 0; }
|
线程支持库
C++11引入的线程支持库提供了并发编程的功能。
线程创建和管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <thread> #include <iostream> #include <vector>
void print_number(int n) { std::cout << "Number: " << n << std::endl; }
int main() { std::thread t1(print_number, 42); t1.join(); std::thread t2(print_number, 100); t2.detach(); std::vector<std::thread> threads; for (int i = 0; i < 5; ++i) { threads.emplace_back(print_number, i); } for (auto& t : threads) { t.join(); } return 0; }
|
同步原语
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| #include <thread> #include <mutex> #include <condition_variable> #include <queue> #include <iostream>
std::mutex mtx; std::condition_variable cv; std::queue<int> tasks; bool done = false;
void producer() { for (int i = 0; i < 5; ++i) { { std::lock_guard<std::mutex> lock(mtx); tasks.push(i); std::cout << "Produced: " << i << std::endl; } cv.notify_one(); } { std::lock_guard<std::mutex> lock(mtx); done = true; } cv.notify_all(); }
void consumer() { while (true) { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return !tasks.empty() || done; }); if (tasks.empty() && done) { break; } int task = tasks.front(); tasks.pop(); lock.unlock(); std::cout << "Consumed: " << task << std::endl; } }
int main() { std::thread prod(producer); std::thread cons(consumer); prod.join(); cons.join(); return 0; }
|
标准库最佳实践
容器选择
| 场景 | 推荐容器 | 原因 |
|---|
| 随机访问频繁,需要动态大小 | std::vector | 连续内存,缓存友好,随机访问O(1) |
| 频繁在中间插入/删除 | std::list 或 std::forward_list | 链表结构,插入/删除O(1) |
| 需要两端操作 | std::deque | 两端插入/删除O(1),随机访问O(1) |
| 固定大小数组 | std::array | 编译期大小,栈分配,性能好 |
| 查找频繁,需要有序 | std::set / std::map | 红黑树实现,查找O(log n) |
| 查找频繁,不需要有序 | std::unordered_set / std::unordered_map | 哈希表实现,平均查找O(1) |
| 需要按优先级处理元素 | std::priority_queue | 堆实现,获取最高优先级元素O(1) |
性能优化
- 避免不必要的拷贝:使用移动语义和引用
- 预分配内存:对
std::vector使用reserve() - 选择合适的算法:根据具体需求选择最优算法
- 使用视图而非拷贝:如
std::string_view(C++17+) - 避免动态内存分配:优先使用栈分配和对象池
- 利用编译期计算:使用
constexpr和模板元编程 - 并行处理:对于大型数据集,考虑使用并行算法(C++17+)
安全性
- 使用智能指针:避免内存泄漏和悬空指针
- 边界检查:使用
at()而非operator[]进行边界检查 - 异常安全:确保代码在异常情况下仍能保持一致状态
- 类型安全:避免使用
void*和类型转换 - 资源管理:使用RAII模式管理资源
代码风格
- 命名空间:在实现文件中使用
using namespace std;,但在头文件中避免 - 包含顺序:标准库头文件在前,然后是第三方库,最后是本地头文件
- 代码组织:将相关功能分组,使用清晰的注释
- 一致性:保持代码风格一致,包括缩进、命名和格式
标准库高级主题
自定义分配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| #include <memory> #include <vector>
template <typename T> class MyAllocator { public: using value_type = T; MyAllocator() noexcept = default; template <typename U> MyAllocator(const MyAllocator<U>&) noexcept {} T* allocate(std::size_t n) { if (n > std::size_t(-1) / sizeof(T)) { throw std::bad_alloc(); } if (auto p = static_cast<T*>(std::malloc(n * sizeof(T)))) { return p; } throw std::bad_alloc(); } void deallocate(T* p, std::size_t) noexcept { std::free(p); } };
template <typename T, typename U> bool operator==(const MyAllocator<T>&, const MyAllocator<U>&) noexcept { return true; }
template <typename T, typename U> bool operator!=(const MyAllocator<T>&, const MyAllocator<U>&) noexcept { return false; }
int main() { std::vector<int, MyAllocator<int>> v; v.push_back(42); return 0; }
|
类型擦除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <memory> #include <functional> #include <iostream>
class Drawable { public: virtual ~Drawable() = default; virtual void draw() const = 0; };
template <typename T> class DrawableImpl : public Drawable { public: DrawableImpl(T t) : obj(std::move(t)) {} void draw() const override { obj.draw(); } private: T obj; };
class AnyDrawable { public: template <typename T> AnyDrawable(T t) : impl(std::make_unique<DrawableImpl<T>>(std::move(t))) {} void draw() const { impl->draw(); } private: std::unique_ptr<Drawable> impl; };
class Circle { public: void draw() const { std::cout << "Drawing a circle" << std::endl; } };
class Square { public: void draw() const { std::cout << "Drawing a square" << std::endl; } };
int main() { std::vector<AnyDrawable> drawables; drawables.emplace_back(Circle()); drawables.emplace_back(Square()); for (const auto& d : drawables) { d.draw(); } return 0; }
|
小结
C++标准库是一个功能丰富、设计精良的工具集,为C++程序员提供了从基础数据结构到高级并发工具的全方位支持。本章详细介绍了标准库的各个组成部分,包括:
- 核心语言支持:提供类型定义、错误处理等基础功能
- 容器库:实现各种数据结构,如向量、列表、映射等
- 算法库:提供排序、查找、数值计算等通用算法
- 工具库:包括智能指针、函数对象、元组等实用工具
- 字符串库:处理字符串和字符串视图
- 流库:实现输入/输出操作
- 文件系统库:提供跨平台的文件和目录操作
- 时间库:处理时间和日期
- 随机数库:生成高质量随机数
- 线程支持库:实现并发编程
掌握标准库的使用对于编写高效、可维护的C++代码至关重要。通过合理选择和使用标准库组件,开发者可以:
- 提高开发效率:避免重复造轮子,利用成熟的实现
- 提升代码质量:标准库经过广泛测试和优化,质量有保障
- 增强可移植性:标准库确保代码在不同平台上的一致性
- 优化性能:标准库组件通常经过性能优化
在实际编程中,应根据具体需求选择合适的标准库组件,并遵循最佳实践,以充分发挥C++语言的优势。
标准库是C++语言不断发展的重要部分,随着C++标准的更新(如C++17、C++20、C++23),标准库也在不断扩展和改进,为开发者提供更多现代、高效的工具。持续学习和掌握标准库的新特性,是成为优秀C++程序员的必经之路。