C++带有延迟初始化的 const getter 方法
为延迟初始化的成员变量实现 getter 方法并保持 const 正确性的正确方法是什么?也就是说,我希望我的 getter 方法是 const,因为在第一次使用它之后,它就是一个普通的 getter 方法。只有第一次(当对象第一次初始化时)const 不适用。我想做的事:
class MyClass {
MyClass() : expensive_object_(NULL) {}
QObject* GetExpensiveObject() const {
if (!expensive_object_) {
expensive_object = CreateExpensiveObject();
}
return expensive_object_;
}
private:
QObject *expensive_object_;
};
我可以一边吃蛋糕一边吃吗?
What is the proper way to implement a getter method for a lazily-initialized member variable and maintain const-correctness? That is, I would like to have my getter method be const, because after the first time it is used, it's a normal getter method. It is only the first time (when the object is first initialized) that const does not apply. What I would like to do:
class MyClass {
MyClass() : expensive_object_(NULL) {}
QObject* GetExpensiveObject() const {
if (!expensive_object_) {
expensive_object = CreateExpensiveObject();
}
return expensive_object_;
}
private:
QObject *expensive_object_;
};
Can I eat my cake and have it too?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
这很好,并且是典型的做法。
您必须将
exppressive_object_
声明为mutable
mutable
基本上意味着“我知道我在一个 const 对象中,但修改它不会破坏常量。”That's fine and is the typical way of doing it.
You will have to declare
expensive_object_
asmutable
mutable
basically means "I know I'm in a const object, but modifying this won't break const-ness."我建议将 James Curran 的答案封装成如果您经常这样做,则可以使用它自己的类:
现在在您的代码中使用它,如下所示:
I propose encapsulating James Curran's answer into a class of its own if you do this frequently:
Now use this in your code as follows:
使
expense_object_
可变。Make
expensive_object_
mutable.使用
const_cast
在该特定位置回避 const。恕我直言,这比使
expense_object_
mutable
更好,因为您不会在所有其他方法中失去常量安全性。Use a
const_cast
to side-step const in that one specific place.IMHO, this is better than making
expensive_object_
mutable
because you don't lose the const-safety in all your other methods.您考虑过包装类吗?您也许可以使用智能指针之类的东西,仅使用 const 返回版本的
operator*
和operator->
以及也许operator[]
...您可以从中获得类似scoped_ptr
的行为作为奖励。让我们试一下,我相信人们可以指出一些缺陷:
使用
type_traits
可能会让这个更好......您需要不同版本的数组指针,并且您可能必须如果您想将参数传递给
T
的构造函数,请使用创建者函子或工厂对象或其他东西。但你可以像这样使用它:
是时候去编译它,看看我是否可以打破它......=)
Have you considered a wrapper class? You might be able to get away with something like a smart pointer, with only const-returning versions of
operator*
andoperator->
and maybeoperator[]
... You can getscoped_ptr
-like behavior out of it as a bonus.Let's give this a shot, I'm sure people can point out a few flaws:
Use of
type_traits
might make this better...You'd need different versions for array pointers, and you might have to play around a bit with a creator functor or factory object or something if you wanted to pass in arguments to
T
's constructor.But you could use it like this:
Time to go off and compile this and see if I can break it... =)
提出一个更奇特的解决方案此处,但它不处理没有默认构造函数的类型...
Proposing an even fancier solution here, but it doesn't handle types with no default constructor...
我创建了一个具有以下功能的类模板
Lazy
:下面是如何使用它:
这是实现。
I've crated a class template
Lazy<T>
with the following features:Here's how you use it:
Here's the implementation.
我对这个主题进行了一些研究,并提出了一个替代解决方案,以防您使用 C++11。考虑以下内容:
主要思想是保存一个返回昂贵对象作为成员而不是对象本身的函数。在构造函数中使用执行以下操作的函数进行初始化:
我喜欢这一点的是,初始化代码仍然写在构造函数中(如果不需要惰性,我自然会将其放在构造函数中),即使它仅在第一次查询昂贵对象时才会执行。
这种方法的缺点是 std::function 在执行过程中重新分配自身。重新分配后访问任何非静态成员(在使用 lambda 的情况下捕获)将导致未定义的行为,因此需要额外注意。这也是一种黑客行为,因为 GetExpectiveObject() 是 const,但它仍然会在第一次调用时修改成员属性。
在生产代码中,我可能更愿意使成员可变为 詹姆斯·柯兰描述。这样,类的公共 API 就清楚地表明该成员不被视为对象状态的一部分,因此它不会影响常量性。
经过一番思考,我发现 std::async 与 std::launch::deferred 也可以与 std::shared_future 结合使用,以便能够多次检索结果。这是代码:
I played a bit with this topic and came up with an alternative solution in case you use C++11. Consider the following:
Main idea is to hold a function returning the expensive object as member instead of the object itself. In the constructor initialize with a function that does the following:
What I like about this is that the initialization code is still written in the constructor (where I'd naturally put it if lazyness was not needed) even though it will only get executed when the first query for the expensive object happens.
A downside of this approach is that std::function reassigns itself within it's execution. Accessing any non static members (captures in case of using lambda's) after the reassignment would result in undefined behavior so this requires extra attention. Also this is kind of a hack since GetExpensiveObject() is const but it still modifies a member attribute on first call.
In production code I'd probably prefer to just make the member mutable as James Curran described. This way the public API of your class clearly states that the member is not considered part of the objects state, therefore it doesn't affect constness.
After a bit more thinking I figured that std::async with std::launch::deferred could also be used in combination with a std::shared_future in order to be able to retrieve the result multiple times. Here is the code:
你的 getter 并不是真正的 const,因为它确实改变了对象的内容。我觉得你想多了。
Your getter isn't really const since it does change the content of the object. I think you're over thinking it.