4

我正在向 const 方法添加一些惰性初始化逻辑,这使得该方法实际上不是 const。有没有办法让我不必从公共界面中删除“const”?

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

    return some int...

}

编辑:“可变”关键字在这里起作用吗?

4

6 回答 6

10

使 m_bFirstTime 可变:

class MyClass
{
  : :
  mutable bool m_bFirstTime;
};

...但这也常常表明存在设计缺陷。所以要小心。

于 2008-11-26T16:51:15.517 回答
8

实际上,您说您不想更改头文件。所以你唯一的选择是抛弃 this 指针的常量......

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...

}

如果使用 mutable 引发红旗,这会在轨道上启动红旗存储。做这样的事情通常是一个非常糟糕的主意。

于 2008-11-26T16:53:33.220 回答
5

我认为这个问题涉及两个概念:(1)“逻辑上的 const”和(2)“按位的 const”。我的意思是从类中获取一些 int 并不会在逻辑上更改类,并且在大多数情况下它不会更改类成员的。但是,在某些情况下,例如您的情况,确实如此。

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

顺便说一句,即使您已经接受“可变”答案是正确的,您也必须更新 .h!

于 2008-11-26T17:09:59.943 回答
1

将 m_bFirstTime 成员设置为可变

于 2008-11-26T16:50:52.570 回答
1

正如John Dibling 所说,将更改的字段标记为可变。重要的部分在 ypnos 的评论中:“不要真正改变对象的状态”(正如外界所感知的那样)。也就是说,在 const 方法调用之前和之后的任何方法调用都必须产生相同的结果。否则你的设计有缺陷。

一些有意义的事情是可变的:

  • 互斥锁或其他锁类型
  • 缓存结果(不会改变)

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

对于缓存,您必须考虑仅对检索成本高且假定不会更改的数据有意义的数据(例如 DNS 结果)。否则,您可能会将过时的数据返回给您的用户。

在 const 方法中不应该更改的一些内容:

  • 任何修改对象状态的东西
  • 影响此或其他方法结果的任何内容

执行 const 方法的类的任何用户都将假定您的类(从外部世界看)在执行期间不会改变。如果它不一样,那将是相当误导和容易出错的。举个例子,假设一个 dump() 方法改变了一些内部变量 -state, value- 并且在调试过程中你的类的用户决定在给定的点 dump() 你的对象:你的类在跟踪和没有跟踪时的行为会有所不同: 完美的调试噩梦。

请注意,如果您执行惰性优化,则必须执行它们才能访问不可变数据。也就是说,如果您的界面规定在构建期间您将从数据库中检索一个元素,该元素以后可以通过常量方法访问,那么如果您对数据进行延迟获取,您最终可能会遇到用户构造您的对象的情况保留旧数据的副本,修改数据库,然后决定将以前的数据恢复到数据库中。如果您执行了延迟提取,您最终会丢失原始值。如果在程序执行期间不允许修改配置文件,则相反的示例是配置文件解析。您可以避免将文件解析到需要的位置,因为您知道在开始或稍后执行读取将产生相同的结果。

于 2008-11-26T18:29:06.290 回答
0

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

唯一可能导致 havok 的编译器优化是让编译器确定您使用相同的参数调用该方法两次,并且只重用第一个返回值 - 只要该函数在逻辑上确实是 const 就可以了,并为给定的一组参数(和对象状态)返回相同的值。如果任何人(包括另一个线程)都可以访问该对象以在其上调用非常量方法,那么即使该优化也是无效的。

mutable 被添加到专门针对这种情况的语言中。C++ 是一种实用的语言,并且很乐意在需要时允许这样的极端情况存在。

于 2008-11-26T17:08:20.540 回答