在“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( );
作者表示第四个例子将显示未定义的行为。我想知道原因。
答案 0 :(得分:5)
string_rep
检查是否有日期的缓存字符串表示。如果没有,它调用compute_cache_value
方法创建字符串表示并对其进行缓存,然后将缓存表示标记为有效,以便将来调用string_rep
不会重新计算它。
行const Date d2;
将显示未定义的行为,因为编译器可能认为它可以将d2
放入非易失性存储器中(例如,微控制器中的闪存,或只读标记的存储器或存储器映射的存储器) ,const_cast<Date*>
在这种情况下可能无效,导致cache_valid
保持false
或cache
保持为空字符串。
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_value
是cache_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对象。它可能存储在标记为“只读”的内存中。
那会发生什么?
底线:这是不必要的和愚蠢的。不要这样做。几乎总有比抛弃常数更好的方法。