ca peut se paufiner en ayant un allocateur specifique pour allouer des blocs de plus grosse taille. C'est d'ailleurs bien ce que doit faire la STL avec les list, set et map non?
Version imprimable
ca peut se paufiner en ayant un allocateur specifique pour allouer des blocs de plus grosse taille. C'est d'ailleurs bien ce que doit faire la STL avec les list, set et map non?
certes, oui la STL peut allouer un gros bloc de memoire, mais list, set et map ont tout de même une autre facon de gérer la mémoire... ce qui est d'ailleurs que nous nous en servons pas sur un système à mémoire limitée (et aucune mémoire virtuelle) comme la Wii.
cf. Kurisu pour la réponse. De manière plus précise, cela dépends de la librairie standard utilisée (elles diffèrent selon les vendeurs de compilateurs). Il y a deux points à vérifier :
- L'allocateur par défaut. La plupart du temps, il alloue N blocs lorsqu'on lui demande d'en allouer N.
- La classe conteneur. La plupart des conteneurs (excepté std::vector<> et peut être un ou deux autres) allouent 1 bloc par 1 bloc (ils appellent my_allocator.allocate(1)). C'est le cas de la librairie standard de MS et de celle d'Intel. Je n'ai pas GCC ici, mais je suis près à parier qui c'est aussi le cas).
Pour changer ce comportement, il suffit de créer un nouvel allocateur. Ce n'est pas très complexe : la seule chose à faire est de se conformer à cette interface:
L'ensemble doit implémenter les fonctions décrites dans la norme C++, section 20.4.1.1 (vous pouvez télécharger le draft de la norme sur le site du WG21).Code:
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 template <class T> class allocator { 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 <class U> struct rebind { typedef allocator<U> other; }; allocator() throw(); allocator(const allocator&) throw(); template <class U> allocator(const allocator<U>&) throw(); allocator() throw(); pointer address(reference x) const; const_pointer address(const_reference x) const; pointer allocate( size_type, allocator<void>::const_pointer hint = 0); void deallocate(pointer p, size_type n); size_type max_size() const throw(); void construct(pointer p, const T& val); void destroy(pointer p); };
(Note_1: la méthode allocator::allocate() prend 2 paramêtres - le second est un 'hint', qui est censé aider l'allocateur a effectuer son travail. La plupart des implémentations que j'ai vérifiée n'utilise pas ce hint, donc inutile de vous ennuyer avec).
(Note_2: attention à rebind et au constructeur qui prends un allocator<U>; rebind est généralement utilisé pour créer un allocateur de U similaire à l'allocateur en cours d'utilisation. Par exemple, la librairie standard de MS l'utilise pour définir l'allocateur des _List_nod d'un objet std::list<T,_Alloc>. L'allocateur de _List_nod est du type _Alloc::rebind<_Node>::other, ou _Node est une structure définie dans _List_nod.)
(Note_3: ne pas oublier non plus de définir les fonctions libres operator==(const std::allocator<U>&, const my_allocator<V>&), operator==(const my_allocator<V>&, const std::allocator<U>&), et operator!=(const std::allocator<U>&, const my_allocator<V>&), operator!=(const my_allocator<V>&, const std::allocator<U>&). Les deux première renvoient systématiquement true, les deux autres renvoient false (cf section 20.4.1.2).
Il est courant d'implémenter des allocator un peu plus intelligent que l'allocator par défaut (notamment, des allocator qui réduisent la fragmentation de la mémoire ou qui allouent un nombre constant d'instances de T au moment de leur initialisation). A moins que je ne me trompe (et c'est quand même fort possible, mine de rien), cela permet l'utilisation des conteneurs de la librairie standard dans le contexte d'une console (y compris sur la Wii).
A noter que boost définit l'allocator 'pool', qui est très similaire dans son fonctionnement.