34 votes

opérateur nouvelle surcharge et alignement

Je suis surcharger operator new, mais j'ai récemment frappé un problème avec l'alignement. En gros, j'ai une classe IBase qui fournit operator new et delete dans tous les requis variantes. Toutes les classes dérivent d' IBase , et donc également utiliser l'outil de allocateurs.

Le problème que je me pose maintenant, c'est que j'ai un enfant Foo qui doit être de 16 octets alignés, tandis que tous les autres sont beaux lorsqu'ils sont alignés à 8 octets. Mon allocateur de mémoire cependant aligne 8-les limites d'octets que par défaut, de sorte que maintenant le code en IBase::operator new renvoie un inutilisables morceau de mémoire. Comment est-ce censé être résolu correctement?

Je peux simplement de la force de toutes les allocations de 16 octets, qui fonctionnera bien jusqu'à 32 octets aligné type pop-up. Déterminer l'alignement à l'intérieur d' operator new ne semble pas être trivial (puis-je faire un appel de fonction virtuelle là pour obtenir l'alignement?) Quelle est la méthode recommandée pour gérer cela?

Je connais malloc est censé renvoyer un morceau de la mémoire qui est correctement alignés pour tout, malheureusement, ce "tout" ne comprend pas l'ESS types et je voudrais vraiment obtenir ce travail sans demander à l'utilisateur de se rappeler le type a qui l'alignement.

21voto

Johannes Schaub - litb Points 256113

C'est une solution possible. Il aura toujours le choix de l'opérateur avec le plus grand alignement dans une hiérarchie donnée:

#include <exception>
#include <iostream>
#include <cstdlib>

// provides operators for any alignment >= 4 bytes
template<int Alignment>
struct DeAllocator;

template<int Alignment>
struct DeAllocator : virtual DeAllocator<Alignment/2> {
  void *operator new(size_t s) throw (std::bad_alloc) {
    std::cerr << "alignment: " << Alignment << "\n";
    return ::operator new(s);
  }

  void operator delete(void *p) {
    ::operator delete(p);
  }
};

template<>
struct DeAllocator<2> { };

// ........... Test .............
// different classes needing different alignments
struct Align8 : virtual DeAllocator<8> { };
struct Align16 : Align8, virtual DeAllocator<16> { };
struct DontCare : Align16, virtual DeAllocator<4> { };

int main() {
  delete new Align8;   // alignment: 8
  delete new Align16;  // alignment: 16
  delete new DontCare; // alignment: 16
}

Il est basé sur la domination de la règle: Si il y a une ambiguïté dans la recherche, et de l'ambiguïté est entre les noms de dérivée et d'une classe de base virtuelle, le nom de la classe dérivée est pris à la place.


Des Questions ont été ressuscité pourquoi DeAllocator<I> hérite DeAllocator<I / 2>. La réponse est parce que dans une hiérarchie donnée, il peut être différent de l'alignement des exigences imposées par les classes. Imaginez qu' IBase n'a pas d'exigences alignement, A a 8 octets d'exigence et d' B a 16 octets exigence et hérite A:

class IBAse { };
class A : IBase, Alignment<8> { };
class B : A, Alignment<16> { };

Alignment<16> et Alignment<8> la fois d'exposer un operator new. Maintenant, si vous dites new B, le compilateur va chercher operator new en B et de trouver les deux fonctions:

            // op new
            Alignment<8>      IBase
                 ^            /
                  \         /
                    \     /
 // op new            \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

B ->      Alignment<16>  -> operator new
B -> A -> Alignment<8> -> operator new

Ainsi, ce serait ambiguë et nous n'aurions pas réussi à compiler: Aucune de ces masquer l'autre. Mais si vous héritez Alignment<16> pratiquement Alignment<8> et faire A et B hériter virtuellement, à l' operator new en Alignment<8> sera cachée:

            // op new
            Alignment<8>      IBase
                 ^            /
                / \         /
              /     \     /
 // op new  /         \ /
 Alignment<16>         A
            \         /
              \     /
                \ /
                 B 

Cette règle de masquage spécial (appelé aussi la domination de la règle) mais ne fonctionne que si tous Alignment<8> des objets sont les mêmes. Ainsi, nous héritent toujours pratiquement: Dans ce cas, il n'existe qu' un seul Alignment<8> (ou 16, ...) de l'objet existant dans toute la hiérarchie de classes.

7voto

Lance Diduck Points 685

mixin sont la bonne approche, cependant, la surcharge de l'opérateur new n'est pas. Cela permettra d'accomplir ce que vous avez besoin de:

__declspec(align(256))  struct cachealign{};
__declspec(align(4096)) struct pagealign{};
struct DefaultAlign{};
struct CacheAlign:private cachealign{};
struct PageAlign: CacheAlign,private pagealign{};

void foo(){
 DefaultAlign d;
 CacheAlign c;
 PageAlign p;
 std::cout<<"Alignment of d "<<__alignof(d)<<std::endl;
 std::cout<<"Alignment of c "<<__alignof(c)<<std::endl;
 std::cout<<"Alignment of p "<<__alignof(p)<<std::endl;
}

Imprime

Alignment of d 1
Alignment of c 256
Alignment of p 4096

Pour gcc, l'utilisation

struct cachealign{}__attribute__ ((aligned (256)));

Notez qu'il y a sélection automatique de la plus grande alignement, et cela fonctionne pour les objets placés sur la pile, ceux qui sont nouveaux avais, et que les membres des autres classes. Ni le fait d'ajouter de tout virtuals et en supposant EBCO, pas de supplément de la taille de la classe (en dehors de la marge nécessaire pour l'alignement lui-même).

1voto

RJVB Points 11

À l'aide de Visual Studio Express 2010, l'exemple ci-dessus ne semble pas fonctionner avec les nouvelles:

CacheAlign *c = new CacheAlign;

donnera __aligner_de(c) == 4 (à prévoir je présume), mais l'adresse d'un premier membre de CacheAlign n'est pas aligné comme demandé non plus.

Pas d'une récente question, mais si je comprends l'OP correctement, il a des classes qui sont des enfants d'une classe parent qui définissent l'allocateur & deallocator, et peuvent exiger l'alignement. Quel est le problème avec le fait d'avoir une simple nouvelle qui appelle certaines privé allocateur qui fait tout le boulot et reçoit un alignement de l'argument - une version générique avec alignement par défaut dans la classe parent qui est hérité ou surchargé, avec une version qui spécifie l'alignement correct?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X