2008-12-16 6 views
5

J'implémente une bibliothèque mathématique en C++. La bibliothèque sera compilée dans une DLL afin que ceux qui l'utilisent n'aient besoin que des fichiers d'en-tête les définitions des classes.Comptage de référence en C++

Les utilisateurs de mes cours seront des personnes qui sont nouvelles dans la langue. Cependant, certains objets peuvent être référencés dans plusieurs parties de leurs programmes. Comme je ne m'attends pas à ce qu'ils fassent la gestion de la mémoire, j'aimerais le faire moi-même. Par conséquent, je dois implémenter le comptage des références (le garbage collection n'est pas une possibilité).

Je veux faire cette référence comptant le plus transparent possible, par exemple ...

// Define a Bézier curve 
CVecList pts; 
pts.Add(Vector(0,0,0)); 
pts.Add(Vector(0,0,100)); 
pts.Add(Vector(0,100,0)); 
pts.Add(Vector(0,100,100)); 
CCurve* c1 = new CBezier(pts); 

// Define a 3rd order B-Spline curve 
pts.Clear(); 
pts.Add(Vector(0,0,0)); 
pts.Add(Vector(0,200,100)); 
pts.Add(Vector(0,200,200)); 
pts.Add(Vector(0,-200,100)); 
pts.Add(Vector(0,-200,200)); 
pts.Add(Vector(0,0,0)); 
CCurve* c2 = new CBSpline(pts,3); 

// The Bézier curve object must be deleted automatically 
// because the only reference to it has been released 
// Similar to IUnknown::Release() in COM 
c1 = c2; 

Les choses deviennent un peu plus compliqué quand je définir des objets de surface, parce que certaines surfaces sont définies en termes de deux courbes:

CVecList pts; 
// ... 
CCurve* f = new CBezier(pts); 

pts.Clear(); 
// ... 
CCurve* g = new CBezier(pts); 

// Mixed surface: S(u,v) = (1-v)*f(u) + v*g(u) 
CSurface* s = new CMixed(f,g); 

// There are two references to the first Bézier curve, 
// the first one is f 
// the second one is hidden in a member of CMixed 

// Something similar applies to the second Bézier curve 

Je pensais que l'annulation de operator = pour les pointeurs aurait pu aider:

// This is what I tried, but it's illegal: 
typedef CReferenceCounted* PRC; 
PRC& operator =(PRC& dest, PRC& source) 
{ 
    if (source) 
     source->AddRef(); 
    if (dest) 
     dest->Release(); 
    memcpy(&dest,&source,sizeof(PRC)); 
    return dest; 
} 

... mais j'ai trouvé que operator = n'est pas valide à moins qu'il ne soit en tant que membre non-statique d'une classe.

Quelqu'un peut-il m'aider?

+0

http://ootips.org/yonat/4dev/smart-pointers.html – derobert

+0

J'ai essayé de garder mon code-modèle libre, mais je ne peux pas trouver une autre solution. Je vous remercie. –

+0

Eduardo pouvez-vous éditer ce message, sélectionnez le code dans votre question, puis cliquez sur le bouton avec 0 et 1? Cela va appliquer la coloration syntaxique au code. –

Répondre

11

Ce que vous avez essayé était de surcharger un opérateur pour les types scalaires. C++ ne vous permet pas de faire cela sauf pour les énumérations (à côté du point que operator = doit être un membre). Au moins un des types doit être un type défini par l'utilisateur. Ainsi, ce que vous voulez faire est d'enrouler le pointeur brut dans une classe définie par l'utilisateur, qui surcharge le constructeur, le constructeur de copie, l'opérateur d'affectation de copie et le destructeur et fait le compte de référence approprié. Ceci est une situation idéale pour boost::shared_ptr, qui fait exactement cela:

boost::shared_ptr<CCurve> c1(new CBezier(pts)); 

la même affaire avec des surfaces:

CVecList pts; 
// ... 
boost::shared_ptr<CCurve> f(new CBezier(pts)); 

pts.Clear(); 
// ... 
boost::shared_ptr<CCurve> g(new CBezier(pts)); 

// Mixed surface: S(u,v) = (1-v)f(u) + vg(u) 
boost::shared_ptr<CSurface> s(new CMixed(f,g)); 

Carry autour de ce pointeur intelligent, et il gère automatiquement la durée de vie de la pointé vers l'objet: Si la dernière copie du pointeur est hors de portée, l'objet pointé est libéré. shared_ptr est conçu pour être facile à utiliser. Essayez d'éviter de travailler avec des pointeurs bruts autant que vous le pouvez. Jetez un oeil à ces pointeurs intelligents, ils faciliteront vos programmeurs en direct avec C++ :)

Modifier: Si vous allez envelopper un shared_ptr, vous pouvez le faire en utilisant l'idiome Pimpl (poignée/corps):

/* ---- wrapper in header file bezier.hpp */ 

struct CBezier { 
    CBezier(CVecList const& list); 
    void do_calc(); 
    // ... 

private: 
    struct CBezierImpl; 
    boost::shared_ptr<CBezierImpl> p; 
}; 

/* ---- implementation file bezier.cpp */ 

// private implementation 
struct CBezier::CBezierImpl { 
    CBezierImpl(CVecList const& list); 
    void do_calc(); 
    // ... 
}; 


CBezier::CBezier(CVecList const& list) 
:p(new CBezierImpl(list)) { 

} 

void CBezier::do_calc() { 
    // delegate to pimpl 
    p->do_calc(); 
} 

// ... 
+0

Je voudrais dpo quelque chose comme définir CCurve comme CCurved_internal, puis typedef CCurve à boost :: shared_ptr ,,, – BubbaT

+0

J'ai essayé de garder mon code-modèle libre, mais je ne peux pas trouver une autre solution. Je vous remercie. –

+0

vous pourriez, si vous retournez utiliser CCurve comme non-pointeur, et allouer la ressource en son sein. et alors CCurve pourrait faire le comptage de référence, supprimant le pointeur géré quand nécessaire. vous pouvez simplement envelopper un shared_ptr et le rendre transparent pour vos élèves. –

0

Je recommande intrusive_ptr au lieu de shared_ptr pour les objets que vous pouvez contrôler pour améliorer les performances et la facilité d'utilisation, que vous pouvez attribuer un pointeur brut à intrusive_ptr plus tard, parce que le nombre de référence est intégré dans l'objet.

1

Si vous concevez une bibliothèque mathématique, passez beaucoup de temps à penser que vos classes peuvent ressembler à int ou à std :: complex. C'est-à-dire que les valeurs se comportent comme des valeurs. Par exemple.

std::vector<math::point3d> pts; 
pts.push_back(math::point3d(0,0,0)); 
pts.push_back(math::point3d(110,0,0)); 
pts.push_back(math::point3d(0,100,0)); 
pts.push_back(math::point3d(0,0,100)); 
CCurve c1 = make_bezier(pts); 
0

Les utilisateurs de mes cours seront des gens qui sont nouveaux dans la langue.

Votre cours a-t-il été conçu pour un cours de programmation?

Si tel est le cas, j'éviter d'utiliser des pointeurs et utiliser uniquement copier constructeurs/rendez-:

  • Performance/mémoire n'est pas une priorité
  • Faire la gestion de la mémoire vous montrera un exemple assez mauvais comment utiliser new/delete
  • L'utilisation de n'importe quel type de pointeur intelligent sans connaissance de la gestion de la mémoire peut entraîner beaucoup de confusion par la suite.
0

Je suis d'accord avec Guishu et MSalters. Même si ce n'est pas pour un cours de programmation, il peut être intéressant d'imiter de plus près les maths (par exemple vector3 = vector1 + vector2 etc).

Ce que vous pouvez aussi faire est d'utiliser copy-on-write (refounting étant une conséquence logique), mais seulement en interne. Cela peut vous donner des devoirs assez rapides, éliminer la gestion du tas sur le côté client et la similarité avec la notation mathématique. Notez cependant qu'il existe des bibliothèques mathématiques disponibles pour C++ (TNT, en tête). Avez-vous envisagé de baser votre travail là-dessus?

Questions connexes