Tip of the Week #65: Putting Things in their Place
Posted zhangyifei216
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tip of the Week #65: Putting Things in their Place相关的知识,希望对你有一定的参考价值。
Tip of the Week #65: Putting Things in their Place
Originally posted as totw/65 on 2013-12-12
By Hyrum Wright (hyrum@hyrumwright.org)
“Let me ’splain. No, there is too much. Let me sum up.” –Inigo Montoya
C++11
中添加了一种新方式往标准容器中插入元素,那就是emplace()
系列方法了。这些方法会直接在容器中创建对象,避免创建临时对象,然后通过拷贝或者移动对象到容器中。这对于几乎所有的对象来说避免了拷贝,更加高效。尤其是对于往标准容器中存储只能移动的对象(例如std::unique_ptr
)就更为方便了。
The Old Way and the New Way
让我们通过使用vector
来存储的一个简单的例子来对比下两种方式。第一个例子是C++11之前的编码风格:
class Foo
public:
Foo(int x, int y);
…
;
void addFoo()
std::vector<Foo> v1;
v1.push_back(Foo(1, 2));
通过使用老的push_back
方法,会导致Foo对象被构造两次,一次是临时构造一个Foo对象,然后将临时对象进行移动构造,放到容器中。
我们可以使用C++11
引入的emplace_back()
,这种方式只会引入一个对象的构造,是直接在vector
容器元素所在内存上构造。正是由于emplace
系列函数将其参数直接转发给底层对象的构造函数,因此我们可以直接提供构造函数参数,从而无需创建临时的Foo
对象。
void addBetterFoo()
std::vector<Foo> v2;
v2.emplace_back(1, 2);
Using Emplace Methods for Move-Only Operations
到目前为止,我们已经研究过emplace
方法可以提高性能的情况,此外它可以让之前不能工作的代码可以正常工作,例如容器中的类型是只能被移动的类型像std::unique_ptr
。考虑下面这段代码:
std::vector<std::unique_ptr<Foo>> v1;
如何才能向这个容器中插入一个元素呢? 一种方式就是通过push_back
直接在参数中构造对象:
v1.push_back(std::unique_ptr<Foo>(new Foo(1, 2)));
这种语法有效,但可能有点笨拙。不幸的是,解决这种混乱的传统方式充满了复杂性:
Foo *f2 = new Foo(1, 2);
v1.push_back(std::unique_ptr<Foo>(f2));
上面这段代码可以编译,但是它使得在被插入前,指针的所有权变的不清晰。甚至更糟糕的是,vector
拥有了该对象,但是f2
仍然有效,并且有可能在此后被删除。对于不知情的读者来说,这种所有权模式可能会令人困惑,特别是如果构造和插入不是如上所述的顺序事件。
其他的解决方案甚至都无法编译,因为unique_ptr
是不能被拷贝的:
std::unique_ptr<Foo> f(new Foo(1, 2));
v1.push_back(f); // Does not compile!
v1.push_back(new Foo(1, 2)); // Does not compile!
使用emplace
方法会使得对象的创建更为直观,如果你需要把unique_ptr
放到vector
容器中,可以像下面这样通过std::move
来完成:
std::unique_ptr<Foo> f(new Foo(1, 2));
v1.emplace_back(new Foo(1, 2));
v1.push_back(std::move(f));
通过把emplace
和标准的迭代器结合,可以将对象插入到vector
容器中的任何位置:
v1.emplace(v1.begin(), new Foo(1, 2));
实际上,我们不希望看到上述构造unique_ptr的这些方法,而是希望通过std::make_unique
(C++14),或者是absl::make_unique
(C++11)。
Conclusion
本文使用vector
来作为example中的标准容器,实际上emplace
同样也适用于map
、list
以及其它的STL容器。当unique_ptr
和emplace
结合,使得在堆上分配的对象其所有权的语义更加清晰。希望通过本文能让您感受到新的容器方法的强大功能,以及满足在您自己的代码中适当使用它们的愿望。
以上是关于Tip of the Week #65: Putting Things in their Place的主要内容,如果未能解决你的问题,请参考以下文章
Tip of the Week #24: Copies, Abbrv
Tip of the Week #59: Joining Tuples
Tip of the Week #11: Return Policy
Tip of the Week #64: Raw String Literals