C++ STL容器的Value语义与Reference语义

C++ STL容器的Value语义与Reference语义

1.Value语义vs.Reference语义

1.1两种语义简述

​ 通常情况下,所有容器都是建立元素的copy,返回的元素的copy。因此,容器内的元素与我们放进去的元素虽然equal但是并不非identical。如果修改容器的元素,实际上并没有修改原件对象。这就是STL容器所提供的$value$ 语义。然而在现实中,我们有时候需要将原件对象放入容器,而不是它的copy,这是我们就需要$Reference$语义,让容器容纳元素的reference。

1.2两种语义对比

  1. $Value$语义
    • 优点
      • 复制元素操作很简单;
    • 缺点
      • 复制元素效率可能很低,有时甚至无法复制;
      • 无法在数个不同的容器中管理同一份对象,即处在不同容器中同一个原件对象的copy没有联系。
  2. $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;
}

运行结果
C++ STL容器的Value语义与Reference语义

  • 评价

    如果打算在不同的容器之间共享对象,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;

C++ STL容器的Value语义与Reference语义

  • 评价

假设“只要容器存在,被指向的元素一定存在”,我们就可以使用这种方法。这种方法的优点是不需要pointer语法,然而这也是一共风险,因为此处使用reference并非显而易见。下面的声明不可行。

vector<Item&> books;

C++ STL容器的Value语义与Reference语义

原文链接: https://www.cnblogs.com/yxdong/p/17048289.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    C++ STL容器的Value语义与Reference语义

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/311421

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月16日 上午11:59
下一篇 2023年2月16日 上午11:59

相关推荐