问题
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