Работа с ленивыми вычислениями в классах C ++ - PullRequest
4 голосов
/ 19 мая 2009

Допустим, у меня есть класс:

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_;
}

Хотя я чувствую, что это плохой дизайн. Есть ли лучший способ?

Ответы [ 4 ]

11 голосов
/ 19 мая 2009

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

4 голосов
/ 19 мая 2009

Чтобы понять, почему нужно использовать 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 .

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

3 голосов
/ 19 мая 2009

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

В этом случае трудный размер () не изменяет то, что представляет NumberCollection, что подходит для маркировки как const. Это делает все, что нужно, время от времени менять внутренние компоненты, поэтому вам нужно пометить HardSet_ и UpToDate_ как изменяемые.

1 голос
/ 04 января 2013

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

Подробнее см. Означает ли const потокобезопасность в C ++ 11?

...