课堂设计问题

时间:2011-03-16 22:06:15

标签: c++ class

在“The C ++ programming language”一书中,作者给出了以下例子。他提到“缓存需要在可以使用之前填充”。在我看来,这就是为什么放置compute_cache_value的功能。但是我不明白string_rep()的功能与它的实现有什么关系。谢谢你的澄清。

class Date{
      bool cache_valid;
      string cache;
      void compute_cache_value( );  //fill cache
      // ...
public: 
      // ...
     string string_rep( ) const;
};

string Date:: string_rep( ) const
{
    if (cache_valid == false) {
          Date* th = const_cast<Date*> (this); // cast away const
          th->compute_cache_value( );
          th->cache_valid = true;
    }
    return cache;
}

此外,作者还举例说明了以下内容:

Date d1;
const Date d2;
string s1 = d1.string_rep( );
string s2 = d2.string_rep( );

作者表示第四个例子将显示未定义的行为。我想知道原因。

4 个答案:

答案 0 :(得分:5)

string_rep检查是否有日期的缓存字符串表示。如果没有,它调用compute_cache_value方法创建字符串表示并对其进行缓存,然后将缓存表示标记为有效,以便将来调用string_rep不会重新计算它。

const Date d2;将显示未定义的行为,因为编译器可能认为它可以将d2放入非易失性存储器中(例如,微控制器中的闪存,或只读标记的存储器或存储器映射的存储器) ,const_cast<Date*>在这种情况下可能无效,导致cache_valid保持falsecache保持为空字符串。

P.S。正如Michael J在下面指出的那样,除了使用const_cast之外,几乎总是有更好的方法来做事。对于此示例,mutable关键字就派上用场了。简而言之,标记类成员mutable告诉编译器即使对象是const,也可以更改成员。

class Date
{
      mutable bool cache_valid;
      mutable string cache;
      void compute_cache_value() const;
      // ...
public: 
      // ...
     string string_rep() const;
};

string Date::string_rep() const
{
    if (cache_valid == false) {
          compute_cache_value();
          cache_valid = true;
    }
    return cache;
}

...虽然我认为设置compute_cache_valuecache_valid的责任,我会考虑将operator string() const { return string_rep(); }添加到Date

关于mutable的最后一点是编译器更好地了解发生了什么,并且在像d2这样的情况下,可以将对象放在易失性存储器中,尽管它声明为const

答案 1 :(得分:1)

string_rep()返回缓存数据的字符串表示形式。由于将d2显式声明为const,因此示例4中出现了未定义的行为。 const_cast只能用于(使用已定义的行为)来抛弃隐式const'ness。

答案 2 :(得分:0)

在某些情况下,应该是可变的数据成员,不支持mutable关键字的编译器,以及最初定义为const的对象,标准表示行为未定义。
确实允许编译器将对象作为优化放在只读内存中。

http://www.parashift.com/c++-faq-lite/const-correctness.html#faq-18.13
http://www.gotw.ca/gotw/017.htm

答案 3 :(得分:0)

函数string_rep标记为“const”。这告诉编译器它不会修改对象。但是,通过使用const_cast()进行巧妙的技巧,它确实会改变对象。

这在每种情况下都是危险的,因为编译器可能会执行某些优化,假设对象未被修改,这可能会导致各种问题,包括可能的数据损坏。

这在d2的情况下是双重的,因为d2是一个const对象。它可能存储在标记为“只读”的内存中。

那会发生什么?

  1. 一般都没有。它可能会起作用。
  2. 有时它会无声地更新内存,缓存将包含垃圾。
  3. 可能会导致异常并导致程序崩溃。
  4. 可能欺骗优化者做一些疯狂的事情然后所有的赌注都关闭了。那时,几乎任何事情都可能发生。
  5. 底线:这是不必要的和愚蠢的。不要这样做。几乎总有比抛弃常数更好的方法。