如何为仿std::function的函数容器类添加整型模板参数实现小缓冲区优化?
为自定义函数容器添加可配置小缓冲区优化的实现方案
你需要给MyDynamicCallable添加可配置的小缓冲区大小参数,核心思路是在类中嵌入固定大小的栈缓冲区,当可调用对象的大小和对齐要求能被缓冲区满足时,直接在栈上存储对象以避免堆分配;否则沿用原有的堆内存分配逻辑。以下是具体实现步骤和代码:
核心修改要点
- 给类模板增加非类型模板参数
BufferSize,用于指定小缓冲区大小,可设置默认值(比如16,和部分std::function实现一致) - 在类内部添加对齐的小缓冲区存储,同时增加标记位区分当前使用栈缓冲区还是堆内存
- 修改构造逻辑:判断可调用对象的大小和对齐要求是否适配缓冲区,选择对应存储方式
- 完善
reset函数,确保正确调用对象析构并释放堆内存(如果使用了的话)
修改后的完整代码
#include <type_traits> #include <utility> // 假设MemMngr是已实现的内存管理类 struct MemMngr { static void* mem_alloc(size_t size) { return operator new(size); } static void mem_free(void* ptr) { operator delete(ptr); } }; template <typename T, size_t BufferSize = 16> class MyDynamicCallable; template <typename ret_t, typename... arg_types, size_t BufferSize> class MyDynamicCallable<ret_t(arg_types...), BufferSize> { public: template <typename callable_t> MyDynamicCallable(callable_t&& callable) { using CallableType = std::decay_t<callable_t>; constexpr size_t obj_size = sizeof(CallableType); constexpr size_t obj_align = alignof(CallableType); // 检查对象是否能放入小缓冲区且对齐要求满足 if (obj_size <= BufferSize && obj_align <= alignof(decltype(buffer))) { // 使用栈上缓冲区 is_heap_allocated = false; new (get_buffer_ptr()) CallableType(std::forward<callable_t>(callable)); } else { // 走堆分配 is_heap_allocated = true; obj_ptr = MemMngr::mem_alloc(obj_size); new (obj_ptr) CallableType(std::forward<callable_t>(callable)); } // 设置调用函数指针 call_func_ptr = &call_object<CallableType>; // 保存析构函数指针,用于reset时清理 destroy_func_ptr = &destroy_object<CallableType>; } // 移动构造 MyDynamicCallable(MyDynamicCallable&& other) noexcept : call_func_ptr(other.call_func_ptr), destroy_func_ptr(other.destroy_func_ptr), is_heap_allocated(other.is_heap_allocated) { if (other.is_heap_allocated) { obj_ptr = other.obj_ptr; other.obj_ptr = nullptr; } else { // 复制栈缓冲区内容(需对象支持移动或复制) std::memcpy(buffer, other.buffer, BufferSize); } other.call_func_ptr = nullptr; other.destroy_func_ptr = nullptr; } // 移动赋值运算符 MyDynamicCallable& operator=(MyDynamicCallable&& other) noexcept { if (this != &other) { reset(); call_func_ptr = other.call_func_ptr; destroy_func_ptr = other.destroy_func_ptr; is_heap_allocated = other.is_heap_allocated; if (other.is_heap_allocated) { obj_ptr = other.obj_ptr; other.obj_ptr = nullptr; } else { std::memcpy(buffer, other.buffer, BufferSize); } other.call_func_ptr = nullptr; other.destroy_func_ptr = nullptr; } return *this; } // 禁止拷贝构造和拷贝赋值 MyDynamicCallable(const MyDynamicCallable&) = delete; MyDynamicCallable& operator=(const MyDynamicCallable&) = delete; ~MyDynamicCallable() { reset(); } void reset() { if (destroy_func_ptr) { destroy_func_ptr(get_object_ptr()); destroy_func_ptr = nullptr; call_func_ptr = nullptr; } if (is_heap_allocated && obj_ptr) { MemMngr::mem_free(obj_ptr); obj_ptr = nullptr; is_heap_allocated = false; } } ret_t operator()(arg_types... args) const { return call_func_ptr(get_object_ptr(), std::forward<arg_types>(args)...); } private: // 小缓冲区,用aligned_storage确保对齐 typename std::aligned_storage<BufferSize, alignof(std::max_align_t)>::type buffer; void* obj_ptr = nullptr; ret_t (*call_func_ptr)(void*, arg_types...) = nullptr; void (*destroy_func_ptr)(void*) = nullptr; bool is_heap_allocated = false; // 获取对象指针 void* get_object_ptr() const { return is_heap_allocated ? obj_ptr : const_cast<void*>(static_cast<const void*>(&buffer)); } template <typename callable_t> static ret_t call_object(void* obj, arg_types... args) { return (*static_cast<callable_t*>(obj))(std::forward<arg_types>(args)...); } template <typename callable_t> static void destroy_object(void* obj) { static_cast<callable_t*>(obj)->~callable_t(); } };
关键细节说明
- 缓冲区对齐:使用
std::aligned_storage并指定std::max_align_t作为对齐值,确保缓冲区能满足绝大多数类型的对齐要求;若需更精准对齐,可根据可调用对象的alignof动态判断,但静态缓冲区仅能满足不超过自身对齐要求的对象。 - 析构处理:新增
destroy_func_ptr存储对应类型的析构函数指针,确保reset或析构时能正确销毁对象,避免内存泄漏。 - 移动语义:补充移动构造和移动赋值运算符,处理栈缓冲区内容复制与堆指针转移,符合容器的移动语义要求。
- 模板参数默认值:给
BufferSize设置默认值16,用户既可以用MyDynamicCallable<void()>使用默认缓冲区,也可以通过MyDynamicCallable<void(), 32>自定义大小。
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

