C++ 惰性属性-可变属性的有效使用?

C++ 惰性属性-可变属性的有效使用?,c++,mutable,C++,Mutable,今天我发现自己写了一些类似下面的代码,我想知道这是否是mutable关键字的有效用法 我想通过const指针访问一些属性,但我只想在需要时计算属性 class Person { public: Person() : age(-1) {} ~Person() {} int GetAge() const { if( age == -1 ) { CalculateAge(); } return age; } private:

今天我发现自己写了一些类似下面的代码,我想知道这是否是
mutable
关键字的有效用法

我想通过
const
指针访问一些属性,但我只想在需要时计算属性

class Person {
  public:
    Person() : age(-1) {}
    ~Person() {} 

    int GetAge() const {
      if( age == -1 ) { CalculateAge(); }
      return age;
    }

  private:
    mutable int age;

    // Note: as a result of being called from a `const` method, 
    // CalculateAge has to be const itself.
    void CalculateAge() const {
      age = 33; // do calculation based on date of birth or whatever.
    }
};
这是
可变的关键点,还是我在这里滥用了它?
我用上述代码在概念上等同于

class Person {
  public:
    Person() {}
    ~Person() {} 

    int GetAge() const {
      // Always do the expensive calculation here
      return 33;
    }
};

其中
GetAge()
正确标记为
const
;它是否真正缓存结果是一个与我的类的客户机无关的实现细节

编辑:此外,我还想听听其他解决方案,这些解决方案不涉及将所有内容都设置为非常量

不是一个完整的答案,但对我来说,它看起来像是一种滥用。您应该只标记不修改对象外部状态的成员。在您的例子中,您有一个方法
GetAge
,它公开成员
age
,这意味着更改
age
会更改对象的“可见”状态

编辑


经过你的编辑,我不得不说这是好的。只要像使用缓存一样使用成员
age

就可以使用mutable,因为它不会更改对象的外部状态,但仍然需要更改某些内部状态。在我看来,这正是你在这里所拥有的。延迟的年龄计算只是一些内部优化,对用户隐藏该优化是合理的。用户无法理解为什么获取年龄会修改对象的外部状态


但是:我不喜欢可变的。如果您有一个驻留在只读内存中的常量对象(例如,只读文件映射),则可变可能是一个问题。如果随后对该对象调用const方法,编译器不会抱怨。但是,如果修改某个可变对象,代码将崩溃。

是的,这正是
可变
的基本原理。请注意,
const
方法通常是线程安全的。这打破了这一假设,所以要小心。@Mehrdad线程化引发了更多的问题:如果类有任何非常量函数,则客户端代码可能必须确保所有函数上的同步。但你是对的,线程确实增加了额外的问题。正如@n.m.所说,这正是
mutable
被发明的原因。在
mutable
之前使用的替代方法是丢弃常量。今天,这可能不起作用(无论如何,在理论上),因为如果没有任何
可变的
,编译器就可以将整个对象放入只读内存中。(在实践中,可以依赖预可变规则:编译器不会将具有用户定义构造函数的对象放在只读内存中,因为在构造函数运行时,对象不能放在只读内存中,并且系统不提供以后将内存设为只读的可能性。)@JamesKanze:我想你的意思是,如果客户机代码实际上以某种方式使用了非常量函数,那么它只需要确保所有函数上的正确同步,而不是如果它们仅仅存在的话如果客户机代码只有一个对对象的常量引用,则情况并非如此,因为它不能调用非常量函数。当然,除非它做了一些鬼鬼祟祟的事情(如我们在这里看到的
mutable
,或者
const_cast
,或者类似的事情,带有额外级别的非常量间接寻址),因此我的观点是……我认为如果编译器在只读内存中分配一个带有可变字段的常量对象,那就是一个编译器错误…@TamásZahola:我不知道确切的情况,但我希望如此。但我也可以想象拥有一个只读文件映射或其他只读内存。我知道,这些情况很少见,但我相信必须记住。@TamásZahola:看看标准,我猜7.1.1.11“类数据成员上的可变说明符会使应用于包含类对象的常量说明符无效,并允许修改可变类成员,即使对象的其余部分是常量(7.1.6.1)“禁止编译器将具有可变成员的常量对象放入常量数据节。很高兴知道!
class Person {
  public:
    Person() {
      // Do the expensive calculation here even if age is never used.
      age = 33; 
    }
    ~Person() {} 

    int GetAge() const {
      return age;
    }

  private:
    int age; // non-mutable
};