假设我有一个班级:
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()
在easySet_
添加元素的位置。 difficultSet_
的成员会根据easySet_
的成员而发生变化。
我遇到的问题是,多次插入意味着difficultSet_
会不断重新计算。所以我希望懒惰地计算difficultSet_
(即,只有在调用difficultBegin()
,difficultEnd()
或difficultSize()
时才会这样做。问题是,我实际上必须将difficultSet_
变为mutable
,否则difficultSize()
无法对其进行操作。
所以现在我的类声明看起来像
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_;
}
我觉得这是糟糕的设计。还有更好的方法吗?
答案 0 :(得分:11)
这完全是做到这一点的方式。 Const可以表示二进制const,或者它可以表示概念上的const。使用可变意味着你要做的更晚,这很好。
答案 1 :(得分:4)
为了帮助理解使用mutable的原因,我们可以探索其他选项。
您可以使用 const_cast 解决同样的问题:
size_t NumberCollection::difficultSize() const
{
if(!upToDate_)
{
NumberCollection& nonConst = const_cast<NumberCollection&>(*this);
nonConst.difficultSet_ = PerformExpensiveCalculationFunction();
nonConst.upToDate_ = true;
}
// etc....
}
提供此解决方案之后,我会说它不如使用 mutable 。如果一个成员被标记为 mutable ,那么只需查看标题我就可以收集你如何处理它。如果您使用 const_cast ,我不会收到此信息。
但是有人可能会接受辩论的另一面,并说最好不要在标题中公开实现细节。
答案 2 :(得分:3)
这基本上是C ++具有可变构造的原因。 Alan De Smet关于滥用mutable的rant显示了不应使用mutable的各种情况。
在这种情况下,difficultySize()不会更改NumberCollection所代表的内容 - 这适用于标记为const。它确实需要有时更改内部,这就是为什么你需要将difficultSet_和upToDate_标记为可变的。
答案 3 :(得分:1)
您的解决方案在C ++ 98中很好。请注意,在C ++ 11中,您应该考虑同步对可变数据的访问。否则,当您的类被STL使用时,您可能会遇到问题,这假定所有const成员函数都是线程安全的。
有关详细信息,请参阅Does const mean thread-safe in C++11?