2010-09-20 47 views
3

當我有一個方法調用一組提供強保函的方法時,我也經常遇到回滾更改的問題以便也有一個強有力的保證方法。我們用一個例子:調用強保證方法的強保證方法

// Would like this to offer strong guarantee 
void MacroMethod() throw(...) 
{ 
    int i = 0; 
    try 
    { 
    for(i = 0; i < 100; ++i) 
     SetMethod(i); // this might throw 
    } 
    catch(const std::exception& _e) 
    { 
    // Undo changes that were done 
    for(int j = i; j >= 0; --j) 
     UnsetMethod(j); // this might throw 
    throw; 
    } 
} 

// Offers strong guarantee 
void SetMethod(int i) throw(...) 
{ 
    // Does a change on member i 
} 

// Offers strong guarantee 
void UnsetMethod() throw(...) 
{ 
    // Undoes a change on member i 
} 

很明顯,UnsetMethod可能會拋出。在這種情況下,我的MacroMathod()只提供基本保證。然而,我盡我所能提供了強有力的保證,但我不能絕對確定我的UnsetMethod()不會拋出。這是我的問題:

  1. 我應該甚至試圖在這種情況下提供強有力的保證?
  2. 我應該將我的MacroMethod()記錄爲具有基本或有力的保證嗎?即使UnsetMethod極不可能拋出?
  3. 你能否看到一種方法可以使這種方法真正提供有力的保證?
  4. 我應該試試UnsetMethod()函數,但感覺相當沉重,我該怎麼做呢?

謝謝!

+0

我只看到了強有力的保障被應用到方法不是獨立的功能。我認爲它可以完成,但我們真的需要更多地瞭解上下文。更改成員我什麼是有什麼全局變量我們正在改變狀態?如果它是一個對象。然後你複製一份。如果他們都工作,然後與真正的交換,那麼執行操作。 – 2010-09-20 14:52:03

+0

作爲樣式註釋(帶有一些非常討厭的含義),您應該重新考慮使用異常規範。他們已經知道會導致嚴重的問題,試圖將它們從語言中徹底刪除。 – 2010-09-20 14:58:03

+0

@Stanley:與朋友討論過這件事。編譯器也做了一些測試,並閱讀了boost的理性。你是對的。我雖然這種批評/限制是指定規範中的類型,而不是完全規範。這就解釋了爲什麼我保持拋出()與拋出(...)。感謝您的評論,並會更新我的代碼。 – Geeho 2010-09-24 01:16:04

回答

6

嘗試實現此目的的一個良好模式是使您的方法在要修改的對象的副本上工作。當所有的修改完成後,你交換對象(交換應該保證不會拋出)。這隻有在複製和交換可以有效執行時纔有意義。

此方法的優點是,您不需要代碼中的任何try...catch塊,也不需要清理代碼。如果引發異常,則在堆棧展開期間修改的副本將被丟棄,而原始內容根本未被修改。

+0

我的例子並沒有很好地描述問題的全部範圍,更不用說這個解決方案會導致的性能問題了:成員是一個數據數組,這樣的操作可以定期完成。但是您提出的解決方案將我帶入了正確的解決方案,這基本上就是您的建議。我將添加一個SetCachedMethod()和ApplyCache()/ ClearCache()方法。 SetCachedMethod()將拋出,應用和清除不會。所以我基本上有部分對象複製,而不是完整的。謝謝! – Geeho 2010-09-20 15:29:00

+0

@Geeho:也許你可以使緩存成爲一個外部對象,或者將clear-cache的調用綁定到某個守衛(查找[RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization))。這樣你仍然可以防止在你的代碼中有一個try ... catch塊。 – 2010-09-20 19:08:12

+0

不是一個壞主意。明天早上你會看第一件事。 – Geeho 2010-09-24 01:13:26

1

如果一個強大的例外保證對MacroMethod()很重要,我會重新設計UnsetMethod()以便不拋棄任何東西,如果可以的話。當然,這可以做什麼取決於你在做什麼。

您使用UnsetMethod()SetMethod()失敗後清理。如果UnsetMethod()未能清理,您可以對此做些什麼?爲什麼從析構函數中拋出異常是非常危險的。

1
  1. 不,你不能一般用你給的代碼。但是,根據您的問題,也許可以複製數據,對該副本執行SetMethod,然後交換表示。這提供了有力的保證,但又取決於問題。
  2. 你可以證明:如果UnsetMethod沒有拋出,則強有力的保證,否則爲基本。其實這解釋了爲什麼說析構函數不應該拋出。其實任何撤消操作都不應該拋出。
  3. 是的,請參閱1.
  4. 不,這沒有意義。
0

從根本上說,你需要在調用拋出函數功能做的是對數據的臨時副本,直到所有的可能拋子功能的工作已經完成,然後swap(一定不能丟)臨時值到「真實」結構。

一些說明性的psudocode:

void MacroMethod() throw(...) 
{ 
    int i = 0; 
    MyValues working_copy[100] = *this; //obviously this is psudocode as I don't know your real code 
    for(i = 0; i < 100; ++i) 
    working_vopy[i].SetMethod(i); // if this throws the exception will propogate out, and no changes will be made 
    swap(*this, working_copy); // this must not throw 
}