如何在C ++ STL中覆盖allocator类中构造方法的默认行为

时间:2011-11-11 04:53:56

标签: c++ stl

如何在STL的allocator类中覆盖构造方法的默认行为?以下似乎不起作用:

#include <list>
#include <iostream>
#include <memory>

struct MyObj {
    MyObj() {
        std::cout << "This is the constructor" << std::endl;
    }
    MyObj(const MyObj& x) {
        std::cout << "This is the copy constructor" << std::endl;
    }
};

class MyAlloc : public std::allocator <MyObj>{
public:
    void construct(pointer p, const_reference t){
        std::cout << "Construct in the allocator" << std::endl;
        new( (void*)p ) MyObj(t);
    }
};

int main(){
    MyObj x;         
    std::list <MyObj,MyAlloc> list(5,x);
} 

该程序返回

This is the constructor
This is the copy constructor
This is the copy constructor
This is the copy constructor
This is the copy constructor
This is the copy constructor

我希望它返回

This is the constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor

2 个答案:

答案 0 :(得分:11)

欢迎来到精彩的分配器世界。我希望你喜欢你的逗留,尽管这不太可能。

规则#1:不要从std::allocator派生。如果要使用自己的分配方案,则编写自己的分配器。如果你想“覆盖”std :: allocator中的某些功能,那么只需创建一个std::allocator实例并在未覆盖的函数中调用它的函数。

请注意,派生无论如何都不起作用。在C ++ 03中,不允许分配器具有状态,并且v表指针计为状态。因此分配器不能具有虚函数。这就是std::allocator没有虚函数的原因。

规则#2:std::list<T> 从不分配T个对象。请记住:std::list是一个链接列表。它分配节点,其中T作为成员。它通过一些模板魔术来实现,它使用它的内部节点类型作为参数调用你的迭代器类,并返回同一模板的新分配器对象,但使用不同的模板参数。

它通过分配器调用rebind的模板结构成员执行此操作,该成员具有名为other的成员typedef,用于定义新的分配器类型。在您的情况下,std::list会执行此操作:

MyAlloc::rebind<_ListInternalNodeType>::other theAllocatorIWillActuallyUse();

这仍然是由基类提供的。因此MyAlloc::rebind<_ListInternalNodeType>::other的类型为std::allocator<_ListInternalNodeType>。哪个是std::list将用于实际分配内容的分配器类型。

答案 1 :(得分:4)

您必须做的比您在代码中所做的更多。这是使其按照您希望的方式工作所需的最小代码:

template<typename T>
class MyAlloc : public std::allocator <T>
{
public:
     typedef size_t     size_type;
     typedef ptrdiff_t  difference_type;
     typedef T*         pointer;
     typedef const T*   const_pointer;
     typedef T&         reference;
     typedef const T&   const_reference;
     typedef T          value_type;


     template<typename U>
     struct rebind
     {
       typedef MyAlloc <U> other; 
     };

     MyAlloc() {}

     template<typename U>
     MyAlloc(const MyAlloc<U>&) {}

     void construct(pointer p, const_reference t){
        std::cout << "Construct in the allocator" << std::endl;
        new( (void*)p ) MyObj(t);
     }

};

然后将其用作:

   int main(){
    MyObj x;         
    std::list <MyObj,MyAlloc<MyObj> > list(5,x);
}

输出(如您所愿):

This is the constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor
Construct in the allocator
This is the copy constructor

在线演示:http://www.ideone.com/QKdqm

这个最小代码的全部思想是覆盖基类rebindstd::allocator类模板的定义,该模板定义为:

template<typename U>
struct rebind
{
    typedef std::allocator<U> other; 
};

实际上我们需要这个:

template<typename U>
struct rebind
{
    typedef MyAlloc<U> other; 
};

因为最终它是rebind<U>::other用作分配器。

顺便说一下,typedef是将(类型)名称放在派生类范围内所必需的(默认情况下它们不可见,因为MyAlloc现在是类模板)。所以你可以把它写成:

template<typename T>
class MyAlloc : public std::allocator <T>
{
    typedef std::allocator <T> base;
public:
     typedef typename base::size_type        size_type;
     typedef typename base::difference_type  difference_type;
     typedef typename base::pointer          pointer;
     typedef typename base::const_pointer    const_pointer;
     typedef typename base::reference        reference;
     typedef typename base::const_reference  const_reference;
     typedef typename base::value_type       value_type;

     //same as before
 };

结果相同:http://www.ideone.com/LvQhI