vector中push_back()和emplace_back()的区别

根据cppreference

push_back()分为两步

1) 新元素被初始化为值的副本。 
2) 值被移动到新元素中。

如果传入的是vector存储类型的值,那么将根据具体实现(赋值构造和移动构造)将元素移动到对应的位置

class MyClass {
public:
    // 默认构造函数
    MyClass() :num(0), st("default") {
        std::cout << "Constructed with default constructor." << std::endl;
    }

    MyClass(int num, string st) :num(num), st(st) {
        std::cout << "Constructed with default constructor." << std::endl;
    }

    // 析构函数
    ~MyClass() {
        std::cout << "Destructed." << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) :num(other.num), st(other.st) {
        std::cout << "Constructed with copy constructor." << std::endl;
    }

    // 拷贝赋值运算符
    MyClass& operator=(const MyClass& other) {
        std::cout << "Copy assignment operator called." << std::endl;
        if (this != &other) {  // 避免自赋值
            num = other.num;
            st = other.st;
        }
        return *this;
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept :num(std::move(other.num)), st(std::move(other.st)) {
        std::cout << "Constructed with move constructor." << std::endl;
    }

    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        std::cout << "Move assignment operator called." << std::endl;
        if (this != &other) {
            num = std::move(other.num);
            st = std::move(other.st);
        }
        return *this;
    }
private:
    string st;
    int num;
};
int main(){
    vector<MyClass> v;
    // 防止插入的过程中发生扩容,大量调用类的移动构造
    v.reserve(10);

    // 调用默认构造
    auto a = MyClass();

    // 传递左值, 调用拷贝构造
    v.push_back(a);

    // 传递右值, 调用移动构造
    v.push_back(std::move(a));
}

/*
实验结果:
Constructed with default constructor.
Constructed with copy constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/

如果传入的是存储类型的构造参数,且类支持隐式构造,那么将先进行隐式构造(构造完是右值),后移动到vector末尾

int main(){
    vector<MyClass> v;
    // 防止插入的过程中发生扩容,大量调用类的移动构造
    v.reserve(10);

    // 单参数和双参数的隐式构造
    v.push_back(1);
    v.push_back({2, "xxx"});
    
}
/*
Constructed with default constructor.
Constructed with move constructor.
Destructed.
Constructed with default constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/

对于emplace_back()

"该元素是通过 std::allocator_traits::construct 构造的,通常使用placement-new 在容器提供的位置就地构造元素。参数 args... 作为 std::forward<Args>(args)... 转发到构造函数。"

意思就是,emplace_back()会首先尝试使用存储元素类型的构造函数,就地构造

而传入emplace_back()的参数会完美转发给构造函数

int main(){
    vector<MyClass> v;
    // 防止插入的过程中发生扩容,大量调用类的移动构造
    v.reserve(10);

    // 单参数和双参数的隐式构造
    v.emplace_back(1);
    v.emplace_back(2, "xxx");

}
/*
Constructed with default constructor.
Constructed with default constructor.
Destructed.
Destructed.
*/

以上,只需要一次构造就可以,并不需要将元素”移动“到vector指定位置。

除了就地构造外,和push_back()是相同的

int main(){
    vector<MyClass> v;
    // 防止插入的过程中发生扩容,大量调用类的移动构造
    v.reserve(10);

    auto a = MyClass();

    // 传递左值, 调用拷贝构造
    v.emplace_back(a);

    // 传递右值, 调用移动构造
    v.emplace_back(std::move(a));

}
/*
Constructed with default constructor.
Constructed with copy constructor.
Constructed with move constructor.
Destructed.
Destructed.
Destructed.
*/

需要注意的是

    // 不报错
    v.emplace_back(1);
    // 报错 error: no matching function for call to 'std::vector<MyClass>::emplace_back(<brace-enclosed initializer list>)'
    v.emplace_back({1, "xx"});

这是因为,emplace_back()首先会尝试使用存储对象的构造函数原地构造,而{1, "xx"}会自动转换成initializer_list,并经过完美转发,MyClass并没有接受参数initializer_list的构造参数,报错

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值