RAII:在const方法中初始化数据成员

时间:2010-03-19 16:31:55

标签: c++ lazy-evaluation const-correctness

RAII 中,资源在访问之前不会初始化。但是,许多访问方法被声明为常量。我需要调用mutable(非const)函数来初始化数据成员。

示例:从数据库加载

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 个答案:

答案 0 :(得分:20)

这不是RAII。在RAII中,您将在构造函数中初始化它,这将解决您的问题。

所以,你在这里使用的是Lazy。无论是懒惰的初始化还是懒惰的计算。

如果你不使用mutable,你就会陷入伤害的世界。

当然你可以使用const_cast,但如果有人这样做会怎么样:

static const MyClass Examplar;

并且编译器决定它是只读内存的一个很好的候选者?那么,在这种情况下,const_cast的效果是不确定的。充其量没有任何反应。

如果您仍希望采用const_cast路线,请按R Samuel Klatchko进行操作。

如果你考虑并认为可能有更好的选择,你可以决定包装你的变量。如果它只是自己的类,只有3种方法:getsetload_from_database,那么你不必担心它是mutable

答案 1 :(得分:5)

您基本上正在实施缓存机制。就个人而言,我认为将缓存数据标记为可变是可以的。

答案 2 :(得分:5)

正如Matthieu已经指出的那样,你在这里尝试做的事与RAII几乎没有关系(如果有的话)。同样,我怀疑constmutable的任何组合确实会有所帮助。 constmutable修改类型,并同样适用于对该类型对象的所有访问。

您似乎想要的是少量代码具有写访问权限,而其他任何内容只能读取对该值的访问权限。鉴于C ++(和大多数类似语言)的基本设计,正确的方法是将变量移动到自己的类中,少量代码需要写访问作为(或可能是朋友的一部分)的一部分。 )那个班。世界其他地方通过类的接口(即检索值的成员函数)获得只读访问权。

你发布的MyClass({1}}非常接近正确 - 你只需要自己使用它,而不是作为一个有很多其他成员的大班级的一部分。要改变的主要内容是1)从MyClasslazy_int和2)的名称(至少根据我的偏好)get_value()应该重命名为operator int() }。是的,m_value可能需要是可变的,但这不允许其他代码写入值,因为其他代码根本无法访问值本身。

然后,您将该类型的对象嵌入到更大的类中。由于它的operator int(),该外部类中的代码可以将其视为一个int(在只读的基础上),但不能写它,因为该类没有给出任何方法。

答案 3 :(得分:4)

[看完了!没有案例! :))]

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聚合对象的方法 通过const指针

答案 4 :(得分:1)

不要在这里使用const_cast,否则你会遇到麻烦。在这种情况下使用mutable不应该是一个问题,但如果分析器没有提出其他建议,那么我认为用户不会惊讶地看到一个构造成本高的对象比第一次调用昂贵的访问器方法

答案 5 :(得分:0)

如果您的方法更改了对象的状态(例如,通过更改底层数据库的状态),则该方法不应该是const。在这种情况下,你应该有一个单独的非const load方法,必须在调用const getter之前调用它。

此方法既不需要const_cast也不需要mutable,并且会使潜在的昂贵操作显式化。