首页 > 解决方案 > 奇怪的 c++ 自定义分配器不匹配的析构函数调用

问题描述

当我跟踪对 STL 容器使用的自定义分配器类的成员函数的调用时,我注意到一些奇怪的行为:对我的分配器类的析构函数的调用与任何早期的构造函数调用都不匹配。这怎么可能?

这是我的代码:

/*
  g++ -pedantic -W -Wall -Werror screwy_allocator_example.cc -o screwy_allocator_example
*/
#include <iostream>
#include <vector>

namespace {

template<class T>
class MyAllocator {
 public:
  using value_type = T;

  template<typename U> struct rebind { using other = MyAllocator<U>; };

  MyAllocator() {
    std::cout << "    in MyAllocator ctor(this="<<this<<")" << std::endl;
    std::cout << "    out MyAllocator ctor(this="<<this<<")" << std::endl;
  }
  ~MyAllocator() {
    std::cout << "    in MyAllocator dtor(this="<<this<<")" << std::endl;
    std::cout << "    out MyAllocator dtor(this="<<this<<")" << std::endl;
  }

  template<typename From>
  MyAllocator(const From &from) {
    std::cout << "    in MyAllocator copy ctor("
              << "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
    std::cout << "    out MyAllocator copy ctor("
              << "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
  }
  template<typename From>
  MyAllocator &operator=(const From &from) {
    std::cout << "    in MyAllocator operator=("
              << "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
    std::cout << "    out MyAllocator operator=("
              << "this="<<this<<", &from="<<(void*)&from<<")" << std::endl;
    return *this;
  }

  T *allocate(size_t n) {
    std::cout << "    in MyAllocator::allocate("
              << "this="<<this<<", n="<<n<<")" << std::endl;
    // assume n*sizeof(T) doesn't overflow
    T *answer = (T*)std::malloc(n * sizeof(T));
    // assume answer!=nullptr
    std::cout << "    out MyAllocator::allocate(this="<<this<<", n="<<n<<")"
              << ", returning "<<(void*)answer << std::endl;
    return answer;
  }

  void deallocate(T *p, size_t n) {
    std::cout << "    in MyAllocator::deallocate("
              << "this="<<this<<", p="<<(void*)p<<", n="<<n<<")" << std::endl;
    std::free(p);
    std::cout << "    out MyAllocator::deallocate("
              << "this="<<this<<", p="<<(void*)p<<", n="<<n<<")" << std::endl;
  }

  template<typename U> bool operator==(const MyAllocator<U> &) const
  { return true; }
  template<typename U> bool operator!=(const MyAllocator<U> &) const
  { return false; }
};  // class MyAllocator<T>

}  // namespace

int main(const int, char**const) {
  std::cout << "in main" << std::endl;
  {
    std::cout << "  constructing my_allocator" << std::endl;
    MyAllocator<double> my_allocator;
    std::cout << "  constructed my_allocator" << std::endl;
    {
      std::cout << "  constructing v(my_allocator)" << std::endl;
      std::vector<double, MyAllocator<double>> v(my_allocator);
      std::cout << "  constructed v(my_allocator)" << std::endl;
      std::cout << "  pushing one item onto v" << std::endl;
      v.push_back(3.14);
      std::cout << "  pushed one item onto v" << std::endl;
      std::cout << "  destructing v(my_allocator)" << std::endl;
    }
    std::cout << "  destructed v(my_allocator)" << std::endl;
    std::cout << "  destructing my_allocator" << std::endl;
  }
  std::cout << "  destructed my_allocator" << std::endl;
  std::cout << "out main" << std::endl;
  return 0;
}

这是输出(-std=c++11、-std=c++14、-std=c++17、-std=c++2a 相同):

in main
  constructing my_allocator
    in MyAllocator ctor(this=0x7ffe4cee5747)
    out MyAllocator ctor(this=0x7ffe4cee5747)
  constructed my_allocator
  constructing v(my_allocator)
  constructed v(my_allocator)
  pushing one item onto v
    in MyAllocator::allocate(this=0x7ffe4cee5720, n=1)
    out MyAllocator::allocate(this=0x7ffe4cee5720, n=1), returning 0x55890a41d2c0
  pushed one item onto v
  destructing v(my_allocator)
    in MyAllocator::deallocate(this=0x7ffe4cee5720, p=0x55890a41d2c0, n=1)
    out MyAllocator::deallocate(this=0x7ffe4cee5720, p=0x55890a41d2c0, n=1)
    in MyAllocator dtor(this=0x7ffe4cee5720)
    out MyAllocator dtor(this=0x7ffe4cee5720)
  destructed v(my_allocator)
  destructing my_allocator
    in MyAllocator dtor(this=0x7ffe4cee5747)
    out MyAllocator dtor(this=0x7ffe4cee5747)
  destructed my_allocator
out main

请注意,对 dtor 进行了两次调用,但对 ctor 进行了一次调用:

这里发生了什么?

似乎最简单的解释是我只是忘记了编译器为我生成的某种构造函数。是这样吗?

如果没有,这里有一些其他的想法。

我知道在 c++11 之前,分配器对象必须是“无状态的”。我不确定这到底是什么意思,但也许可以说这意味着,在 c++11 之前,编译器在玩游戏时是合理的,比如制作分配器对象的字节副本,跳过构造函数调用?

但是,无论如何,在 c++11 及更高版本中,分配器应该被允许有状态,对吧?鉴于此,我看不出跳过构造函数调用有何意义。

鉴于这种奇怪的行为,在我看来,我别无选择,只能使用 c++11 之前的限制来实现我的分配器:也就是说,分配器必须是其他资源的无状态句柄。这就是我正在做的,成功的,但我想了解为什么这是必要的。

标签: c++stl

解决方案


推荐阅读