2010-08-22 23 views
1

Il s'agit d'une question de conception, en supposant C++ et une hiérarchie d'objets comptés de référence. Un grand nombre de classes dans mon code dérivent d'une classe de base commune (ObjectBase), qui implémente des méthodes retain() et release() pour augmenter et diminuer le nombre de références d'une instance d'objet.Objets comptés de référence et multiplicateurs

Chaque instance d'un objet peut être créée sur la pile ou sur le tas, en utilisant un certain nombre d'allocateurs de mémoire définissables par l'utilisateur. Pour que l'instance d'objet se suicide (supprimez ceci) dans la méthode release() si la propriété retainCount atteint 0, l'instance doit connaître l'allocateur avec lequel elle a été construite.

Actuellement, j'alloue de la mémoire pour une instance d'objet en utilisant un allocateur arbitraire, puis appelle placement new pour construire l'instance d'objet et appelle une méthode setAllocator() sur l'objet pour définir l'allocateur avec lequel elle a été créée. Si l'objet a été construit sur la pile, l'allocateur est défini sur NULL et release() n'appelle pas delete. Ce processus est très redondant et potentiellement sujette aux erreurs (fuites de mémoire, si j'oublie d'appeler setAllocator, etc ...) Idéalement, je voudrais faire une seule étape comme ceci:

Object* o = myPoolAllocator.allocate<Object>(constructor arguments...); 

Mais cela fait il est très difficile de soutenir et le nombre arbitraire d'arguments constructeur.

Je cherche juste des idées sur la façon de résoudre ce problème. J'aime vraiment l'idée de pouvoir référencer des objets sans avoir à compter sur un pointeur intelligent, d'autant plus que la plupart des classes dérivent d'une base commune, de toute façon.

Merci pour votre aide.

Florian

Répondre

1

Jetez un oeil à cet article: Overloading New in C++. Vous pouvez surcharger l'opérateur new pour ObjectBase pour qu'il prenne votre allocateur en tant que paramètre et fait le reste du travail:

void *ObjectBase::operator new(size_t size, Allocator *allocator) { 
    void *ptr = allocator->allocate(size); 

    // Hack to pre-initialize member before constructor is called 
    ObjectBase *obj = static_cast<ObjectBase *>(ptr); 
    obj->setAllocator(allocator); 

    return ptr; 
} 

Normalement, l'opérateur est censé revenir juste un pointeur vers la mémoire allouée, mais depuis vous avez besoin d'accéder au nouvel objet pour appeler votre méthode setAllocator, j'ai inclus un hack qui devrait (mais ne peut pas) fonctionner. Notez que le constructeur ObjectBase réel est appelé après le retour de la fonction ci-dessus, vous devez donc vous assurer que votre constructeur ne réinitialise pas le membre allocateur.

Et puis une surcharge similaire pour delete:

void ObjectBase::operator delete(void *ptr) { 
    ObjectBase *obj = static_cast<ObjectBase *>(ptr); 
    obj->getAllocator()->free(ptr); 
} 

Vous pouvez ensuite créer des objets en appelant new (allocator) SomeClass(...)SomeClass dérive de ObjectBase.

Edit: Un problème potentiel avec ceci est que vous ne pouvez pas affecter des objets sur la pile plus, parce qu'il n'y a aucun moyen d'initialiser le allocateur à NULL sans affecter la façon dont les new surchargées fonctionne.

Mise à jour: Il y a un dernier hack (sale) pour le faire fonctionner avec la pile et l'allocation dynamique. Vous pouvez faire en sorte que new définisse une variable globale (un membre statique de classe fonctionnerait également) pointant vers l'allocateur actuel et le constructeur pourrait le consommer et le réinitialiser à NULL.Dans tous les autres cas, ce global sera déjà NULL donc un objet construit sur la pile recevra un allocateur NULL.

Allocator *currentAllocator = NULL; 

void *ObjectBase::operator new(size_t size, Allocator *allocator) { 
    currentAllocator = allocator; 
    return allocator->allocate(size); 
} 

ObjectBase::ObjectBase() { 
    setAllocator(currentAllocator); 
    currentAllocator = NULL; 
} 
+0

casablanca, merci pour votre réponse rapide et merci pour la fixation de la balise source dans mon OT! J'ai considéré une méthode similaire à celle que vous suggérez, mais comme vous l'avez noté correctement, je ne serai plus capable de créer des objets sur la pile. J'ai pensé à vérifier dans le constructeur ObjectBase, si le champ d'allocateur pointe vers une instance d'allocateur valide et si ce n'est pas le cas, en considérant l'objet à être sur la pile. Cependant, il s'agit d'une activité dangereuse, étant donné qu'il existe certaines (bien que petites) chances que le champ allocateur non initialisé pointe vers un allocateur valide. – FlorianZ

+0

@FlorianZ: Voir ma réponse mise à jour pour encore un autre hack possible. – casablanca

+0

@casablanca. C'est une très bonne idée aussi! Ma seule préoccupation à ce sujet est que ce n'est probablement pas thread-safe. Que se passe-t-il si un changement de contexte se produit après le retour de l'opérateur, mais avant que le constructeur n'ait une chance de consommer la variable currentAllocator. Si les allocations sont ensuite exécutées dans le deuxième thread, currentAllocator serait alors compromis. Des idées sur la façon de rendre votre solution sûre pour les threads? – FlorianZ