RAII:在 const 方法中初始化数据成员
在RAII中,资源在被访问之前不会被初始化。然而,许多访问方法被声明为常量。我需要调用一个可变(非常量)函数来初始化数据成员。
示例:从数据库加载
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;
}
我的原始解决方案是将数据成员声明为可变
。我宁愿不这样做,因为它表明其他方法可以更改成员。
如何转换 load_from_database()
语句来消除编译器错误?
In RAII, resources are not initialized until they are accessed. However, many access methods are declared constant. I need to call a mutable
(non-const) function to initialize a data member.
Example: Loading from a data base
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;
}
My primitive solution is to declare the data member as mutable
. I would rather not do this, because it suggests that other methods can change the member.
How would I cast the load_from_database()
statement to get rid of the compiler errors?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这不是 RAII。在 RAII 中,您可以在构造函数中初始化它,这将解决您的问题。
所以,您在这里使用的是
Lazy
。无论是延迟初始化还是延迟计算。如果您不使用
mutable
,您将陷入痛苦的境地。当然,您可以使用
const_cast
,但是如果有人这样做怎么办:并且编译器认为它是只读内存的良好候选者?好吧,在这种情况下,const_cast 的效果是未定义的。最好的情况下,什么也不会发生。
如果您仍然希望采用
const_cast
路线,请像R Samuel Klatchko
那样进行。如果您经过深思熟虑并认为可能有更好的选择,您可以决定包装您的变量。如果它是在它自己的类中,只有 3 个方法:
get
、set
和load_from_database
,那么你就不会担心它是<代码>可变。This is not RAII. In RAII you would initialize it in the constructor, which would solve your problems.
So, what you are using here is
Lazy
. Be it lazy initialization or lazy computation.If you don't use
mutable
, you are in for a world of hurt.Of course you could use a
const_cast
, but what if someone does:And the compiler decides it is a good candidate for Read-Only memory ? Well, in this case the effects of the
const_cast
are undefined. At best, nothing happens.If you still wish to pursue the
const_cast
route, do it asR Samuel Klatchko
do.If you thought over and think there is likely a better alternative, you can decide to wrap your variable. If it was in class of its own, with only 3 methods:
get
,set
andload_from_database
, then you would not worry about it beingmutable
.您基本上是在实现缓存机制。我个人认为将缓存数据标记为可变是可以的。
You are basically implementing a caching mechanism. Personally I think it's OK to mark cached data as mutable.
正如 Matthieu 已经指出的那样,您在这里尝试做的事情与 RAII 几乎没有关系(如果有的话)。同样,我怀疑 const 和 mutable 的任何组合是否真的会有帮助。
const
和mutable
修改类型,并同样适用于对该类型对象的所有访问。您似乎想要的是少量代码具有写入权限,而其他任何代码都只能读取该值。考虑到 C++(以及大多数类似语言)的基本设计,正确的方法是将变量移动到它自己的类中,其中需要写入访问权限的少量代码作为(或可能是)那个班级。世界的其他部分通过类的接口(即检索值的成员函数)获得只读访问权限。
您发布的(大概是精简的)
MyClass
非常接近正确——您只需要单独使用它,而不是作为具有许多其他成员的较大类的一部分。主要需要更改的是 1) 将名称从MyClass
更改为lazy_int
之类的名称,以及 2) (至少按照我的喜好)get_value() 可能应该重命名为
operator int()
。是的,m_value
可能需要是可变的,但这不允许其他代码写入该值,因为其他代码根本无法访问该值本身。然后将这种类型的对象嵌入到更大的类中。由于其
operator int()
,该外部类中的代码可以将其视为 int (只读),但无法写入它,只是因为该类没有给出任何方式都可以这样做。As Matthieu already pointed out, what you're trying to do here has little (if anything) to do with RAII. Likewise, I doubt that any combination of
const
andmutable
is really going to help.const
andmutable
modify the type, and apply equally to all access to an object of that type.What you seem to want is for a small amount of code to have write access, and anything else only read access to the value. Given the basic design of C++ (and most similar languages), the right way to do that is to move the variable into a class of its own, with the small amount of code that needs write access as part of (or possibly a friend of) that class. The rest of the world is given its read-only access via the class' interface (i.e., a member function that retrieves the value).
The (presumably stripped down)
MyClass
you've posted is pretty close to right -- you just need to use that by itself, instead of as part of a larger class with lots of other members. The main things to change would be 1) the name fromMyClass
to something likelazy_int
, and 2) (at least by my preference)get_value()
should probably be renamed tooperator int()
. Yes,m_value
will probably need to be mutable, but this doesn't allow other code to write the value, simply because other code doesn't have access to the value itself at all.Then you embed an object of that type into your larger class. The code in that outer class can treat it as an int (on a read-only basis) thanks to its
operator int()
, but can't write it, simply because the class doesn't give any way to do so.[看,妈妈!没有演员表! :)) ]
这个想法是拥有一个政治上正确的
MyClass
和const
方法除了调用之外不改变任何东西< /b> 聚合对象的const
和非const
方法通过 const 指针。[ LOOK MA! NO CASTS! :)) ]
The idea is to have a politically correct
MyClass
withconst
methods not mutating anything but calling bothconst
and non-const
methods of aggregated objects via const pointers.不要在这里使用 const_cast,否则你就是在自找麻烦。在这种情况下使用可变应该不是问题,但如果探查器没有提出其他建议,那么我认为用户在看到构造成本高昂的对象而不是第一次调用成本高昂的访问器方法时不会感到惊讶。
Don't use const_cast here, or you're asking for trouble. Using mutable in this case shouldn't be a problem, but if the profiler didn't suggest otherwise then I think users would be less surprised to see an object that is expensive to construct than an accessor method that is expensive to call the first time.
如果您的方法更改了对象的状态(例如,通过更改底层数据库的状态),则该方法不应该是 const。在这种情况下,您应该有一个单独的非常量
load
方法,必须在调用const
getter 之前调用该方法。此方法既不需要
const_cast
也不需要mutable
,并且会使潜在的昂贵操作变得显式。If your method changes the state of the object (e.g. by changing the state of the underlying database), then the method should not be const. In that case you should have a separate, non-const
load
-method, that has to be called before theconst
getter can be called.This method would require neither
const_cast
notmutable
, and would make the potentially expensive operation explicit.