簡體   English   中英

RAII國家管理

[英]RAII state management

我需要改變一個州。 然后做的事情。 然后將狀態重置回原來的狀態 - 例如:

auto oldActivationOrder = mdiArea->activationOrder();
mdiArea->setActivationOrder( QMdiArea::StackingOrder );
mdiArea->cascadeSubWindows();
mdiArea->setActivationOrder( oldActivationOrder );

我如何以RAII方式執行此操作? (c ++ 11和/或14)

編輯:謝謝你的所有答案。

有幾個建議可以創建一個用於處理狀態更改的自定義類(BoBTFish,mindriot,Mattias Johansson)。 這個解決方案似乎很清楚。 但是我認為這是一個缺點,它將行數從4增加到20+。 如果使用了很多,這將使代碼膨脹。 也似乎有一個單獨的類丟失了一些地方。

Ami Tavory建議使用std :: unique_ptr。 這沒有代碼膨脹問題並維護局部性。 但是,正如Ami所指出的那樣,它可能不是最易讀的解決方案。

sp2danny建議可以重用的通用狀態更改類。 這可以避免代碼膨脹,前提是它可以替換多個自定義類。 我將接受這個答案 - 但我認為正確的方法實際上取決於背景。

RAII: R esource A cquisition I I I nitialisation。

這也暗示資源釋放是毀滅性的,雖然我從未見過人們談論RRID,盡管這是更有用的一面。 (也許那應該是終止,還是終結?)

關鍵是,你在一個對象的構造函數中做了一些工作,並在析構函數中有效地反轉它。 這意味着無論你如何退出范圍,都會執行清理:多次return ,多次break ,拋出異常,......(甚至是goto !)

class ScopedActivationOrderChange {
    QMdiArea&             area_;     // the object to operate on
    QMdiArea::WindowOrder oldOrder_; // save the old state

  public:
    ScopedActivationOrderChange(QMdiArea& area, ActivationOrder newOrder)
        : area_(area)
        , oldOrder_(area_.activationOrder()) // save old state
    {
        area_.setActivationOrder(newOrder); // set new state
    }

    ~ScopedActivationOrderChange()
    {
        area_.setActivationOrder(oldOrder_); // reset to old state
    }
};

// ...

{ // <-- new scope, just to establish lifetime of the change
    ScopedActivationOrderChange orderChange{*mdiArea, QMdiArea::StackingOrder};
    mdiArea->cascadeSubWindows();
} // <-- end of scope, change is reversed

標准圖書館沒有為此提供任何一般設施。 它確實提供了一些用於更具體的用途,例如std::unique_ptr用於刪除動態分配的對象,在某些情況下可以用於其他事情,盡管它有點難看。 std::vector可以看作是動態數組的RAII類,也提供了一些其他管理工具,但是這個很容易被濫用於其他目的。

實現范圍保護模式的最簡潔方式(盡管可能不是最可讀)也許是使用帶有自定義刪除器std::unique_ptr

#include <memory>
#include <utility>


int main()
{
    void *p, *q;                                                                                                                                                                                         
    auto reverser = [&p, &q](char *){std::swap(p, q);};
    /* This guard doesn't really release memory - 
        it just calls the lambda at exit. */
    auto guard = std::unique_ptr<char, decltype(reverser)>{nullptr, reverser};
    std::swap(p, q);
}  

你可以這樣做:

class SetActivationOrder
{
public:
  SetActivationOrder(QMdiArea *mdiArea, QMdiArea::WindowOrder order)
    : m_mdiArea(mdiArea),
      m_oldActivationOrder(mdiArea->activationOrder())
  {
    m_mdiArea->setActivationOrder(order);
  }

  ~SetActivationOrder()
  {
    m_mdiArea->setActivationOrder(m_oldActivationOrder)
  }

private:
  QMdiArea *m_mdiArea;
  QMdiArea::WindowOrder m_oldActivationOrder;
};

然后像這樣使用它:

{
  // This sets the order:
  SetActivationOrder sao(mdiArea, QMdiArea::StackingOrder);
  mdiArea->cascadeSubWindows();
  // Destructor is called at end of scope and sets the old order
}

使用RAII(資源分配是初始化),您將在本地范圍(即堆棧)中創建存儲類的實例。 將要存儲的狀態傳遞給存儲對象的構造函數,並確保存儲對象的析構函數再次恢復狀態。 因為C ++保證在對象超出范圍時會自動為您調用本地作用域上的對象的析構函數,所以如果拋出異常,您也不必擔心要記住再次恢復狀態。

我會寫這樣的類:

class ActivationOrderState
{
public:
    ActivationOrderState(QMdiArea& area)
        : m_area(area)
    {
        // Get the old value
        m_oldOrder = area.activationOrder();
    }

    ~ActivationOrderState()
    {
        // Restore the old value
        m_area.setActivationOrder( m_oldOrder );
    }

private:
    QMdiArea& m_area;
    QMdiArea::WindowOrder m_oldOrder;
};

然后像這樣使用該對象

{
    ActivationOrderState state(*mdiArea); // saves the state
    mdiArea->setActivationOrder( QMdiArea::StackingOrder ); // set the new state

    // do other things here...

} // end of scope, destructor is called and state is restored again

為了確保沒有其他用戶通過在免費存儲/堆上而不是在本地范圍上分配它來濫用此代碼,您可以刪除operator new:

class ActivationOrderState
{
public:
    ActivationOrderState(QMdiArea& area)
        : m_area(area)
    {
        // Get the old value
        m_oldOrder = area.activationOrder();
    }

    ~ActivationOrderState()
    {
        // Restore the old value
        m_area.setActivationOrder( m_oldOrder );
    }

    // Remove the possibility to create this object on the free store.
    template<typename... Args> void* operator new(std::size_t,Args...) = delete;

private:
    QMdiArea& m_area;
    QMdiArea::WindowOrder m_oldOrder;
};

另請參閱使用RAII暫時提高線程優先級

您可以執行通用模板:

template< typename Obj, typename Getter, typename Setter , typename StateType >
class ScopedStateChangeType
{
public:
    ScopedStateChangeType( Obj& o, Getter g, Setter s, const StateType& state )
        : o(o), s(s)
    {
        oldstate = (o.*g)();
        (o.*s)(state);
    }
    Obj* operator -> () { return &o; }
    ~ScopedStateChangeType()
    {
        (o.*s)(oldstate);
    }

private:
    Obj& o;
    Setter s;
    StateType oldstate;
};

template< typename Obj, typename Getter, typename Setter , typename StateType >
auto MakeScopedStateChanger( Obj& o, Getter g, Setter s, StateType state )
     -> ScopedStateChangeType<Obj,Getter,Setter,StateType>
{
    return { o, g, s, state };
}

使用它像:

QMdiArea mdiArea;

{
    auto ref = MakeScopedStateChanger(
        mdiArea, &QMdiArea::activationOrder, &QMdiArea::setActivationOrder,
        QMdiArea::StackingOrder );
    ref->cascadeSubWindows();
}

如果你經常使用這種模式,也許值得

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM