Trattare con calcolo pigrizia classi C ++
-
22-08-2019 - |
Domanda
Diciamo che ho una classe:
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_;
}
Dove insert()
aggiunge un elemento alla easySet_
. i membri di difficultSet_
cambiano a seconda dei membri del easySet_
.
Il problema che sto avendo è che, più inserimenti significa che difficultSet_
viene costantemente ricalcolato. Quindi voglio difficultSet_
da calcolare pigramente (vale a dire, solo quando difficultBegin()
, difficultEnd()
o difficultSize()
sono chiamati). Il problema è, quindi io in realtà deve fare difficultSet_
in un mutable
perché altrimenti difficultSize()
non può operare su di esso.
Così ora la mia dichiarazione di classe appare come
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_;
}
Mi sento come se questa è la cattiva progettazione però. C'è un modo migliore?
Soluzione
Questo è totalmente il modo per farlo. Const può significare const binario, o può significare const concettualmente. Utilizzando mutabili significa che stai facendo più tardi, che va bene.
Altri suggerimenti
Per aiutare a capire perché utilizzare mutevole, siamo in grado di esplorare altre opzioni.
È possibile risolvere lo stesso problema usando const_cast :
size_t NumberCollection::difficultSize() const
{
if(!upToDate_)
{
NumberCollection& nonConst = const_cast<NumberCollection&>(*this);
nonConst.difficultSet_ = PerformExpensiveCalculationFunction();
nonConst.upToDate_ = true;
}
// etc....
}
Dopo aver offerto questa soluzione, io dico che è inferiore a usando mutevole . Se un membro è contrassegnato come mutevole , poi semplicemente guardando l'intestazione ho potuto capire come si sta trattando. Non capisco queste informazioni se si utilizza const_cast .
Ma allora qualcuno potrebbe prendere l'altro lato del dibattito, e dire che è meglio non esporre i dettagli di implementazione nell'intestazione.
Questo è essenzialmente il motivo C ++ ha il costrutto mutevole. di sproloquio circa l'abuso di mutabile Alan De Smet mostra i tipi di situazioni in cui mutevole non dovrebbe essere usato.
In questo caso, difficultSize () non cambia ciò che il NumberCollection rappresenta - che è adatto per la marcatura come const. Lo fa come mai bisogno di cambiare la struttura interna, a volte, è per questo che è necessario marcare difficultSet_ e upToDate_ come mutevole.
La soluzione va bene in C ++ 98. Si noti che in C ++ 11 si dovrebbe considerare di sincronizzare l'accesso ai propri dati mutabili. In caso contrario, si può incorrere in problemi quando si la classe è utilizzata dal STL, che presuppone che tutte le funzioni membro const sono thread-safe.
Per ulteriori informazioni, vedere Ha const significa thread-safe in C ++ 11?