2011-03-16 73 views
3

在「C++程序設計語言」一書中,作者給出了以下例子。他提到「緩存需要先填充才能使用」。在我看來,這就是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(); 

而筆者說,第四個例子顯示未定義的行爲。我想知道爲什麼。

回答

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_value的責任來設置cache_valid,我會考慮加入operator string() const { return string_rep(); }Date

這很好關於mutable是編譯器是怎麼回事的一個更好的想法,像d2的情況下,可以把物體在易失性存儲器,儘管其宣佈爲const最後一兩件事。

1

string_rep()返回緩存數據的字符串表示形式。示例4中未定義的行爲是因爲將d2顯式聲明爲const。 const_cast只能用於(具有定義的行爲)來拋出隱式的常量。

0

功能string_rep被標記爲 「常量」。這是告訴編譯器它不會修改對象。然而,通過使用const_cast()來玩巧妙的技巧,它確實會改變對象。

這是在任何情況下,因爲編譯器可以執行一些優化,它假定沒有修改的對象危險的,這可能會導致各種問題,包括可能的數據損壞。

這是D2的情況下,雙重壞D2是一個const對象。它可能存儲在標記爲「只讀」的內存中。

那麼會發生什麼?

  1. 大多一無所獲。它可能會正常工作。
  2. 有時它會悄悄地失敗更新的內存和緩存將含有垃圾。
  3. 可能會導致異常並導致程序崩潰。
  4. 可能欺騙優化器做一些瘋狂的事情,然後所有的投注都關閉。那時,幾乎任何事情都可能發生。

底線:這是不必要的和愚蠢的。不要這樣做。幾乎總是有比拋棄常量更好的方法。