为啥这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次
Posted
技术标签:
【中文标题】为啥这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次【英文标题】:Why is the destructor of this custom allocator being called twice in GCC/MSVS's stdlib为什么这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次 【发布时间】:2017-08-12 19:21:23 【问题描述】:我一直在尝试编写一个简单的分配器,我已经编写了一个只记录其调用的最小分配器。
当尝试在一些简单的std::vector
操作中使用它时 - 至少在 GCC 和 Visual Studio 上 - 记录的行为看起来很直观,除了在请求所有分配之前似乎调用了析构函数,以及在结尾。在 clang 上一切正常,所以我不确定这是否只是一个编译器问题。
假设这不是编译器错误,那么这个分配器缺少什么;或者我对分配器如何被调用的理解是错误的并且很好?
我有下面的代码作为现场演示here
#include <ios>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using std::size_t;
struct Indexer
static size_t nextId, objectsAlive;
;
size_t Indexer::nextId, Indexer::objectsAlive;
template<typename T>
class DebugAllocator : protected Indexer
static std::string formatPointer(const void* p)
std::ostringstream s;
s << "[93m0x" << std::hex << std::uppercase << uintptr_t(p) << "[0m";
return s.str();
static std::string formatFunctionName(const char* functionName)
return "[96m" + std::string(functionName) + "[0m";
static std::string indentation()
return std::string((objectsAlive + 1) * 4, ' ');
public:
using value_type = T;
using pointer = value_type*;
using size_type = std::make_unsigned_t<typename std::pointer_traits<pointer>::difference_type>;
size_t id;
DebugAllocator() noexcept
: id(nextId++)
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ")\n";
++objectsAlive;
template<typename T_rhs>
DebugAllocator(const DebugAllocator<T_rhs>& rhs) noexcept
: id(nextId++)
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
++objectsAlive;
template<typename T_rhs>
DebugAllocator& operator=(const DebugAllocator<T_rhs>& rhs) noexcept
std::cerr << indentation() << id << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
~DebugAllocator() noexcept
--objectsAlive;
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ")\n";
pointer allocate(size_type n) const
value_type* const p((value_type*) new char[sizeof(value_type) * n]);
std::cerr << indentation() << formatPointer(p) << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << n << ")\n";
return p;
void deallocate(pointer p, size_type n) const noexcept
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << formatPointer(p) << ", " << n << ")\n";
delete[] (value_type*) p;
bool operator==(const DebugAllocator& rhs) const noexcept
std::cerr << indentation() << std::boolalpha << true << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
return true;
bool operator!=(const DebugAllocator& rhs) const noexcept
std::cerr << indentation() << std::boolalpha << false << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
return false;
;
int main()
std::vector<int, DebugAllocator<int>> v3;
v.push_back(1);
v.push_back(2);
v.emplace_back(1);
v.insert(std::begin(v) + 2, 4);
v.erase(std::begin(v) + 3);
std::string separator;
for (int& x : v)
std::cerr << separator << std::move(x);
separator = ", ";
std::cerr << '\n';
GCC / MSVS 日志:
DebugAllocator::DebugAllocator(0)
0xF86C50 = DebugAllocator::allocate(0, 1)
DebugAllocator::~DebugAllocator(0)
0xF86CA0 = DebugAllocator::allocate(0, 2)
DebugAllocator::deallocate(0, 0xF86C50, 1)
0xF86C50 = DebugAllocator::allocate(0, 4)
DebugAllocator::deallocate(0, 0xF86CA0, 2)
0xF86C20 = DebugAllocator::allocate(0, 8)
DebugAllocator::deallocate(0, 0xF86C50, 4)
3, 1, 4, 1
DebugAllocator::deallocate(0, 0xF86C20, 8)
DebugAllocator::~DebugAllocator(0)
叮当日志:
DebugAllocator::DebugAllocator(0)
0xD886F0 = DebugAllocator::allocate(0, 1)
0xD88710 = DebugAllocator::allocate(0, 2)
DebugAllocator::deallocate(0, 0xD886F0, 1)
0xD886F0 = DebugAllocator::allocate(0, 4)
DebugAllocator::deallocate(0, 0xD88710, 2)
0xD88730 = DebugAllocator::allocate(0, 8)
DebugAllocator::deallocate(0, 0xD886F0, 4)
3, 1, 4, 1
DebugAllocator::deallocate(0, 0xD88730, 8)
DebugAllocator::~DebugAllocator(0)
【问题讨论】:
make_unsigned_t 。 xcode 抱怨它 尝试包含<type_traits>
,如果你没有设置那个标志,那是C++14类型
能否将错误代码提取成20行左右的代码?
【参考方案1】:
Apparently
template<typename T_rhs>
DebugAllocator(const DebugAllocator<T_rhs>& rhs)
不算作复制构造函数。因此调用了编译器生成的复制构造函数,而您没有观察到。
【讨论】:
以上是关于为啥这个自定义分配器的析构函数在 GCC/MSVS 的 stdlib 中被调用两次的主要内容,如果未能解决你的问题,请参考以下文章