emplace_back & push_back

之前往顺序容器中添加元素,基本使用的都是push_back,使用push_back其实是先创建元素,在将元素拷贝到容器中,c11后,push_back可以选择的是创建完元素后,是将元素拷贝or移动到容器内。c11除了这个方面的优化外,还提供了一个添加元素的方法emplace_back:使用该接口将会直接在容器尾部创建元素,不会拷贝or移动。这样对性能是有提升的。看下下面的例子

class emplace_test {
public:
    emplace_test(std::string str) : str_(std::move(str)) {
        std::cout << "emplace_test construct." << std::endl;
    };
    emplace_test(const emplace_test& other) : str_(std::move(other.str_)) {
        std::cout << "emplace_test copy construct." << std::endl;
    }
    emplace_test(const emplace_test&& other) : str_(std::move(other.str_)) {
        std::cout << "emplace_test move construct." << std::endl;
    }
    emplace_test& operator=(const emplace_test& other) {
        str_ = other.str_;
        std::cout << "emplace_test copy =." << std::endl;
        return *this;
    }
private:
    std::string str_;
};
int main() {
    std::vector<emplace_test> vec;
    vec.reserve(4);
    vec.emplace_back(string("222"));
    vec.push_back(string("111"));
}

emplace_test construct.
emplace_test construct.
emplace_test move construct.

可以看到emplace_back只有构造函数,而没有拷贝or移动。push_back是除了构造还多了移动

上述示例中,vec.reserve(4);是为了提前申请好内存,否则会可能由于容器容量不够导致重新申请内存,而结果展示就会具有迷惑性如:

//vec.reserve(4);

emplace_test construct.
emplace_test construct.
emplace_test move construct.
emplace_test copy construct.

多了一次拷贝构造,我们可以加日志

class emplace_test {
public:
    emplace_test(std::string str) : str_(std::move(str)) {
        std::cout << "emplace_test construct." << str_ << std::endl;
    };
    emplace_test(const emplace_test& other) : str_(std::move(other.str_)) {
        std::cout << "emplace_test copy construct." << str_ << std::endl;
    }
    emplace_test(const emplace_test&& other) : str_(std::move(other.str_)) {
        std::cout << "emplace_test move construct." << str_ << std::endl;
    }
    emplace_test& operator=(const emplace_test& other) {
        str_ = other.str_;
        std::cout << "emplace_test copy =." << std::endl;
        return *this;
    }
private:
    std::string str_;
};
int main() {
    std::vector<emplace_test> vec;
    //vec.reserve(4);
    std::cout << "vec capacity is "<< vec.capacity() << std::endl;
    vec.emplace_back(string("222"));
    std::cout << "vec capacity is " << vec.capacity() << std::endl;
    vec.push_back(string("111"));
    std::cout << "vec capacity is " << vec.capacity() << std::endl;
}

vec capacity is 0
emplace_test construct.222
vec capacity is 1
emplace_test construct.111
emplace_test move construct.111
emplace_test copy construct.222
vec capacity is 2

我们可以看到是因为容量不够,而导致重新申请内存,所以一个优化点是使用vector的时候,我们可以预先指定vector的容量,防止前期添加元素导致频繁的重新申请内存,一个简单测试看看vector的容量增长

    std::vector<int> vec;
    for (int i = 0; i < 10; i++) {
        vec.emplace_back(i);
        std::cout << "vec capacity is " << vec.capacity() << std::endl;
    }

vec capacity is 1
vec capacity is 2
vec capacity is 3
vec capacity is 4
vec capacity is 6
vec capacity is 6
vec capacity is 9
vec capacity is 9
vec capacity is 9
vec capacity is 13

可以看到在一个简单的十次循环中,vector重新申请内存的次数也是不太能接受的。所以可以根据自己的业务特点,适当的提前规划好vector的容量还是很有必要的

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容