第29章 自定义标准库
标准库自定义概述
C++标准库设计为可扩展和可定制的,通过各种机制允许开发者根据特定需求自定义标准库组件。自定义标准库的主要方式包括:
- 自定义分配器:控制内存分配策略
- 自定义迭代器:实现特定的数据遍历方式
- 扩展算法:基于标准算法框架实现自定义算法
- 适配器模式:包装现有组件以提供新接口
- 特化标准模板:为特定类型定制标准库行为
- 概念约束:使用C++20概念限制模板参数
自定义分配器
分配器是标准库中用于内存管理的组件,为容器提供内存分配和释放功能。自定义分配器可以优化特定场景下的内存使用。
分配器接口
一个符合标准的分配器需要实现以下核心接口:
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
| #include <memory> #include <cstddef>
template <typename T> class CustomAllocator { public: using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; CustomAllocator() noexcept = default; template <typename U> CustomAllocator(const CustomAllocator<U>&) noexcept {} T* allocate(size_type n) { if (n > max_size()) { throw std::bad_alloc(); } return static_cast<T*>(::operator new(n * sizeof(T))); } void deallocate(T* p, size_type) noexcept { ::operator delete(p); } size_type max_size() const noexcept { return static_cast<size_type>(-1) / sizeof(T); } template <typename U> bool operator==(const CustomAllocator<U>&) const noexcept { return true; } template <typename U> bool operator!=(const CustomAllocator<U>&) const noexcept { return false; } };
|
带状态的分配器
分配器可以包含状态,例如内存池或线程本地存储:
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 63 64 65 66 67 68 69
| #include <memory> #include <vector> #include <mutex>
class MemoryPool { public: void* allocate(size_t size) { std::lock_guard<std::mutex> lock(mutex_); return ::operator new(size); } void deallocate(void* p, size_t size) { std::lock_guard<std::mutex> lock(mutex_); ::operator delete(p); } private: std::mutex mutex_; };
template <typename T> class PoolAllocator { public: using value_type = T; PoolAllocator(MemoryPool& pool) noexcept : pool_(&pool) {} template <typename U> PoolAllocator(const PoolAllocator<U>& other) noexcept : pool_(other.pool_) {} T* allocate(std::size_t n) { return static_cast<T*>(pool_->allocate(n * sizeof(T))); } void deallocate(T* p, std::size_t n) noexcept { pool_->deallocate(p, n * sizeof(T)); } template <typename U> bool operator==(const PoolAllocator<U>& other) const noexcept { return pool_ == other.pool_; } template <typename U> bool operator!=(const PoolAllocator<U>& other) const noexcept { return !(*this == other); } template <typename U> struct rebind { using other = PoolAllocator<U>; }; private: template <typename> friend class PoolAllocator; MemoryPool* pool_; };
int main() { MemoryPool pool; std::vector<int, PoolAllocator<int>> vec(PoolAllocator<int>(pool)); vec.push_back(42); return 0; }
|
分配器使用场景
- 内存池:减少频繁内存分配的开销
- 对齐优化:为特定类型提供对齐保证
- 统计和跟踪:监控内存使用情况
- 线程本地存储:避免线程间竞争
- 受限环境:在内存有限的环境中优化使用
自定义迭代器
迭代器是连接容器和算法的桥梁,自定义迭代器可以为特殊数据结构提供标准的遍历接口。
迭代器类别
C++标准定义了五种迭代器类别,按功能从弱到强排序:
- 输入迭代器:只读,单遍,不可重复
- 输出迭代器:只写,单遍,不可重复
- 前向迭代器:可读写,多遍,可重复
- 双向迭代器:前向迭代器 + 反向遍历
- 随机访问迭代器:双向迭代器 + 随机访问
迭代器实现
下面实现一个简单的双向迭代器:
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include <iterator>
template <typename T> struct Node { T value; Node* next; Node* prev; Node(const T& v) : value(v), next(nullptr), prev(nullptr) {} };
template <typename T> class ListIterator { public: using value_type = T; using difference_type = std::ptrdiff_t; using pointer = T*; using reference = T&; using iterator_category = std::bidirectional_iterator_tag; ListIterator(Node<T>* node) : current(node) {} reference operator*() const { return current->value; } pointer operator->() const { return &(current->value); } ListIterator& operator++() { current = current->next; return *this; } ListIterator operator++(int) { ListIterator tmp = *this; ++(*this); return tmp; } ListIterator& operator--() { current = current->prev; return *this; } ListIterator operator--(int) { ListIterator tmp = *this; --(*this); return tmp; } bool operator==(const ListIterator& other) const { return current == other.current; } bool operator!=(const ListIterator& other) const { return !(*this == other); } private: Node<T>* current; };
template <typename T> class ConstListIterator { };
|
迭代器适配器
迭代器适配器是基于现有迭代器构建的新迭代器,例如反向迭代器:
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
| template <typename Iterator> class ReverseIterator { public: using value_type = typename std::iterator_traits<Iterator>::value_type; using difference_type = typename std::iterator_traits<Iterator>::difference_type; using pointer = typename std::iterator_traits<Iterator>::pointer; using reference = typename std::iterator_traits<Iterator>::reference; using iterator_category = typename std::iterator_traits<Iterator>::iterator_category; ReverseIterator(Iterator it) : current(it) {} reference operator*() const { Iterator tmp = current; --tmp; return *tmp; } ReverseIterator& operator++() { --current; return *this; } ReverseIterator operator++(int) { ReverseIterator tmp = *this; --current; return tmp; } ReverseIterator& operator--() { ++current; return *this; } ReverseIterator operator--(int) { ReverseIterator tmp = *this; ++current; return tmp; } bool operator==(const ReverseIterator& other) const { return current == other.current; } bool operator!=(const ReverseIterator& other) const { return !(*this == other); } private: Iterator current; };
|
自定义算法
标准库算法是通用的,但在某些场景下需要定制化的算法实现。
基于标准算法的扩展
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
| #include <algorithm> #include <vector> #include <functional>
template <typename InputIt, typename... Predicates> auto find_if_all(InputIt first, InputIt last, Predicates&&... preds) { return std::find_if(first, last, [&](const auto& value) { return (std::invoke(preds, value) && ...); }); }
template <typename InputIt, typename UnaryFunction> UnaryFunction for_each_with_index(InputIt first, InputIt last, UnaryFunction f) { std::size_t index = 0; for (; first != last; ++first, ++index) { f(*first, index); } return f; }
int main() { std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; auto it = find_if_all(v.begin(), v.end(), [](int x) { return x > 5; }, [](int x) { return x % 2 == 0; }); for_each_with_index(v.begin(), v.end(), [](int value, std::size_t index) { std::cout << "Index " << index << ": " << value << std::endl; }); 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
| #include <execution> #include <algorithm> #include <vector> #include <numeric>
template <typename ExecutionPolicy, typename InputIt, typename OutputIt, typename UnaryOp, typename T> T transform_reduce(ExecutionPolicy&& policy, InputIt first, InputIt last, OutputIt result, UnaryOp op, T init) { std::transform(policy, first, last, result, op); return std::reduce(policy, result, result + std::distance(first, last), init); }
int main() { std::vector<int> v = {1, 2, 3, 4, 5}; std::vector<int> transformed(v.size()); int sum_of_squares = transform_reduce( std::execution::par, v.begin(), v.end(), transformed.begin(), [](int x) { return x * x; }, 0 ); return 0; }
|
适配器模式
适配器模式可以包装现有标准库组件,为其提供新的接口或行为。
容器适配器
标准库中的stack、queue和priority_queue都是容器适配器,我们可以实现自定义适配器:
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 63 64
| #include <deque> #include <vector> #include <algorithm>
template <typename T, typename Container = std::deque<T>> class FixedSizeQueue { public: using container_type = Container; using value_type = typename Container::value_type; using size_type = typename Container::size_type; using reference = typename Container::reference; using const_reference = typename Container::const_reference; explicit FixedSizeQueue(size_type max_size) : max_size_(max_size) {} bool empty() const { return c_.empty(); } size_type size() const { return c_.size(); } size_type max_size() const { return max_size_; } reference front() { return c_.front(); } const_reference front() const { return c_.front(); } reference back() { return c_.back(); } const_reference back() const { return c_.back(); } void push(const value_type& value) { if (size() >= max_size_) { c_.pop_front(); } c_.push_back(value); } void push(value_type&& value) { if (size() >= max_size_) { c_.pop_front(); } c_.push_back(std::move(value)); } void pop() { c_.pop_front(); } private: Container c_; size_type max_size_; };
int main() { FixedSizeQueue<int> q(3); q.push(1); q.push(2); q.push(3); q.push(4); while (!q.empty()) { std::cout << q.front() << ' '; q.pop(); } 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
| #include <functional>
template <typename Func> class CachedFunction { public: explicit CachedFunction(Func func) : func_(std::move(func)) {} template <typename... Args> auto operator()(Args&&... args) { using Key = std::tuple<std::decay_t<Args>...>; Key key(std::forward<Args>(args)...); auto it = cache_.find(key); if (it != cache_.end()) { return it->second; } auto result = func_(std::forward<Args>(args)...); cache_[key] = result; return result; } void clear_cache() { cache_.clear(); } private: Func func_; std::unordered_map<std::tuple<>, decltype(func_())> cache_; };
template <typename Func> auto make_cached(Func&& func) { return CachedFunction<std::decay_t<Func>>(std::forward<Func>(func)); }
|
特化标准模板
为特定类型特化标准库模板可以定制其行为。
特化std::hash
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 <functional> #include <string>
struct Point { int x; int y; bool operator==(const Point& other) const { return x == other.x && y == other.y; } };
namespace std { template <> struct hash<Point> { size_t operator()(const Point& p) const { size_t h1 = std::hash<int>()(p.x); size_t h2 = std::hash<int>()(p.y); return h1 ^ (h2 << 1); } }; }
int main() { std::unordered_set<Point> points; points.insert({1, 2}); points.insert({3, 4}); return 0; }
|
特化std::less
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 <functional> #include <set> #include <string>
struct Person { std::string name; int age; };
namespace std { template <> struct less<Person> { bool operator()(const Person& a, const Person& b) const { if (a.age != b.age) { return a.age < b.age; } return a.name < b.name; } }; }
int main() { std::set<Person> people; people.insert({"Alice", 30}); people.insert({"Bob", 25}); people.insert({"Charlie", 30}); return 0; }
|
概念约束
C++20引入的概念(Concepts)可以为模板参数提供更清晰的约束。
定义自定义概念
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
| #include <concepts> #include <iterator>
template <typename T> concept Incrementable = requires(T t) { { ++t } -> std::same_as<T&>; { t++ } -> std::same_as<T>; };
template <typename T> concept Printable = requires(std::ostream& os, const T& t) { { os << t } -> std::same_as<std::ostream&>; };
template <Incrementable T> void increment_n(T& value, int n) { for (int i = 0; i < n; ++i) { ++value; } }
template <typename T> concept Iterable = requires(T t) { { std::begin(t) } -> std::input_or_output_iterator; { std::end(t) } -> std::input_or_output_iterator; };
template <typename T> concept IterableAndPrintable = Iterable<T> && Printable<typename std::iter_value_t<decltype(std::begin(std::declval<T>()))>>;
template <IterableAndPrintable T> void print_all(const T& container) { for (const auto& element : container) { std::cout << element << ' '; } std::cout << std::endl; }
|
概念与标准库
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 <concepts> #include <vector> #include <list> #include <set>
template <std::ranges::range R> void process_range(R&& range) { for (auto&& element : range) { } }
template <std::sortable S> void sort_container(S& container) { std::sort(std::begin(container), std::end(container)); }
int main() { std::vector<int> v = {3, 1, 4, 1, 5}; process_range(v); sort_container(v); std::list<int> l = {5, 3, 1}; process_range(l); return 0; }
|
自定义类型特征
类型特征(Type Traits)是编译期信息,用于查询类型的属性。
自定义类型特征
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
| #include <type_traits>
template <typename T> struct is_smart_pointer : std::false_type {};
template <typename T> struct is_smart_pointer<std::unique_ptr<T>> : std::true_type {};
template <typename T> struct is_smart_pointer<std::shared_ptr<T>> : std::true_type {};
template <typename T> struct is_smart_pointer<std::weak_ptr<T>> : std::true_type {};
template <typename T> inline constexpr bool is_smart_pointer_v = is_smart_pointer<T>::value;
template <typename T> concept SmartPointer = is_smart_pointer_v<T>;
template <typename T> void process_pointer(T ptr) { if constexpr (is_smart_pointer_v<T>) { if (ptr) { } } else if constexpr (std::is_pointer_v<T>) { if (ptr) { } } else { } }
|
类型特征组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <type_traits>
template <typename T> struct is_integral_or_enum : std::bool_constant< std::is_integral_v<T> || std::is_enum_v<T> > {};
template <typename T> inline constexpr bool is_integral_or_enum_v = is_integral_or_enum<T>::value;
template <typename T> struct remove_cvref { using type = std::remove_cv_t<std::remove_reference_t<T>>; };
template <typename T> using remove_cvref_t = typename remove_cvref<T>::type;
|
自定义工具类
作用域守卫
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
| #include <functional>
class ScopeGuard { public: template <typename Func> explicit ScopeGuard(Func&& func) : func_(std::forward<Func>(func)), active_(true) {} ScopeGuard(ScopeGuard&& other) noexcept : func_(std::move(other.func_)), active_(other.active_) { other.active_ = false; } ~ScopeGuard() { if (active_) { func_(); } } ScopeGuard(const ScopeGuard&) = delete; ScopeGuard& operator=(const ScopeGuard&) = delete; ScopeGuard& operator=(ScopeGuard&&) = delete; void dismiss() noexcept { active_ = false; } private: std::function<void()> func_; bool active_; };
#define SCOPE_EXIT auto scope_guard_##__LINE__ = ScopeGuard([&]()
int main() { FILE* file = fopen("test.txt", "w"); if (!file) return 1; SCOPE_EXIT { fclose(file); }); fprintf(file, "Hello, World!"); 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
| #include <memory> #include <vector> #include <functional>
class AnyValue { public: virtual ~AnyValue() = default; virtual std::unique_ptr<AnyValue> clone() const = 0; };
template <typename T> class AnyValueImpl : public AnyValue { public: explicit AnyValueImpl(T value) : value_(std::move(value)) {} std::unique_ptr<AnyValue> clone() const override { return std::make_unique<AnyValueImpl<T>>(value_); } T& get() { return value_; } const T& get() const { return value_; } private: T value_; };
class HeterogeneousVector { public: template <typename T> void push_back(T value) { values_.push_back(std::make_unique<AnyValueImpl<T>>(std::move(value))); } template <typename T> T& get(size_t index) { auto* impl = dynamic_cast<AnyValueImpl<T>*>(values_[index].get()); if (!impl) { throw std::bad_cast(); } return impl->get(); } size_t size() const { return values_.size(); } private: std::vector<std::unique_ptr<AnyValue>> values_; };
|
最佳实践
自定义标准库组件的原则
- 遵循接口:严格按照标准库接口设计,确保兼容性
- 注重性能:自定义组件应在特定场景下提供性能优势
- 保持可移植性:避免依赖平台特定特性
- 全面测试:确保与标准库算法和容器配合正常
- 文档清晰:详细说明自定义组件的用途和限制
- 异常安全:提供强异常安全保证
- 资源管理:正确管理所有资源,避免泄漏
性能考虑
- 内存局部性:自定义分配器应优化内存布局
- 减少开销:迭代器操作应尽可能高效
- 避免动态分配:在性能关键路径上减少内存分配
- 编译期计算:利用模板和constexpr提高性能
- 并行处理:对于大型操作,考虑并行实现
安全性考虑
- 类型安全:使用模板和概念确保类型安全
- 边界检查:在适当位置添加边界检查
- 异常处理:正确处理和传播异常
- 资源管理:使用RAII模式管理资源
- 线程安全:在多线程环境中确保安全
实际应用案例
内存池分配器
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 63 64 65 66 67 68
| class MemoryPool { public: MemoryPool(size_t block_size, size_t num_blocks) : block_size_(block_size), num_blocks_(num_blocks) { pool_ = static_cast<char*>(::operator new(block_size * num_blocks)); for (size_t i = 0; i < num_blocks - 1; ++i) { *reinterpret_cast<char**>(pool_ + i * block_size) = pool_ + (i + 1) * block_size; } *reinterpret_cast<char**>(pool_ + (num_blocks - 1) * block_size) = nullptr; free_list_ = pool_; } ~MemoryPool() { ::operator delete(pool_); } void* allocate() { if (!free_list_) { return nullptr; } void* ptr = free_list_; free_list_ = *reinterpret_cast<char**>(free_list_); return ptr; } void deallocate(void* ptr) { if (!ptr) return; *reinterpret_cast<char**>(ptr) = free_list_; free_list_ = static_cast<char*>(ptr); } private: size_t block_size_; size_t num_blocks_; char* pool_; char* free_list_; };
template <typename T> class PoolAllocator { public: using value_type = T; PoolAllocator(MemoryPool& pool) : pool_(&pool) {} template <typename U> PoolAllocator(const PoolAllocator<U>& other) : pool_(other.pool_) {} T* allocate(size_t n) { if (n != 1) { throw std::bad_alloc(); } return static_cast<T*>(pool_->allocate()); } void deallocate(T* p, size_t) { pool_->deallocate(p); } private: template <typename> friend class PoolAllocator; MemoryPool* pool_; };
|
自定义范围适配器
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 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| #include <ranges> #include <vector> #include <iostream>
template <typename T> class StepRange { public: StepRange(T start, T end, T step) : start_(start), end_(end), step_(step) {} class Iterator { public: using value_type = T; using difference_type = std::ptrdiff_t; using pointer = const T*; using reference = const T&; using iterator_category = std::input_iterator_tag; Iterator(T value, T step) : value_(value), step_(step) {} reference operator*() const { return value_; } pointer operator->() const { return &value_; } Iterator& operator++() { value_ += step_; return *this; } Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } bool operator==(const Iterator& other) const { return value_ == other.value_; } bool operator!=(const Iterator& other) const { return !(*this == other); } private: T value_; T step_; }; Iterator begin() const { return Iterator(start_, step_); } Iterator end() const { return Iterator(end_, step_); } private: T start_; T end_; T step_; };
template <typename T> auto step_range(T start, T end, T step) { return StepRange<T>(start, end, step); }
int main() { for (int i : step_range(0, 10, 2)) { std::cout << i << ' '; } return 0; }
|
小结
自定义C++标准库组件是一项高级技术,需要深入理解标准库的设计原理和接口规范。通过合理的自定义,可以:
- 优化性能:针对特定场景定制内存管理和算法实现
- 扩展功能:为标准库添加新的能力和接口
- 提高可维护性:封装复杂逻辑,提供清晰的接口
- 增强安全性:添加类型检查和边界验证
- 适应特殊环境:在受限环境中提供标准库功能
自定义标准库组件时,应遵循以下原则:
- 保持兼容性:严格遵循标准接口,确保与现有代码配合
- 注重质量:提供全面的测试和文档
- 权衡利弊:只有在确实需要时才自定义,避免过度设计
- 持续学习:关注C++标准的更新,利用新特性改进实现
通过本章的学习,读者应该掌握了自定义标准库组件的核心技术,能够根据具体需求设计和实现高质量的自定义组件,从而提升C++程序的性能和可维护性。