C++STL::兩種方式實現STL容器的reference語義

Inside_Zhang發表於2015-11-17

STL容器提供的是“value semantic”而非“reference semantic”。然而不論是為了降低元素過大的複製代價還是為了在不同集合中共用同一個元素的需求,這時我們必須使用“reference semantic”,方式有二:

使用智慧指標

#include <string>
#include <iostream>
#include <memory>
#include <set>
#include <deque>
#include <algorithm>

class Item
{
private:
    std::string _name;
    float _price;
public:
    Item(const std::string& name, float price):
            _name(name), _price(price){}
    std::string getName() const { return _name;}
    void setName(const std::string& name) { _name = name;}
    float getPrice() const { return _price;}
    void setPrice(float price) { _price = price;}
};

template<typename Coll>
void printItems(const std::string& msg, const Coll& coll)
{
    std::cout << msg << std::endl;
    for (const auto& elem: coll)
        std::cout << " " << elem->getName() << ": " << elem->getPrice() << std::endl;
}

int main(int, char**)
{
    typedef std::shared_ptr<Item> ItemPtr;
    std::set<ItemPtr> allItems;
    std::deque<ItemPtr> bestItems;
    bestItems = {ItemPtr(new Item("C++", 10.)), ItemPtr(new Item("Python", 20.))};
    allItems = {ItemPtr(new Item("Machine Learning", 30.))};
    allItems.insert(bestItems.begin(), bestItems.end());
                    // 此時bestItems同時被兩個容器所操縱
                    // 而我們知道兩種容器的更新應當是同步的
    printItems("bestItems: ", bestItems);
    printItems("allItems: ", allItems);
    std::cout << std::endl;

    // 將bestItems的售價都提高一倍
    std::for_each(bestItems.begin(), bestItems.end(), 
                [](ItemPtr& elem){elem->setPrice(elem->getPrice()*2);});
    // 同時修改allItems,bestItems

    // 將bestItems[1]替換為“Machine Learning”
    bestItems[1] = *std::find_if(allItems.begin(), allItems.end(),
                [](ItemPtr elem){return elem->getName() == "Machine Learning";});

    bestItems[0]->setPrice(55.);
    printItems("bestItems: ", bestItems);
    printItems("allItems: ", allItems);
    return 0;
}

這裡尤其注意一個隱蔽的細節:
對bestItems[1]進行替換時,使用set的find成員函式還是使用std::find()都無法實現需求,如:

find(allItems.begin(), allItems.end(), ItemPtr(new Item("Machine Learning", 30.)));
allItems.find(ItemPtr(nwe Item("Machine Learning", 30.)));

因為將allItems的內部元素和傳遞進來的new出來的智慧指標作比較,是永遠不會相等的,所以最終返回的Iterator是allItems.end(),無法提領其元素。

這裡會存在一個問題,此時將bestItems銷燬時,allItems中的相應元素不會被銷燬,而會持續有效。

bestItems.clear();
printItems("allItems: ", allItems);

使用reference wrapper

假設保證“只要容器存在,被指向的元素一定存在”,就需使用另一種方法使用class reference_wrapper<>(位於<functional>標頭檔案)。

typedef std::reference_wrapper<Item> ItemRef;
std::vector<ItemRef> books;
            // std::vector<Item&>,不允許
Item item1("C++", 20.);
books.push_back(item1);

for (const auto& book: books)
    std::cout << book.get().getName() << ": " << book.get().getPrice() << std::endl;

// 當宣告其具體型別時,get()就不再必須
for (const Item& book: books)
    std::cout << book.getName() << ": " << book.getPrice() << std::endl;

book.setPrice(29.99);
std::cout << books[0].get().getPrice() << std::endl;
                    // 29.99
books[0].get().setName("C++ Primer");
std::cout << books[0].get().getName() << std::endl;
                    // C++ Primer

相關文章