2009-05-19 8 views
4

Disons que j'ai une classe:Traiter avec le calcul paresseux dans les classes de C

class NumberCollection 
{ 
public: 
    typedef std::set<int> SetType; 
    typedef SetType::iterator iterator; 
    void insert(int n); 

    iterator begin(); 
    iterator end(); 
    size_t size() const; 

    iterator difficultBegin(); 
    iterator difficultEnd(); 
    size_t difficultSize() const;  

private: 
    SetType easySet_, difficultSet_; 
} 

insert() ajoute un élément à easySet_. Les membres de difficultSet_ changent en fonction des membres de easySet_.

Le problème que j'ai est que, insertions multiples signifie que difficultSet_ est constamment recalculé. Je veux donc que difficultSet_ soit calculé paresseusement (c'est-à-dire seulement lorsque difficultBegin(), difficultEnd(), ou difficultSize() sont appelés). Le problème est, alors je dois effectivement faire difficultSet_ dans un mutable parce que sinon difficultSize() ne peut pas fonctionner sur elle.

Alors maintenant ma déclaration de classe ressemble

class NumberCollection 
{ 
public: 
    typedef std::set<int> SetType; 
    typedef SetType::iterator iterator; 
    void insert(int n); 

    iterator begin(); 
    iterator end(); 
    size_t size() const; 

    iterator difficultBegin(); 
    iterator difficultEnd(); 
    size_t difficultSize() const;  

private: 
    SetType easySet_; 
    mutable SetType difficultSet_; 
    mutable bool upToDate_; 
} 

Je me sens comme cela est une mauvaise conception bien. Y a-t-il un meilleur moyen?

+0

Pourquoi ne vous déclarez méthode difficultSize() avec mot-clé const? Y a-t-il une raison particulière à cela? – Wacek

+0

@Wacek: Parce qu'il devrait être possible pour une méthode qui prend un const & NumberCollection d'appeler difficileSize() (je présume). Mais vous avez raison, vous pouvez "rétrograder" cette méthode à non-const pour rendre le fait qu'elle puisse mettre à jour la représentation en cache plus explicite. –

+0

Il y a des cas où j'ai besoin d'appeler difficileSize() sur une référence const, et logiquement, vérifier la taille ne change pas l'objet. – rlbond

Répondre

11

C'est totalement la façon de le faire. Const peut signifier binaire const, ou cela peut signifier conceptuellement const. En utilisant mutable signifie que vous faites le plus tard, ce qui est bien.

+0

+1. Je crois que "mutable" a été ajouté au langage pour adresser précisément ce cas - de mettre en cache des valeurs "logiquement const". –

+0

Vous avez raison en C++ 98. En C++ 11, vous pouvez envisager de synchroniser l'accès aux données mutables. Pour plus de détails, voir ma réponse. –

3

C'est essentiellement la raison pour laquelle C++ a la construction mutable. Le rant d'Alan De Smet sur l'abus de mutable montre les types de situations dans lesquelles mutable ne devrait pas être utilisé. Dans ce cas, difficileSize() ne change pas ce que NumberCollection représente - ce qui est approprié pour marquer comme const. Il a toujours besoin de changer les internes, ce qui explique pourquoi vous devez marquer difficileSet_et upToDate_ comme mutable.

+0

+1, joli lien. –

4

Pour aider à comprendre pourquoi utiliser mutable, nous pouvons explorer d'autres options.

Vous pouvez résoudre le même problème en utilisant const_cast:

size_t NumberCollection::difficultSize() const 
{ 
    if(!upToDate_) 
    { 
      NumberCollection& nonConst = const_cast<NumberCollection&>(*this); 
      nonConst.difficultSet_ = PerformExpensiveCalculationFunction(); 
      nonConst.upToDate_ = true; 
    } 
    // etc.... 
} 

Après avoir offert cette solution, je vais dire que c'est inférieur à l'aide mutable. Si un membre est marqué mutable, alors simplement en regardant l'en-tête, je peux comprendre comment vous le traitez. Je ne reçois pas cette information si vous utilisez const_cast. Mais alors quelqu'un pourrait prendre l'autre côté du débat, et dire qu'il vaut mieux ne pas exposer les détails de l'implémentation dans l'en-tête.

+0

+1. Oui, il y a toujours n côtés de chaque histoire :) –

1

Votre solution est bonne en C++ 98. Notez que dans C++ 11, vous devriez envisager de synchroniser l'accès à vos données mutables. Sinon, vous risquez de rencontrer des problèmes lorsque votre classe est utilisée par la STL, ce qui suppose que toutes les fonctions membres const sont thread-safe.

Pour plus de détails, voir Does const mean thread-safe in C++11?

Questions connexes