在这种情况下C ++是否可变?

时间:2009-04-15 00:29:48

标签: c++ mutable

我想问一下mutable的使用是否合适:

#include <iostream>

class Base
{
protected:
  int x;

public:
  virtual void NoMod() const
  {
    std::cout << x << std::endl;
  }
  void Draw() const
  {
    this->NoMod();  
  }
};

class Derive : public Base
{
private:
  mutable int y;

public:
  void NoMod() const
  {
    y = 5;
  }
};

int main()
{
  Derive derive;

  // Test virtual with derive
  derive.Draw();

  return 0;
}

Base类是第三方库。我正在扩展它以提供我自己的NoMod()。库原始NoMod()被声明为const。

我的NoMod()与Base的不同之处在于它需要修改自己的成员变量。

因此,对于我自己的NoMod()来编译并在调用Draw()时调用,我不得不

1)将Derive :: NoMod()实现为const
2)让我的内容变得可变。

这是我能做的最好的吗?

7 个答案:

答案 0 :(得分:9)

很难说,因为您没有提供y所指的内容或其使用方式的任何背景信息。

通常,mutable仅在更改可变变量时不会更改对象的实际“值”。例如,当我为C风格的字符串编写包装器时,我需要使内部mLength变量可变,以便我可以缓存长度,即使请求的内容是{{1}对象。它没有更改长度或字符串,并且在类本身之外是不可见的,因此将其设为const是正常的。

答案 1 :(得分:8)

正如“头极客”所描述的那样,问题的答案取决于您的数据成员的使用方式。

我在一个类中区分了两种类型的数据成员。

我使用常用​​术语“属性”来指代数据成员 这是对象的逻辑状态或“值”。 通常,属性很少被声明为可变。

我创造了它表示的原型“贡献” 简单地“工作记忆/存储”的数据成员 这有点与对象的状态脱节。 Contributes与对象的用户没有上下文相关性, 它们存在于课堂上,只是为了维护 并有效地操作对象。 Contributes通常在类中声明为可变的并且始终是 私人或受保护。

例如,假设您的对象是链接列表, 所以你有一个指向列表中第一项的指针。 我认为这个指针是有贡献的,因为 它不代表列表中的数据。 即使列表已排序和 指针设置为列表中的新第一项, 列表对象的用户可以更少关心 列表保持不变。只有列表数据有 是否已修改,列表是否已排序 与用户的观点相关。 即使您有一个booean数据成员'已排序'以快速确定 如果列表处于排序状态,那么这也是一个贡献 因为列表结构本身会影响排序状态, 'sorted'变量成员仅用于有效地记住状态 无需扫描列表。

另一个例子,如果你有一个搜索列表的const方法。 假设您知道通常搜索将返回 最近搜索过的项目, 你会在你的班级中保留一个指针给你的方法 可以在搜索之前首先检查最后找到的项目是否与搜索关键字匹配 整个列表(如果该方法确实需要搜索列表并查找 一个项目,指针将被更新)。 这个指针我会认为是一个贡献,因为它 只是为了帮助加快搜索速度。即便如此 搜索更新指针贡献,方法是有效的 const因为容器中没有任何项目的数据被修改。

因此,属性的数据成员通常不会被声明为可变的, 有助于对象运作的数据成员通常是可变的。

答案 2 :(得分:3)

我认为mutable唯一可行的事情就是引用计数等不是对象状态的一部分。

如果y是对象的物理状态的一部分,而不是逻辑状态,那么这没关系,否则,不要这样做。

答案 3 :(得分:1)

你可以想到'mutable'的另一种情况是你有一个带有'const'成员变量的类,你需要实现赋值运算符(=)而不必跳过'const'成员的赋值。

此外,const_cast在最初声明的'const'变量上使用它并使用它是U.B.根据C ++标准。因此,如果方法的接口接受必须在内部修改的'const',则将其传递给'mutable'参数。

您可以从上述情况判断相同的适当性,即只在语义上有意义!不要用它来使源代码可编译。

此致

答案 4 :(得分:1)

当类的成员没有真正定义对象的状态时使用mutable(例如,是一个有助于提高性能的缓存值/对象)。

我用来做另一个区别。在您的示例中,您只强制对const对象进行一次更改。您也可以使用const_cast运算符:

const_cast< Derive*>( this)->y = 10;

当您使用const_cast运算符时,您可以通过在代码中运行运算符名称搜索来轻松识别强制执行const到非const转换的位置。

但是,正如我所说的,如果成员不是对象状态的一部分,但必须在几个常量方法中间接更改,则对该成员使用mutable。

答案 5 :(得分:1)

我需要可变功能的唯一情况是:

  • 派生数据的缓存版本。例如,如果你有一个Rectangle类,它有一个很可能被调用的GetSurface()成员函数,你可以添加一个可变的m_surfaceCache成员变量来保存派生数据。
  • 关键部分成员变量。这是因为我的CriticalSection :: Enter()函数在概念上不是const,但是临界区成员变量不是类数据的真实部分,它更像是编译器指南。

但是,作为一般的经验法则,我建议不要经常使用mutable,因为它绕过了C ++精彩的const特性。

答案 6 :(得分:1)

如果你说,它是第三方图书馆的一部分,你可能没有选择权。 C ++本质上是一种实用的语言,让你做你需要做的事情,即使它可能并不总是一个“最佳实践”。

有一点需要注意的是,第三方库正在记录NoMod不应该通过添加const说明符来修改对象。违反该合同,您可能会遇到麻烦。如果库在某些情况下多次调用NoMod,那么派生类最好能够处理它,因为真正的const方法对它没有任何问题。

我首先寻找另一种解决问题的方法,但在那里失败宣告它是可变的。