How to overwrite the default behavor of the construct method in the allocator class in C++ STL

你离开我真会死。 提交于 2019-12-22 18:41:44

问题


How do you overwrite the default behavior of the construct method in the allocator class in STL? The following does not seem to work:

#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 program returns

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

I would like it to return

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

回答1:


Welcome to the wonderful world of allocators. I hope you enjoy your stay, though that's unlikely.

Rule #1: Do not derive from std::allocator. If you want to use your own allocation scheme, then write your own allocator. If you want to "override" some functionality in std::allocator, then simply create a std::allocator instance and call its functions in the un-overridden functions.

Note that deriving doesn't really work anyway. In C++03, allocators aren't allowed to have state, and a v-table pointer counts as state. So allocators can't have virtual functions. Which is why std::allocator doesn't have virtual functions.

Rule #2: std::list<T> never allocates T objects. Remember: std::list is a linked list. It allocates nodes, which have a T as a member. It does this via some template magic, where it calls your iterator class using it's internal node type as a parameter, and that returns a new allocator object of the same template, but with a different template parameter.

It does this via a template struct member of your allocator call rebind, which has a member typedef called other that defines the new allocator type. In your case, std::list will do this:

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

And that is still provided by the base class. So the type of MyAlloc::rebind<_ListInternalNodeType>::other is std::allocator<_ListInternalNodeType>. Which is the allocator type that std::list will use to actually allocate things.




回答2:


You have to do a little more than what you're doing in your code. This is the minimal code which is needed in order to make it work the way you want it to:

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);
     }

};

And then use it as:

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

Output (as you desire):

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

Online demo : http://www.ideone.com/QKdqm

The whole idea of this minimal code is to override the definition of rebind class template in the base class std::allocator which is defined as:

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

when in fact we need this:

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

Because eventually it is rebind<U>::other which is used as allocator.

By the way, the typedefs are necessary to bring the names (of types) in the scope of derived class (by default they're not visible as MyAlloc now is a class template). So you could write that as:

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
 };

The result would be same : http://www.ideone.com/LvQhI



来源:https://stackoverflow.com/questions/8089850/how-to-overwrite-the-default-behavor-of-the-construct-method-in-the-allocator-cl

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!