[OOD-More C++Idioms]書き込み時コピー(Copy on Write)

4278 ワード

目的
遅延コピー(lazy copy)の最適化の目的を達成する.遅延初期化(lazy initialization)と同様に、適切なタイミングでより効果的に選択されます.
別名#ベツメイ#
  • COW (copy-on-write)
  • Lazy copy

  • 動機
    コピーオブジェクトは、パフォーマンス損失(performance penalty)をもたらす場合があります.オブジェクトが頻繁にコピーされるが、修正が少ない場合、copy-on-writeはパフォーマンスを大幅に向上させることができます.copy-on-writeを実現するには、スマートポインタを使用して本物のオブジェクト値をカプセル化し、変更するたびにオブジェクトの参照カウントをチェックする必要があります.オブジェクトが複数回参照されている場合は、変更前にコピーを作成します.
    ソリューションおよび例
    #ifndef COWPTR_HPP
    #define COWPTR_HPP
    
    #include <memory>
    
    template <class T>
    class CowPtr
    {
        public:
            typedef std::shared_ptr<T> RefPtr;
    
        private:
            RefPtr m_sp;
    
            void detach()
            {
                T* tmp = m_sp.get();
                if( !( tmp == 0 || m_sp.unique() ) ) {
                    m_sp = RefPtr( new T( *tmp ) );
                }
            }
    
        public:
            CowPtr(T* t)
                :   m_sp(t)
            {}
            CowPtr(const RefPtr& refptr)
                :   m_sp(refptr)
            {}
            const T& operator*() const
            {
                return *m_sp;
            }
            T& operator*()
            {
                detach();
                return *m_sp;
            }
            const T* operator->() const
            {
                return m_sp.operator->();
            }
            T* operator->()
            {
                detach();
                return m_sp.operator->();
            }
    };
    
    #endif
      :      boost ,   std    。
    

    これは簡単な実装バージョンです.インテリジェントポインタデリファレンス(dereference)によって内部オブジェクトを参照する必要があるのは不便であるほか、クラスが内部状態を返すリファレンス:char & String::operator[](int)は、予期せぬ動作を伴うという少なくとも1つの欠点がある.
    次のコード・セグメントを考慮します.
    CowPtr<std::string> s1 = new std::string("Hello");
    char &c = s1->operator[](4); //     detach       
    CowPtr<std::string> s2(s1); //     ,     
    c = '!'; //    

    最後の行は、元の文字列s1ではなく、元の文字列s2を修正しなければならないが、実際にはs2も修正された.
    1つの比較的良い方法は、カスタムcopy-on-write実装を書くことであり、パッケージには遅延コピー(lazy-copy)のクラスが必要であり、ユーザーに透明性を保つことである.上記の問題を解決するために、オブジェクトを「共有不可(unshareable)」とマークできる状態は、メモリオブジェクトへの参照が渡されたこと、すなわち強制的に深度コピーが行われたことを示す.さらに最適化すると、クライアントコードがこれらの参照が無効になることを望んでいるため、内部オブジェクト参照を放棄しないnon-const操作後に「共有(shareable)」状態に復元することができる.
    この部分ははっきり言えません.タグオブジェクトは共有不可であり、例えば上記の例では、文字cを取り出して共有不可とし、s 2を再構築する際に直接深いコピーを行う.またnon-const操作では内部オブジェクトは放棄されず,このような操作で複本が作成され,このときの元のオブジェクトがshareableに更新されることを指す.
    既知のアプリケーション
  • Active Template Library
  • Many Qt classes (implicit sharing)

  • 関連する慣用法
    リファレンス
  • Herb Sutter, More Exceptional C++, Addison-Wesley 2002 - Items 13–16
  • Wikipedia: Copy-on-write

  • 詳細については、Githubプロジェクトにアクセスしてください.