# STL vector容器emplace_back和push_back的区别

这个问题可以讲是C++面试问到的比较频繁的问题了,今天复习C++类的默认生成的函数时,使用例子来说明下这两个函数的区别。

# C++的三法则和五法则

C++11以前,如果一个类需要析构函数,则一定需要拷贝构造函数和拷贝赋值操作符,譬如类包含指针成员时。这个是三法则。

C++11后又添加了移动语义支持的移动构造函数和移动赋值操作符。因此扩充后称为五法则。

C++11及以后,编译器会默认为类生成8个函数,分别是:

  • 默认构造函数
  • 默认析构函数
  • 默认复制构造函数
  • 默认赋值运算符重载函数
  • 默认移动构造函数
  • 默认移动赋值运算符重载函数
  • 默认取地址函数
  • 默认取地址const函数

# 比较emplace_backpush_back函数

  • push_backemplace_back都是往vector容器追加一个元素
  • push_back是复制或者移动一个对像到vector
  • emplace_back支持直接调用对像的构造函数,避免像push_back那样的复制和移动。

代码比较:

先定义Object类和包含vector成员的MultObjects

class Object {
    public:
    Object() {
        std::cout << "Object created.\n";
    }
    ~Object() {
        std::cout << "Object deleted.\n";
    }
    Object(const Object& obj) 
    {
        std::cout << "Copy constructor.\n";
    }
    Object& operator=(const Object &obj) {
        std::cout << "Copy Assignment.\n";
        return *this;
    }
    Object(Object &&obj) {
        std::cout << "Move Constuctor.\n";
    }
    Object& operator=(const Object &&obj) {
        std::cout << "Move Assignment.\n";
        return *this;
    }
};

struct MultObjects
{
    using Objs = std::vector<Object>;
    Objs objects;
    MultObjects(Objs objs) : objects{std::move(objs)}{}
};

注意,MultObjects包含一个vector成员,构造函数接受vector类型值是通过move来初始化的,这样可以直接将传入的值转化为右值并赋值给成员变量,避免了对像复制。

常规构造push_back操作:

int main(void)
{
    std::vector<Object> objects;
    Object obj;
    objects.push_back(obj);
    MultObjects mult_objects(objects);
    return 0;
}

以上程序运行的输出结果为:

Object created.
Copy constructor.
Copy constructor.
Object deleted.
Object deleted.
Object deleted.

可以看到对像被创建后复制了两次。

改进1:构造MutlObjects对像时传入move转化的值。

int main(void)
{
    std::vector<Object> objects;
    Object obj;
    objects.push_back(obj);
    MultObjects mult_objects(std::move(objects));
    return 0;
}

程序编译运行后的输出:

# Object created.
# Copy constructor.
# Object deleted.
# Object deleted.

可见对像创建一次,复制一次,比改进前少复制了一次。

改进2:使用emplace_back

int main(void)
{
    std::vector<Object> objects;
    objects.emplace_back();
    MultObjects mult_objects(std::move(objects));
    return 0;
}

编译运行后程序的输出:

# Object created.
# Object deleted.

可以看到,使用emplace_back后,没有复制也没有移动,直接调用了对像的构造函数。

# 总结

C++标准库提供了emplace_back函数,可以直接调用对像的构造函数,避免不必要的复制和移动。

# reference