2009-05-19 2 views
4

Допустим, у меня есть класс:Работа с ленивым вычисления в классах 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() добавляет элемент 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

Почему вы объявляете метод difficultSize() с константным ключевым словом? Есть ли для этого особая причина? – Wacek

+0

@Wacek: Потому что это должно быть возможным для метода, который принимает параметр NumberCollection const &, чтобы вызвать трудныйSize() (я полагаю). Но вы правы, вы можете «понизить» этот метод до неконстантного, чтобы тот факт, что он может обновить кэшированное представление более явным. –

+0

Бывают случаи, когда мне нужно вызвать трудноеСистему() для ссылки на константу, и логически проверка размера не изменяет объект. – rlbond

ответ

11

Это полностью способ сделать это. Const может означать двоичную константу, или это может означать концептуально const. Используя изменчивые средства, которые вы делаете позже, это нормально.

+0

+1. Я считаю, что «mutable» был добавлен к языку для решения именно этого случая - кеширования «логически const» значений. –

+0

Вы прямо на C++ 98. В C++ 11 вы можете рассмотреть возможность синхронизации доступа к изменяемым данным. Более подробно см. Мой ответ. –

3

Это по существу причина, по которой C++ имеет изменяемую конструкцию. Алан Де Смет rant о неправильном использовании mutable показывает виды ситуаций, в которых mutable не должен использоваться.

В этом случае трудныйSize() не изменяет то, что представляет NumberCollection, - которое подходит для маркировки как const. Он как-то должен время от времени менять внутренности, поэтому вам нужно пометить функции трудныхSet_ и upToDate_ как изменчивые.

+0

+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.... 
} 

Предложив это решение, я скажу, что это хуже, используя изменяемый. Если элемент помечен как измененный, то просто взглянув на заголовок, я могу узнать, как вы его обрабатываете. Я не получаю эту информацию, если вы используете const_cast.

Но тогда кто-то может принять другую сторону дискуссии и сказать, что лучше не раскрывать детали реализации в заголовке.

+0

+1. Да, в каждой истории всегда есть n сторон :) –

1

Ваше решение прекрасно в C++ 98. Обратите внимание, что в C++ 11 вы должны синхронизировать доступ к вашим изменяемым данным. В противном случае вы можете столкнуться с проблемами, когда ваш класс используется STL, который предполагает, что все функции-члены-члены являются потокобезопасными.

Для получения более подробной информации см Does const mean thread-safe in C++11?