参考push_back(std::move(string))与emplace_back(string)_string emplaceback-CSDN博客
在C++中,性能消耗是复制>移动>构造。
这是因为:
复制(Copy)操作会创建对象的一个完整副本,这涉及到分配新的内存和复制现有对象的数据,这是三者中最耗费资源的操作。
移动(Move)操作则是将一个对象的资源转移到另一个对象,这通常只涉及到修改指针或其他简单的内部状态,而不是复制整个数据,因此比复制要快。
构造(Construct)操作直接在预分配的内存空间中创建对象,通常是最高效的,因为它避免了不必要的内存分配和数据复制。
因此,当可能的时候,优先使用构造器(如emplace_back)来减少性能消耗吗?
push_back() & emplace_back()
先来看看两者的区别,以下是push_back的简单版本,总体,一个复制版本,一个移动版本
void pushback(const T& value)
{
if (m_Size >= m_Capacity)
ReAlloca(m_Capacity + m_Capacity / 2);
m_Data[m_Size] = value;
m_Size++;
}
void pushback(T&& value)
{
if (m_Size >= m_Capacity)
ReAlloca(m_Capacity + m_Capacity / 2);
m_Data[m_Size] =std::move(value);
m_Size++;
}
而emplace_back用到了完美转发和placement new,emplace_back的设计目的是在容器的内存空间中直接构造对象,这意味着emplace_back可以直接使用构造函数的参数,在容器的末尾就地构造新元素。
当你调用emplace_back时,它会将参数完美转发(perfect forwarding)到元素的构造函数,允许直接在容器的内存中构造对象。这避免了创建临时对象和额外的移动或复制操作,因此通常比push_back更高效。
template<typename...Args>
T& emplaceback(Args&&...args)
{
if (m_Size >= m_Capacity)
ReAlloca(m_Capacity + m_Capacity / 2);
m_Data[m_Size] = T(std::forward<Args>(args)...);
new(&m_Data[m_Size]) T(std::forward<Args>(args)...);
return m_Data[m_Size++];
}
在大多数情况下,直接构造对象的确比移动对象要高效。但是,这里的关键点在于emplace_back和push_back(std::move())之间的选择取决于上下文和对象的状态。
已经创建了一个string对象。在这种情况下,使用push_back(std::move(string))是合适的,因为:已经拥有一个完整的string对象,并且在添加到vector之后不再需要它。
使用std::move可以避免复制,因为它将string对象的所有权移动到vector中,而不是创建一个新的副本。
另一方面,emplace_back更适合于直接使用构造函数参数来构造新元素的场景。如果您的场景是从一组参数开始,直接在vector中构造一个新对象,那么emplace_back将是更优的选择,因为它可以避免任何复制或移动操作。
push_back(std::move(string))可以减少不必要的性能开销。这并不意味着emplace_back不高效,而是在这种特定情况下,push_back(std::move(string))更符合您的需求。如果是从一开始就在vector中构造新元素,那么emplace_back将是更合适的选择。
总结一下:
如果你想要添加一个已经存在的对象,并且想要避免复制,那么使用push_back(std::move(string))是合适的。
如果你想要直接在vector中构造一个新元素,那么使用emplace_back并传递构造新元素所需的参数是更好的选择。