一、C++标准库提供的是value语义
- 通常,所有容器都会建立元素拷贝,返回的也是元素的拷贝
- STL只支持value语义,不支持reference语义。优缺点如下:
- 优点:
- 复制元素很简单
- 使用reference时容易出错,你必须确保reference所指对象仍然健在,并需要小心队服偶尔出现的环式指向状态
- 缺点:
- 复制元素可能会导致不良的效率,有时甚至无法复制
- 无法在数个不同的容器中管理同一份对象
- 实现reference语义的两种做法:
- ①使用智能指针
- ②使用reference_wrapper
二、使用Shared Pointer
- C++标准库提供了许多智能指针。如果你打算在不同的容器之间共享对象,shared_ptr<>是一个适当的选择
演示案例
- 创建了一个Item类和一个printItems()函数用来打印类的内容
class Item {
private:
std::string name;
float price;
public:
Item(const std::string& n, float p = 0) :name(n), price(p) {}
std::string getName()const { return name; }
void setName(const std::string& n) { name = n; }
float getPrice()const { return price; }
void setPrice(float p) { price = p; }
};
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()
{
typedef std::shared_ptr<Item> ItemPtr;
std::set<ItemPtr> allItems;
std::deque<ItemPtr> bestsellers;
//为bestsellers添加元素
bestsellers = { ItemPtr(new Item("Kong Yize",20.10)),
ItemPtr(new Item("A Midsummer Night's Dream",14.99)),
ItemPtr(new Item("The Maltese Falcon",9.88))
};
//为allItems添加元素
allItems = { ItemPtr(new Item("Water",0.44)),
ItemPtr(new Item("Pizza",2.22))
};
//将bestsellers内的所有元素添加进allItems
allItems.insert(bestsellers.begin(), bestsellers.end());
//打印一下bestsellers和allItems内的元素
printItems("bestsellers", bestsellers);
printItems("allItems", allItems);
std::cout << std::endl;
//遍历bestsellers,将其内部的每一个对象的price加倍
for_each(bestsellers.begin(), bestsellers.end(),
[](std::shared_ptr<Item>& elem) {elem->setPrice(elem->getPrice() * 2);}
);
//将bestsellers[1]的元素更改为allItems内名为“Pizza”的元素
bestsellers[1] = *(
find_if(allItems.begin(), allItems.end(),
[](std::shared_ptr<Item> elem) {return elem->getName() == "Pizza"; })
);
//将bestsellers[0]对象的price设置为44.77
bestsellers[0]->setPrice(44.77);
//打印一下bestsellers和allItems内的元素
printItems("bestsellers", bestsellers);
printItems("allItems", allItems);
}
- 使用智能指针为set<>和deque<>创建元素,其内的每个元素都是智能指针。后面对set<>和deque<>进行了一系列的操作,由于智能指针是共享的,因此改变一者,另一者也改变
- 注意事项:使用shared_ptr<>会使事情变得复杂。例如,set<>容器内存储的是智能指针之后,不能再使用find()算法了,因此find()算法会找出拥有相等value的元素,现在比较的却是内部(由new返回)的pointer,因此只能使用find_if()算法
三、使用Reference Wrapper
- reference_wrapper在之前的文章介绍过,参阅:
- reference_wrapper可以将传值调用改为传reference调用
- 假设保证“只要容器存在,被指向的元素一定存在”,那就可以使用reference_wrapper。见下面的演示案例
演示案例
- 此演示案例仍然使用到上面的Item类
int main()
{
std::vector<std::reference_wrapper<Item>> books;
Item f("Faust", 12.99);
books.push_back(f); //将f的引入传递给books,现在books内存放的是f的引用
for (const auto& book : books) {
std::cout << book.get().getName() << ": " << book.get().getPrice() << std::endl;
}
//通过f改变内容
f.setPrice(9.99);
std::cout << books[0].get().getPrice() << std::endl;
//可以看到vector内的也改变了
for (const auto& book : books) {
std::cout << book.get().getName() << ": " << book.get().getPrice() << std::endl;
}
}
- 这种做法的优点是不需要pointer语法。然而这个也有风险,因此此处使用reference而非显然意见
- 注意,下面的声明是不行的,因此其不是引用: