我应该在指针容器中使用C++11的emplace_back吗?

34

有一个通常的基类 -> 派生类层次结构,例如:

class Fruit { ... };
class Pear : Fruit { ... };
class Tomato : Fruit { ... };

std::vector<Fruit*> m_fruits;

使用emplace_back而不是push_back是否有意义(例如:性能是否更好)?

std::vector::emplace_back( new Pear() );
std::vector::emplace_back( new Tomato() );

2
你不会知道它的性能表现,直到你进行基准测试/分析。 - Nicu Stiurca
9
使用std::vector<std::unique_ptr<Fruit>>可以避免内存泄漏问题,这样答案就显而易见了。 - ipc
2
我了解emplace_back会构造对象,但是对于指针的情况,我不知道是否有意义。 - Zhen
@Zhen,指针是一个对象。它所指向的是另一个对象。 - Caleth
2个回答

35

不要使用裸指针,使用 std::unique_ptr 来替代,像这样:

std::vector<std::unique_ptr<Fruit>> m_fruits;

由于你不能复制构造一个 std::unique_ptr,所以必须使用 emplace_back(虽然你可以使用 push_backstd::move)。

m_fruits.emplace_back(new Pear());
m_fruits.emplace_back(new Tomato());

编辑:

看起来如果 std::vector 需要并且无法重新分配内存的话,std::vector<std::unique_ptr<T>>::emplace_backnew 就会泄漏。因此,在 C++14 引入 std::make_unique 之前,我建议使用以下方式的 push_back

m_fruits.push_back(std::unique_ptr<Fruit>(new Pear()));
m_fruits.push_back(std::unique_ptr<Fruit>(new Tomato()));

或者使用std::make_unique

m_fruits.push_back(std::make_unique<Pear>());
m_fruits.push_back(std::make_unique<Tomato>());

在C++17和C++20中,Edit中的方法仍然有效吗? - r0n9
1
@r0ng 是的,据我所知,在C++17和C++2a中,如果向vector添加新元素时无法分配内存,则vec.emplace_back(new Object())仍然可能会泄漏。请使用编辑中的建议。 - Felix Glas
使用emplace_back和make_unique有什么缺点吗?例如:m_fruits.emplace_back (std::make_unique<Tomato>()); @FelixGlas - Defisher
1
在实践中,不会。但是这有点滥用API,因为emplace_back是用于原地构造的,并且正在使用元素类型的ctor参数。例如,如果Fruit是一个具体类,需要一个参数const std::string&name,那么您将调用m_fruits.emplace_back("Pear")等。回到示例 - 调用m_fruits.emplace_back(std::make_unique<Pear>())将在幕后将参数传递给元素std::unique_ptr<Pear>(arg)的原地构造,即(不必要地)使用元素类型的复制/移动ctor。 - Felix Glas

27

指针是标量类型,因此是字面类型,所以从左值或右值进行的复制、移动和原位构造都是等效的,通常会编译为相同的代码(标量复制)。push_back 更清晰表示正在执行标量复制,而 emplace_back 应该保留给调用非复制或移动构造函数(例如转换或多参数构造函数)的原位构造。

如果您的向量应该保存 std::unique_ptr<Fruit> 而不是原始指针(以防止内存泄漏),则由于正在调用转换构造函数,emplace_back 将更正确。但是,如果扩展向量失败,则仍然可能发生泄漏,因此在这种情况下,应使用 push_back(make_unique<Pear>()) 等。


make_unique不在标准库中,而且看起来emplace_back与push_back(std::move(X))相同[我正在使用gcc 4.7]。 - Zhen
6
@Zhen:看起来是这样,但实际上不是。emplace_back(new T()) 可能会泄漏,而 push_back(make_unique<T>()) 不会。是的,make_unique 因疏忽被遗漏了,你可以在网上找到其它实现方式。 - GManNickG

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接