C++ STL容器的Value语义与Reference语义
1.Value语义vs.Reference语义
1.1两种语义简述
通常情况下,所有容器都是建立元素的copy,返回的元素的copy。因此,容器内的元素与我们放进去的元素虽然equal但是并不非identical。如果修改容器的元素,实际上并没有修改原件对象。这就是STL容器所提供的$value$ 语义。然而在现实中,我们有时候需要将原件对象放入容器,而不是它的copy,这是我们就需要$Reference$语义,让容器容纳元素的reference。
1.2两种语义对比
- $Value$语义
- 优点
- 复制元素操作很简单;
- 缺点
- 复制元素效率可能很低,有时甚至无法复制;
- 无法在数个不同的容器中管理同一份对象,即处在不同容器中同一个原件对象的copy没有联系。
- 优点
- $Reference$
- 优点
- 复制元素效率高;
- 可以在不同容器中管理同一份对象;
- 缺点
- 使用和管理相对复杂
- 优点
2.实现Reference语义
最先想到的方法是以pointer作为元素。不过寻常的pointer存在一些问题,可能出现野指针导致不确定情况,另外,pointer之间的比较行为可能常常不是我们所期待的,我们希望比较的是pointer所指的对象而不是pointer本身,所以需要在管理时付出很大的心血,非常谨慎,否则就会进入不确定情况的泥潭。
相对更好的方法是使用smart poniter,这是一种对象,有着类似pointer的接口,但内部做了额外的检查和处理,便于管理;另一种方式是使用$class std::reference_wrapper<>$,让STL容器持有reference。
2.1使用$shared_ptr$智能指针
- 举例说明
#include <iostream>
#include <string>
#include <set>
#include <algorithm>
#include <deque>
#include <memory>
using namespace std;
class Item {
private:
string name;
float price;
public:
Item(const string& n, float p = 0) : name(n), price(p) {
}
string getName() const {
return name;
}
void setName(string& n) {
name = n;
}
float getPrice() const {
return price;
}
void setPrice(float p) {
price = p;
}
};
template <typename T>
void printItems(const std::string& msg, const T& coll) {
cout << msg << endl;
for (const auto& elem : coll) {
cout << ' ' << elem->getName() << ':' << elem->getPrice() << endl;
}
}
int main()
{
typedef shared_ptr<Item> ItemPtr;
set<ItemPtr> allItems;
deque<ItemPtr> bestsellers;
bestsellers = { ItemPtr(new Item("Apple", 5.20)),
ItemPtr(new Item("Banana", 3.21)),
ItemPtr(new Item("Cake", 10.24)) };
allItems = { ItemPtr(new Item("Pizza", 2.22)),
ItemPtr(new Item("People", 0.01)) };
//将bestsellers中的项目插入allItems中,并且对于apple,banana,cake两者应该指向同一个对象.
allItems.insert(bestsellers.begin(), bestsellers.end());
//print1
printItems("bestsellers1:", bestsellers);
printItems("allItems1:", allItems);
cout << endl;
//double price of bestsellers --> allItems中相应的元素也会改变
for_each(bestsellers.begin(), bestsellers.end(),
[](shared_ptr<Item>& elem) {
elem->setPrice(elem->getPrice() * 2);
});
//Banana <--> Pizza
bestsellers[1] = *(find_if(allItems.begin(), allItems.end(),
[](shared_ptr<Item> elem) {
return elem->getName() == "Pizza";
}));
//set price
bestsellers[0]->setPrice(100000.2);
//print2
printItems("bestsellers2:", bestsellers);
printItems("allItems2:", allItems);
cout << endl;
return 0;
}
运行结果
-
评价
如果打算在不同的容器之间共享对象,shared_ptr是合适的选择。不过,使用它也可会让事情变复杂,例如,面对set使用find(),会找出相等value的元素,现在比较的是内部的pointer.所以我们需要使用find_if算法。
allItems.find(ItemPtr(new Item("Pizza", 2.22))) //failed
2.2使用$class std::reference_wrapper<>$
- 举例说明
vector<reference_wrapper<Item>> books; //它的元素是reference
Item f("STL Standard", 20.1);
books.push_back(f);
cout << books[0].get().getName() << ' ' << books[0].get().getPrice() << endl; //get是必要的
f.setPrice(9.99);
cout << books[0].get().getPrice() << endl;
cout << f.getPrice() << endl;
- 评价
假设“只要容器存在,被指向的元素一定存在”,我们就可以使用这种方法。这种方法的优点是不需要pointer语法,然而这也是一共风险,因为此处使用reference并非显而易见。下面的声明不可行。
vector<Item&> books;
标签:容器,Reference,STL,bestsellers,元素,语义,allItems,Item
From: https://www.cnblogs.com/yxdong/p/17048289.html