RAII: инициализация элемента данных в методе const - PullRequest
3 голосов
/ 19 марта 2010

В RAII ресурсы не инициализируются до тех пор, пока к ним не будет получен доступ. Однако многие методы доступа объявлены постоянными. Мне нужно вызвать mutable (неконстантную) функцию для инициализации члена данных.

Пример: загрузка из базы данных

struct MyClass
{
  int get_value(void) const;

  private:
     void  load_from_database(void); // Loads the data member from database.

     int m_value;
};

int
MyClass ::
get_value(void) const
{
  static bool value_initialized(false);
  if (!value_initialized)
  {
    // The compiler complains about this call because
    // the method is non-const and called from a const
    // method.
    load_from_database();
  }
  return m_value;
}

Мое примитивное решение - объявить элемент данных как mutable. Я бы предпочел не делать этого, потому что он предполагает, что другие методы могут изменить член.

Как мне привести оператор load_from_database(), чтобы избавиться от ошибок компилятора?

Ответы [ 6 ]

20 голосов
/ 19 марта 2010

Это не RAII. В RAII вы бы инициализировали его в конструкторе, который решит ваши проблемы.

Итак, вы используете здесь Lazy. Будь то ленивая инициализация или ленивые вычисления.

Если вы не используете mutable, вы попадете в мир боли.

Конечно, вы можете использовать const_cast, но что, если кто-то сделает:

static const MyClass Examplar;

И компилятор решает, что он является хорошим кандидатом на использование памяти только для чтения? Ну, в этом случае эффекты const_cast не определены. В лучшем случае ничего не происходит.

Если вы все еще хотите следовать по маршруту const_cast, сделайте это как R Samuel Klatchko do.

Если вы продумали и думаете, что, вероятно, есть лучшая альтернатива, вы можете решить обернуть свою переменную. Если бы он был в своем классе, используя только 3 метода: get, set и load_from_database, вы бы не беспокоились о том, что это mutable.

5 голосов
/ 19 марта 2010

Как уже говорил Матье, то, что вы пытаетесь здесь сделать, имеет мало (если вообще что-то) общего с RAII. Также я сомневаюсь, что любая комбинация const и mutable действительно поможет. const и mutable изменяют тип и применяются в равной степени к всем доступу к объекту этого типа.

То, что вы, похоже, хотите, чтобы небольшой объем кода имел доступ для записи, а все остальное - только чтение для значения. Учитывая базовый дизайн C ++ (и большинства похожих языков), правильный способ сделать это - переместить переменную в собственный класс с небольшим объемом кода, которому необходим доступ для записи как части (или, возможно, друга). ) этот класс. Остальному миру предоставляется доступ только для чтения через интерфейс класса (т. Е. Функция-член, которая получает значение).

(предположительно урезанный) MyClass вы опубликовали довольно близко к праву - вам просто нужно использовать это само по себе, а не как часть большого класса с большим количеством других членов. Основные вещи, которые нужно изменить: 1) имя с MyClass на что-то вроде lazy_int и 2) (по крайней мере, по моим предпочтениям) get_value(), вероятно, следует переименовать в operator int(). Да, m_value, вероятно, должно быть изменчивым, но это не позволяет другому коду записывать значение просто потому, что другой код вообще не имеет доступа к самому значению.

Затем вы встраиваете объект такого типа в свой больший класс. Код в этом внешнем классе может обрабатывать его как int (только для чтения) благодаря его operator int(), но не может писать его просто потому, что класс не дает никакого способа сделать это.

5 голосов
/ 19 марта 2010

Вы в основном реализуете механизм кэширования. Лично я думаю, что можно пометить кэшированные данные как изменяемые.

4 голосов
/ 19 марта 2010

[СМОТРИТЕ МА! НЕТ СЛУЧАЕВ! :))]

struct DBValue 
{
  int get_value();

private:
  void load_from_database();
  int value;
};

struct MyClass 
{
  MyClass(): db_value(new DBValue()) {}
  ~MyClass() { delete db_value; } 

  int get_value() const;

private:
  DBValue * const db_value;
};

int MyClass::get_value() const
{
  return db_value->get_value(); // calls void load_from_database() if needed
}

Идея состоит в том, чтобы иметь политкорректные MyClass с const методами , не изменяющими ничего, а вызывающими как const, так и не const методы агрегированных объектов через константные указатели .

1 голос
/ 19 марта 2010

Не используйте const_cast здесь, иначе у вас возникнут проблемы. Использование mutable в этом случае не должно быть проблемой, но если профилировщик не предлагает иного, тогда я думаю, что пользователи будут менее удивлены, увидев объект, который дорого построить, чем метод доступа, который стоит вызвать в первый раз .

0 голосов
/ 19 марта 2010

Если ваш метод изменяет состояние объекта (например, путем изменения состояния базовой базы данных), то метод не должен быть постоянным.В этом случае у вас должен быть отдельный неконстантный load -метод, который должен быть вызван до того, как может быть вызван геттер const.

Этот метод не потребует ни const_cast, ни mutable и сделало бы потенциально дорогостоящую операцию явной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...