English 中文(简体)
在C++中,我希望我的接口.h声明为int GetSomeInt() const;……但实际上这个方法确实更新了"this"。
原标题:
  • 时间:2008-11-26 16:48:14
  •  标签:

我正在将一些惰性初始化逻辑添加到一个常量方法中,这使得该方法实际上并不是常量。是否有一种方法可以在不从公共接口中删除“const”的情况下完成此操作?

int MyClass::GetSomeInt() const
{
    // lazy logic
    if (m_bFirstTime)
    {
        m_bFirstTime = false;
        Do something once
    }

    return some int...

}

编辑:这里的“mutable”关键字有作用吗?

最佳回答

将m_bFirstTime设置为可变的:

class MyClass
{
  : :
  mutable bool m_bFirstTime;
};

但这也往往是设计缺陷的一个很好的指示。所以要小心。

问题回答

实际上,你说你不想改变头文件。所以你唯一的选择是去除this指针的const属性。

int MyClass::GetSomeInt() const
{
    MyClass* that = const_cast<MyClass*>(this);

    // lazy logic
    if (that->m_bFirstTime)
    {
        that->m_bFirstTime = false;
        Do something once
    }

    return some int...

}

如果使用可变引起警报,这将启动一个红旗商店进入轨道。做这样的事通常是一个非常糟糕的想法。

我认为这个问题涉及到两个概念:(1)“逻辑常数”和(2)“按位常数”。这意味着从一个类中获得某个整数,并不会逻辑上改变该类,并且在大多数情况下不会改变类成员的。然而,在你的情况下,有些情况确实会改变。

在这些情况下,尽管方法在逻辑上是一个常量,但并不是按位常量,编译器无法知道这一点。这就是mutable关键字存在的原因。像John Dibling展示的那样使用它,但这并不是设计上的缺陷,相反,有许多情况是必须这样做的。在您的例子中,我想计算int的开销很大,因此如果不需要计算它,我们就不想计算它。在其他情况下,您可能希望缓存方法的结果以供以后使用等等。

顺便提一句,即使您已经接受了“可变”答案作为正确答案,您仍需要更新.h文件!

将m_bFirstTime成员设置为mutable。

正如John Dibling所说,将被更改的字段标记为可变的。 ypnos的评论中的重要部分是:实际上不要改变对象的状态(在外界看来)。也就是说,在const方法调用之前和之后的任何方法调用必须产生相同的结果。否则,您的设计存在缺陷。

有些可以改变的事情:

  • mutex or other lock types
  • cached results (that will not change)

互斥锁不是你的对象状态的一部分,它们只是阻止机制,以确保数据完整性。从你的类中检索值的方法确实需要更改互斥锁,但是在常量方法执行后,你的类数据和状态将与之前完全相同。

通过缓存,你必须考虑到只有那些昂贵得去检索而且被假定不会改变的数据(比如DNS结果)才有意义。否则你可能会向用户返回过期的数据。

一些在const方法内不应更改的东西:

  • Anything that modifies the state of the object
  • Anything that affects this or other method results

你的类的任何使用const方法的用户都会假定你的类(从外部世界看)在执行期间不会改变。如果不是这样,这将是非常误导性和容易出错的。例如,假设一个dump()方法改变了一些内部变量-状态、值-并且在调试期间,你的类的用户决定在给定的点中dump()你的对象:你的类将会在跟踪和没有跟踪的情况下表现不同:完美的调试噩梦。

请注意,如果您使用懒惰优化,必须对可变数据进行访问。也就是说,如果您的接口规定在构建期间将从数据库检索元素,然后可以通过常量方法访问该元素,那么如果您对数据进行懒惰获取,最终可能会发生这样的情况:用户构建对象以保留旧数据的副本,修改数据库,然后决定将先前的数据恢复到数据库中。如果您执行了懒惰获取,则最终将丢失原始值。与之相反的例子是配置文件解析,如果不允许在程序执行期间修改配置文件,则可以避免在需要的时候解析文件,因为始终在开头或稍后读取将产生相同的结果。

无论如何-请注意,这将不再是线程安全的。如果一个对象只有const方法(或者您只在初始化后使用const方法),您通常可以依赖它是线程安全的。但是,如果这些const方法只是逻辑上的const,那么您将失去这种好处(除非您始终锁定它)。

唯一可能造成混乱的编译器优化是编译器能够确定你使用相同参数两次调用方法,并且只重用第一个返回值——只要函数确实是逻辑上的const,并且返回给定参数(和对象状态)相同的值即可。即使有任何人(包括另一个线程)都可以访问对象调用非const方法,该优化也是无效的。

“mutable是专门为这种情况添加到语言中的。C ++是一门实用语言,它很高兴允许这样的边缘情况存在,以便需要时使用。”





相关问题
热门标签