欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++17實(shí)現(xiàn)flyweight_factory模板類及使用示例詳解

 更新時(shí)間:2023年08月10日 10:25:57   作者:hedzr  
這篇文章主要為大家介紹了C++17實(shí)現(xiàn)flyweight_factory模板類及使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

FlyWeight Pattern

回顧享元模式,考慮實(shí)作它的各種問(wèn)題。

理論

享元模式,是將復(fù)雜對(duì)象的相同的組成元素抽出并單獨(dú)維護(hù)的一種結(jié)構(gòu)型設(shè)計(jì)模式。這些相同的組成元素被稱為共享元件,它們?cè)谝粋€(gè)單獨(dú)的容器中被唯一性地管理,而復(fù)雜對(duì)象只需持有到該唯一實(shí)例的參考,而無(wú)需重復(fù)創(chuàng)建這樣的相同的元素,從而能夠大幅度地削減內(nèi)存占用。

以字處理器為例,每個(gè)字符都具有獨(dú)立的、區(qū)別于其它字符的特殊屬性:例如字體樣式,背景、邊框、對(duì)齊等等。如果一個(gè)文檔中全部字符都單獨(dú)存儲(chǔ)一份它的所有屬性的副本,那么這將會(huì)是龐大的內(nèi)存需求。但考慮到一大堆(例如1000個(gè))字符可能都有相同的“宋體,9pt”這樣的屬性,那么實(shí)際上我們只需要單獨(dú)存儲(chǔ)一份“宋體,9pt”的字體樣式屬性,而一個(gè)字符只需要一個(gè)指向該字體樣式屬性的指針就可以了,這就比1000個(gè)字符的1000個(gè)字體樣式屬性拷貝要節(jié)約的多。

類似的案例還有相當(dāng)多,例如例子系統(tǒng)中的每個(gè)粒子(例如子彈、彈片,或者敵方飛機(jī))都有一些相同的屬性(例如顏色,輪廓等等)占地不小,但值卻相同。

工廠模式

很容易想到,我們可以在一個(gè)工廠中就地管理享元對(duì)象。當(dāng)客戶以具體值來(lái)請(qǐng)求一個(gè)享元對(duì)象時(shí),工廠會(huì)從一個(gè)字典中檢索享元是否存在,然后返回該元素的參考引用給客戶。如果享元尚未存在,那么工廠會(huì)創(chuàng)建它,然后在返回引用。

不可變性

按照傳統(tǒng)的說(shuō)法,享元模式要求這些相同的部分(享元,相同的組成元素)是不可變的。但這并不是鐵律。

一個(gè)方法是,以一個(gè)享元為整體,我們可以整體修改對(duì)象持有的享元參考。

例如我們正在修改字處理器中的一個(gè)單詞的字體樣式,從“宋體,9pt”改為“黑體,12pt”,那么我們可以直接修改引用指向。也就是說(shuō),我們提供 character.apply_font_style(font_style& style) 這樣的整體修改接口。

另一個(gè)方法可以從更細(xì)的粒度出發(fā)進(jìn)行修改,例如從“宋體,9pt”改為“宋體,10pt”,但在發(fā)生變更時(shí),嘗試從工廠中查證新值的參考。也就是說(shuō),我們提供 character.set_font_size(float pt) 這樣的接口,但在其實(shí)現(xiàn)過(guò)程中記得去查證享元工廠(管理器)以求更新內(nèi)部引用。

C++ 實(shí)現(xiàn)

傳統(tǒng)的享元模式的實(shí)現(xiàn)方式有這樣的示例代碼:

namespace hicc::dp::flyweight::basic {
  /**
     * flyweight Design Pattern
     *
     * Intent: Lets you fit more objects into the available amount of RAM by sharing
     * common parts of state between multiple objects, instead of keeping all of the
     * data in each object.
     */
  struct shared_state {
    std::string brand_;
    std::string model_;
    std::string color_;
    shared_state(const std::string &brand, const std::string &model, const std::string &color)
      : brand_(brand)
        , model_(model)
        , color_(color) {
      }
    friend std::ostream &operator<<(std::ostream &os, const shared_state &ss) {
      return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]";
    }
  };
  struct unique_state {
    std::string owner_;
    std::string plates_;
    unique_state(const std::string &owner, const std::string &plates)
      : owner_(owner)
        , plates_(plates) {
      }
    friend std::ostream &operator<<(std::ostream &os, const unique_state &us) {
      return os << "[ " << us.owner_ << " , " << us.plates_ << " ]";
    }
  };
  /**
     * The flyweight stores a common portion of the state (also called intrinsic
     * state) that belongs to multiple real business entities. The flyweight accepts
     * the rest of the state (extrinsic state, unique for each entity) via its
     * method parameters.
     */
  class flyweight {
    private:
    shared_state *shared_state_;
    public:
    flyweight(const shared_state *o)
      : shared_state_(new struct shared_state(*o)) {
      }
    flyweight(const flyweight &o)
      : shared_state_(new struct shared_state(*o.shared_state_)) {
      }
    ~flyweight() { delete shared_state_; }
    shared_state *state() const { return shared_state_; }
    void Operation(const unique_state &unique_state) const {
      std::cout << "flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n";
    }
  };
  /**
     * The flyweight Factory creates and manages the flyweight objects. It ensures
     * that flyweights are shared correctly. When the client requests a flyweight,
     * the factory either returns an existing instance or creates a new one, if it
     * doesn't exist yet.
     */
  class flyweight_factory {
    std::unordered_map<std::string, flyweight> flyweights_;
    std::string key(const shared_state &ss) const {
      return ss.brand_ + "_" + ss.model_ + "_" + ss.color_;
    }
    public:
    flyweight_factory(std::initializer_list<shared_state> lists) {
      for (const shared_state &ss : lists) {
        this->flyweights_.insert(std::make_pair<std::string, flyweight>(this->key(ss), flyweight(&ss)));
      }
    }
    /**
     * Returns an existing flyweight with a given state or creates a new one.
     */
    flyweight get(const shared_state &shared_state) {
      std::string key = this->key(shared_state);
      if (this->flyweights_.find(key) == this->flyweights_.end()) {
        std::cout << "flyweight_factory: Can't find a flyweight, creating new one.\n";
        this->flyweights_.insert(std::make_pair(key, flyweight(&shared_state)));
      } else {
        std::cout << "flyweight_factory: Reusing existing flyweight.\n";
      }
      return this->flyweights_.at(key);
    }
    void list() const {
      size_t count = this->flyweights_.size();
      std::cout << "\nflyweight_factory: I have " << count << " flyweights:\n";
      for (std::pair<std::string, flyweight> pair : this->flyweights_) {
        std::cout << pair.first << "\n";
      }
    }
  };
  // ...
  void AddCarToPoliceDatabase(
    flyweight_factory &ff,
    const std::string &plates, const std::string &owner,
    const std::string &brand, const std::string &model, const std::string &color) {
    std::cout << "\nClient: Adding a car to database.\n";
    const flyweight &flyweight = ff.get({brand, model, color});
    // The client code either stores or calculates extrinsic state and passes it
    // to the flyweight's methods.
    flyweight.Operation({owner, plates});
  }
} // namespace hicc::dp::flyweight::basic
void test_flyweight_basic() {
  using namespace hicc::dp::flyweight::basic;
  flyweight_factory *factory = new flyweight_factory({ {"Chevrolet", "Camaro2018", "pink"}, {"Mercedes Benz", "C300", "black"}, {"Mercedes Benz", "C500", "red"}, {"BMW", "M5", "red"}, {"BMW", "X6", "white"} });
  factory->list();
  AddCarToPoliceDatabase(*factory,
                         "CL234IR",
                         "James Doe",
                         "BMW",
                         "M5",
                         "red");
  AddCarToPoliceDatabase(*factory,
                         "CL234IR",
                         "James Doe",
                         "BMW",
                         "X1",
                         "red");
  factory->list();
  delete factory;
}

其輸出結(jié)果如同這樣:

--- BEGIN OF test_flyweight_basic                     ----------------------

flyweight_factory: I have 5 flyweights:
BMW_X6_white
Mercedes Benz_C500_red
Mercedes Benz_C300_black
BMW_M5_red
Chevrolet_Camaro2018_pink

Client: Adding a car to database.
flyweight_factory: Reusing existing flyweight.
flyweight: Displaying shared ([ BMW , M5 , red ]) and unique ([ James Doe , CL234IR ]) state.

Client: Adding a car to database.
flyweight_factory: Can't find a flyweight, creating new one.
flyweight: Displaying shared ([ BMW , X1 , red ]) and unique ([ James Doe , CL234IR ]) state.

flyweight_factory: I have 6 flyweights:
BMW_X1_red
Mercedes Benz_C300_black
BMW_X6_white
Mercedes Benz_C500_red
BMW_M5_red
Chevrolet_Camaro2018_pink
--- END OF test_flyweight_basic                       ----------------------

可以看到,像 [ BMW , X1 , red ] 這樣的一個(gè)享元,單個(gè)實(shí)例較大(數(shù)十、數(shù)百乃至數(shù)十K 字節(jié)),而引用參考不過(guò)是一個(gè)指針的大?。ㄍǔJ?64 bytes on 64-bit OS),那么最終節(jié)省的內(nèi)存是非??捎^的。

元編程中的 FlyWeight Pattern

上面的示例,已經(jīng)是舊風(fēng)格了,C++11 以后我們需要大量地使用智能指針、以及模板語(yǔ)法,而在 C++17 之后更好的原位構(gòu)造能力允許我們的代碼能夠更加 meaningful。

flyweight_factory

一個(gè)想法是,我們認(rèn)為一個(gè)盡可能通用的享元工廠可能是有利于代碼書寫的。所以我們嘗試這樣一個(gè)享元工廠模板:

namespace hicc::dp::flyweight::meta {
  template<typename shared_t = shared_state_impl, typename unique_t = unique_state_impl>
  class flyweight {
    std::shared_ptr<shared_t> shared_state_;
    public:
    flyweight(flyweight const &o)
      : shared_state_(std::move(o.shared_state_)) {
      }
    flyweight(shared_t const &o)
      : shared_state_(std::make_shared<shared_t>(o)) {
      }
    ~flyweight() {}
    auto state() const { return shared_state_; }
    auto &state() { return shared_state_; }
    void Operation(const unique_t &unique_state) const {
      std::cout << "flyweight: Displaying shared (" << *shared_state_ << ") and unique (" << unique_state << ") state.\n";
    }
    friend std::ostream &operator<<(std::ostream &os, const flyweight &o) {
      return os << *o.shared_state_;
    }
  };
  template<typename shared_t = shared_state_impl,
                   typename unique_t = unique_state_impl,
                   typename flyweight_t = flyweight<shared_t, unique_t>,
                   typename hasher_t = std::hash<shared_t>>
  class flyweight_factory {
    public:
    flyweight_factory() {}
    explicit flyweight_factory(std::initializer_list<shared_t> args) {
      for (auto const &ss : args) {
        flyweights_.emplace(_hasher(ss), flyweight_t(ss));
      }
    }
    flyweight_t get(shared_t const &shared_state) {
      auto key = _hasher(shared_state);
      if (this->flyweights_.find(key) == this->flyweights_.end()) {
        std::cout << "flyweight_factory: Can't find a flyweight, creating new one.\n";
        this->flyweights_.emplace(key, flyweight_t(shared_state));
      } else {
        std::cout << "flyweight_factory: Reusing existing flyweight.\n";
      }
      return this->flyweights_.at(key);
    }
    void list() const {
      size_t count = this->flyweights_.size();
      std::cout << "\nflyweight_factory: I have " << count << " flyweights:\n";
      for (auto const &pair : this->flyweights_) {
        std::cout << pair.first << " => " << pair.second << "\n";
      }
    }
    private:
    std::unordered_map<std::size_t, flyweight_t> flyweights_;
    hasher_t _hasher{};
  };
} // namespace hicc::dp::flyweight::meta

然后我們就可以以派生類的方式直接使用這個(gè)享元工廠了:

class vehicle : public flyweight_factory<shared_state_impl, unique_state_impl> {
  public:
  using flyweight_factory<shared_state_impl, unique_state_impl>::flyweight_factory;
  void AddCarToPoliceDatabase(
    const std::string &plates, const std::string &owner,
    const std::string &brand, const std::string &model, const std::string &color) {
    std::cout << "\nClient: Adding a car to database.\n";
    auto const &flyweight = this->get({brand, model, color});
    flyweight.Operation({owner, plates});
  }
};

其中 using flyweight_factory<shared_state_impl, unique_state_impl>::flyweight_factory; 是 C++17 以后的新語(yǔ)法,它將父類的所有構(gòu)造函數(shù)原樣復(fù)制給派生類,從而讓你不必拷貝粘貼代碼然后修改類名。

在 vehicle 模板類中我們使用默認(rèn)的 flyweight<shared_t, unique_t>,但你可以在 flyweight_factory 的模板參數(shù)中修改它以便提供你自己的享元類具體實(shí)現(xiàn)。

測(cè)試代碼

void test_flyweight_meta() {
    using namespace hicc::dp::flyweight::meta;
    auto factory = std::make_unique<vehicle>(
            std::initializer_list<shared_state_impl>{
                    {"Chevrolet", "Camaro2018", "pink"},
                    {"Mercedes Benz", "C300", "black"},
                    {"Mercedes Benz", "C500", "red"},
                    {"BMW", "M5", "red"},
                    {"BMW", "X6", "white"}});
    factory->list();
    factory->AddCarToPoliceDatabase("CL234IR",
                                    "James Doe",
                                    "BMW",
                                    "M5",
                                    "red");
    factory->AddCarToPoliceDatabase("CL234IR",
                                    "James Doe",
                                    "BMW",
                                    "X1",
                                    "red");
    factory->list();
}

附加

我們使用了稍稍不同的基礎(chǔ)類 shared_state_impl 以及 unique_state_impl

namespace hicc::dp::flyweight::meta {
  struct shared_state_impl {
    std::string brand_;
    std::string model_;
    std::string color_;
    shared_state_impl(const std::string &brand, const std::string &model, const std::string &color)
      : brand_(brand)
        , model_(model)
        , color_(color) {
      }
    shared_state_impl(shared_state_impl const &o)
      : brand_(o.brand_)
        , model_(o.model_)
        , color_(o.color_) {
      }
    friend std::ostream &operator<<(std::ostream &os, const shared_state_impl &ss) {
      return os << "[ " << ss.brand_ << " , " << ss.model_ << " , " << ss.color_ << " ]";
    }
  };
  struct unique_state_impl {
    std::string owner_;
    std::string plates_;
    unique_state_impl(const std::string &owner, const std::string &plates)
      : owner_(owner)
        , plates_(plates) {
      }
    friend std::ostream &operator<<(std::ostream &os, const unique_state_impl &us) {
      return os << "[ " << us.owner_ << " , " << us.plates_ << " ]";
    }
  };
} // namespace hicc::dp::flyweight::meta
namespace std {
  template<>
  struct hash<hicc::dp::flyweight::meta::shared_state_impl> {
    typedef hicc::dp::flyweight::meta::shared_state_impl argument_type;
    typedef std::size_t result_type;
    result_type operator()(argument_type const &s) const {
      result_type h1(std::hash<std::string>{}(s.brand_));
      hash_combine(h1, s.model_, s.color_);
      return h1;
    }
  };
} // namespace std

這是因?yàn)槲覀冊(cè)?flyweight_factory 中使用了 std::hash 技術(shù)來(lái)管理一個(gè)享元的鍵值,所以我們必須明確地實(shí)現(xiàn) shared_state_impl 的 std::hash 特化版本。

而在這個(gè)特化版本中我們又利用了一個(gè)特別的 hash_combine 函數(shù)。

hash_combine

這是一個(gè)技術(shù)性很強(qiáng)的概念,因?yàn)樗婕暗搅艘粋€(gè)神奇的幻數(shù)(magicnum)0x9e3779b9。

我們?cè)?nbsp;hicc-cxx/cmdr-cxx 中提供了一個(gè)源于 boost::hash_combine 的同名擴(kuò)展:

namespace std {
  template<typename T, typename... Rest>
  inline void hash_combine(std::size_t &seed, T const &t, Rest &&...rest) {
    std::hash<T> hasher;
    seed ^= 0x9e3779b9 + (seed << 6) + (seed >> 2) + hasher(t);
    int i[] = {0, (hash_combine(seed, std::forward<Rest>(rest)), 0)...};
    (void) (i);
  }
  template<typename T>
  inline void hash_combine(std::size_t &seed, T const &v) {
    std::hash<T> hasher;
    seed ^= 0x9e3779b9 + (seed << 6) + (seed >> 2) + hasher(v);
  }
} // namespace std

它的作用在于計(jì)算一系列的對(duì)象的 hash 值并組合它們。

關(guān)于如何正確地組合一堆 hash 值,較為簡(jiǎn)單地方法是:

std::size_t h1 = std::hash<std::string>("hello");
std::size_t h2 = std::hash<std::string>("world");
std::size_t h = h1 | (h2 << 1);

但仍然有更多的探討,其中得到了公認(rèn)的最佳手段(C++中)是源自于 boost::hash_combine 實(shí)現(xiàn)代碼的一個(gè)方法:

seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);

就目前已知的學(xué)術(shù)研究中,這是最佳的。

那么誰(shuí)制造了這么奇幻的一個(gè)神經(jīng)質(zhì)數(shù)(golden ratio)呢,大體可考的原作應(yīng)該是: A Hash Function for Hash Table Lookup 或 Hash Functions for Hash Table Lookup 。原作者 Bob Jenkins,原發(fā)于 DDJ 刊物 1997 年,代碼大約是成形于 1996 年。而這個(gè)數(shù)嘛,源于這個(gè)表達(dá)式:

$\frac{2^{32}}{\frac{1+\sqrt{5}}{2}}$ (

Epilogue

好,雖然不算太盡人意,但我確實(shí)實(shí)現(xiàn)了一個(gè) C++17 的勉強(qiáng)比較通用的 flyweight_factory 模板類,就將就了吧。

以上就是C++17實(shí)現(xiàn)flyweight_factory模板類及使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于C++17 flyweight_factory模板類的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Linux C 時(shí)間函數(shù)應(yīng)用

    Linux C 時(shí)間函數(shù)應(yīng)用

    本文是關(guān)于Linux C時(shí)間函數(shù) time_t struct tm 進(jìn)行了詳細(xì)的分析介紹并有應(yīng)用實(shí)例,希望能幫到有需要的同學(xué)
    2016-07-07
  • 基于Qt OpenCV的圖像灰度化像素操作詳解

    基于Qt OpenCV的圖像灰度化像素操作詳解

    這篇文章主要為大家詳細(xì)介紹了基于Qt+OpenCV的圖像灰度化像素操作:最大值法、平均法、加權(quán)平均值法,感興趣的小伙伴可以了解一下
    2022-07-07
  • C++ 11新特性之大括號(hào)初始化詳解

    C++ 11新特性之大括號(hào)初始化詳解

    這篇文章主要介紹了C++ 11新特性之大括號(hào)初始化的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • Qt界面美化之自定義qss樣式表的詳細(xì)步驟

    Qt界面美化之自定義qss樣式表的詳細(xì)步驟

    很多人應(yīng)該和我一樣,想做界面才接觸的Qt,結(jié)果就是做不出來(lái)華麗的界面,下面這篇文章主要給大家介紹了關(guān)于Qt界面美化之自定義qss樣式表的詳細(xì)步驟,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • C++使用gtest框架編寫單元測(cè)試的教程詳解

    C++使用gtest框架編寫單元測(cè)試的教程詳解

    gtest 是 Google 開發(fā)的一個(gè)用于 C++ 的測(cè)試框架,廣泛應(yīng)用于編寫和運(yùn)行單元測(cè)試,并且支持任何類型的測(cè)試,而不僅僅是單元測(cè)試,本文本文給大家介紹了C++使用gtest框架編寫單元測(cè)試的教程,需要的朋友可以參考下
    2024-08-08
  • C語(yǔ)言strlen函數(shù)全方位講解

    C語(yǔ)言strlen函數(shù)全方位講解

    在C語(yǔ)言中我們要獲取字符串的長(zhǎng)度,可以使用strlen函數(shù),strlen函數(shù)計(jì)算字符串的長(zhǎng)度時(shí),直到空結(jié)束字符,但不包括空結(jié)束字符,因?yàn)?nbsp;strlen函數(shù)時(shí)不包含最后的結(jié)束字符的,因此一般使用strlen函數(shù)計(jì)算的字符串的長(zhǎng)度會(huì)比使用sizeof計(jì)算的字符串的字節(jié)數(shù)要小
    2022-09-09
  • 深入解析C++ STL中的常用容器

    深入解析C++ STL中的常用容器

    這里我們不涉及容器的基本操作之類,只是要討論一下各個(gè)容器其各自的特點(diǎn)。STL中的常用容器包括:順序性容器(vector、deque、list)、關(guān)聯(lián)容器(map、set)、容器適配器(queue、stac)
    2013-09-09
  • 基于Matlab繪制洛倫茲吸引子相圖

    基于Matlab繪制洛倫茲吸引子相圖

    洛倫茲吸引子(Lorenz attractor)是由MIT大學(xué)的氣象學(xué)家Edward Lorenz在1963年給出的。本文將利用Matlab實(shí)現(xiàn)洛倫茲吸引子相圖的繪制,感興趣的可以了解一下
    2022-04-04
  • C語(yǔ)言用封裝方法實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲

    C語(yǔ)言用封裝方法實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言用封裝方法實(shí)現(xiàn)飛機(jī)大戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++核心編程之內(nèi)存分區(qū)模型詳解

    C++核心編程之內(nèi)存分區(qū)模型詳解

    這篇文章主要為大家介紹了C++核心編程中內(nèi)存分區(qū)模型,C++程序在執(zhí)行時(shí),將內(nèi)存大方向分為四個(gè)區(qū)域,代碼區(qū),全局區(qū),棧區(qū),堆區(qū),文章通過(guò)代碼示例介紹的非常詳細(xì),感興趣的同學(xué)可以參考閱讀下
    2023-07-07

最新評(píng)論