为何自定义MockAllocator未被调用?如何正确定义std::basic_string的Mock分配器
我尝试定义一个使用MockAllocator的std::basic_string特化版本,用于记录basic_string执行的所有内存操作。
struct MockAllocator : std::allocator<char> { char* allocate(size_t n); void deallocate(char *p, size_t n); }; using CustomString = std::basic_string<char, std::char_traits<char>, MockAllocator>; CustomString str("Hello World........ (more symbols to avoid SSO)");
但上述代码并未调用该分配器的方法,甚至跳过方法定义时链接器也未报错,请问问题出在哪里?
原因分析
1. 非虚函数无法通过继承重写实现多态
std::allocator的allocate和deallocate都是非虚成员函数,C++标准容器(包括std::basic_string)是基于分配器的静态模板类型来调用方法的,不会进行动态多态派发。你继承std::allocator后重写的方法,不会被容器调用——容器实际调用的是基类std::allocator的对应方法,而非你定义的MockAllocator版本。
2. 分配器的rebind机制未正确适配
std::basic_string内部会通过分配器的rebind模板生成适配内部使用的分配器类型。如果只是简单继承std::allocator,rebind返回的可能还是std::allocator的实例,而非你的MockAllocator,导致容器全程使用基类分配器。
3. 链接器未报错的本质
因为你的MockAllocator的allocate和deallocate根本没被调用,所以即便没有实现,链接器也不会寻找这些符号。程序实际调用的是标准库提供的std::allocator的方法,这些方法已有完整实现,自然不会触发链接错误。
修复方案
自定义完整的分配器类型(不依赖继承std::allocator),实现C++标准分配器要求的核心接口:
#include <memory> #include <string> #include <iostream> struct MockAllocator { // 必须定义的value_type类型别名 using value_type = char; char* allocate(size_t n) { std::cout << "MockAllocator: 分配 " << n << " 字节\n"; return static_cast<char*>(std::malloc(n)); } void deallocate(char* p, size_t n) { std::cout << "MockAllocator: 释放 " << n << " 字节\n"; std::free(p); } // 可选:定义rebind以支持容器内部的类型适配 template <typename U> struct rebind { using other = MockAllocator; }; }; // 必须:定义分配器的相等性比较运算符 bool operator==(const MockAllocator&, const MockAllocator&) { return true; } bool operator!=(const MockAllocator&, const MockAllocator&) { return false; } using CustomString = std::basic_string<char, std::char_traits<char>, MockAllocator>; int main() { // 确保字符串长度超过SSO阈值,触发堆内存分配 CustomString str("Hello World......................... (足够长以避免SSO)"); return 0; }
这段代码中,MockAllocator是完全自定义的分配器,符合C++标准分配器的要求,std::basic_string会正确调用它的allocate和deallocate方法,此时如果未实现这些方法,链接器会正常报错。
内容的提问来源于stack exchange,提问作者Dmitry Kuzminov

