Question about custom allocator?

I want to see how the STL containers manage their memory. So I write an Allocator that inherited from the std::allocator. All the funcs just print a message and then call those version of BaseType. Like this:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <memory>


template <typename T>
class MyAllocator : public std::allocator<T> {
	typedef typename std::allocator<T> 			BaseType;
	typedef typename BaseType::size_type		size_type;
	typedef typename BaseType::pointer			pointer;
public:
	MyAllocator()
	{ printf( "MyAllocator Constructor.\n" );}
	MyAllocator( const MyAllocator &rhs )
	{printf( "MyAllocator Copy Constructor.\n" );}
	~MyAllocator()
	{ printf( "MyAllocator Destructor.\n" );}

	pointer allocate( size_type _Count, const void* _Hint )
	{
		printf( "MyAllocator::allocate() count = %u, hint = %x\n", _Count, _Hint );
		return BaseType::allocate( _Count, _Hint );
	}
	
	void deallocate( pointer _Ptr, size_type _Count	)
	{
		printf( "MyAllocator::deallocate() ptr = %x, count = %u\n", _Ptr, _Count );
		BaseType::deallocate( _Ptr, _Count );
	}	
	
	void construct( pointer _Ptr, const T &_Val )
	{
		printf( "MyAllocator::construct() ptr = %x\n", _Ptr );
		BaseType::construct( _Ptr, _Val );
	}

	void destroy( pointer _Ptr )
	{
		printf( "MyAllocator::destroy() ptr = %x\n", _Ptr );
		BaseType::destroy( _Ptr );
	}
};


struct Test {
	Test()
	{ printf( "Test Constructor.\n" ); }
	
	Test( const Test &rhs )
	{ printf( "Test Copy Constructor.\n" ); }	
	
	~Test()
	{ printf( "Test Destructor.\n" ); }	
	
	int x;
};

class MyString : public std::basic_string< char, std::char_traits<char>, MyAllocator<char> > {

};

template <typename T>
class MyVector : public std::vector< T, MyAllocator<T> > {

};

int main()
{	
	// MyVector test
	{
		MyVector<int> vec;
		printf( "test\n" );
		vec.reserve(100);
		vec.push_back(1);
		vec.push_back(2);
		printf( "%u, %u\n", vec.size(), vec.capacity() );
	}

	// MyString test
	{
		MyString s;
		s.append("Hello, world!" );
	}
	
    return 0;
} 


However, the output is very strange, I cannot see any output of the funcs in MyAllocator except the constructor and destructor. Like this:


MyAllocator Constructor.
MyAllocator Copy Constructor.
MyAllocator Destructor.
MyAllocator Destructor.
2, 100


I think that when "reserve" or "push_back" is called, MyAllocator::allocate() should be invoked, but I cannot see any message printed by this func. I'm totally confused! That's Why?
You're missing some important code for defining your own allocator. For example you need to declare the rebind structure so that your allocator is actually used and some other stuff:
1
2
3
4
5
6
7
8
    template <class U> 
    struct rebind { typedef MyAllocator<U> other; };

    template <class U>
    MyAllocator(const MyAllocator<U>&) {}

    template <class U>
    MyAllocator& operator=(const MyAllocator<U>&) { return *this; }


You might want to read more about creating your own allocators. For example, here: http://www.roguewave.com/portals/0/products/sourcepro/docs/11.1/html/toolsug/11-6.html
This thread may be relevant:
http://www.cplusplus.com/forum/general/94147/
The problems begin at "So I write an Allocator that inherited from the std::allocator". Does it have any virtual member functions? Its destructor isn't even virtual (but is public). This is clear indication that the class is not intended to be inherited from.

If you want to provide a wrapper around std::allocator, then make it a member of your class, not a base. (and then the compiler will dutifully remind you about the forgotten rebind, unless of course you have C++11 where it's optional)
Last edited on
My theory is that they use memory pools, with a sector for each data element. When you reserve or resize they will do nothing or double the size of the pool. At least thats how I'm thinking about implementing my versions in my library.

Where each sector is an array of bytes of the size of the datatype and the pool size is the number of elements you want to reserve.
also what is the purpose of rebind, Ive been seeing it randomly.
rebind converts an allocator for one type to an allocator for another type.

GNU std::string, for example, uses rebind to get two allocators out of the one you provided: one for char (_Raw_bytes_alloc) and another for the element type of the string (_CharT_alloc_type). They are all the same in your case, but if you inherit from std::allocator and don't provide your own rebind, the base class rebind will be used and it will tell std::string that _Raw_bytes_alloc is std::allocator<char> and that _CharT_alloc_type is std::allocator<char>, so your allocator will never be used.

(just noticed you're also inheriting from std::string and from std::vector.. those classes aren't supposed to be base classes either)
Topic archived. No new replies allowed.