std vector如何避免频繁内存创建

1. std vector中添加元素

In C++ vectors are dynamic arrays. Unlike arrays, they don’t have a fixed size. They can grow or shrink as required. Vectors are assigned memory in blocks of contiguous locations. When the memory allocated for the vector falls short of storing new elements, a new memory block is allocated to vector and all elements are copied from the old location to the new location. This reallocation of elements helps vectors to grow when required. However, it is a costly operation and time complexity is involved in this step is linear.

vector内存不足的时候,会重新分配内存block,而且会把之前已有的数据拷贝到新的内存。这一系列操作是一个稍微耗时的操作,下面通过实际的例子说明,

#include <iostream>
#include <vector>


class A {
public:
    A(int i) :i_(i) { std::cout << "ptr: [" << this << " ]" << " default ctor" << " i_= " << i_ << std::endl; }
    A(const A& x) {
        std::cout << "ptr: [" << this << " ]" << " copy ctor" << " i_= " << x.i_ << std::endl;
        i_ = x.i_;
    }
    ~A() {
        std::cout << "ptr: [" << this << " ]" << " dector" << " i_= " << i_ << std::endl;
    }
private:
    int i_;
};

int main() {
    std::vector<A> y;
    std::cout << "y.size()= " << y.size() << std::endl;
    //先生成A(1)临时对象,然后在std::vector<A>中的构造(拷贝构造函数),然后A(1)临时对象析构
    y.push_back(A(1));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(A(2));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(A(3));
    std::cout << "y.size()= " << y.size() << std::endl;
    return 0;
};

程序输出的结果,

y.size()= 0
ptr: [0000000B4D58FB74 ] default ctor i_= 1
ptr: [00000116C1FC0850 ] copy ctor i_= 1
ptr: [0000000B4D58FB74 ] dector i_= 1
y.size()= 1
ptr: [0000000B4D58FB78 ] default ctor i_= 2
ptr: [00000116C1FD1A14 ] copy ctor i_= 2
//00000116C1FC0850 又被拷贝到了 00000116C1FD1A10,之前的00000116C1FC0850又析构
ptr: [00000116C1FD1A10 ] copy ctor i_= 1
ptr: [00000116C1FC0850 ] dector i_= 1
ptr: [0000000B4D58FB78 ] dector i_= 2
y.size()= 2
ptr: [0000000B4D58FB7C ] default ctor i_= 3
ptr: [00000116C1FD1BF8 ] copy ctor i_= 3
ptr: [00000116C1FD1BF0 ] copy ctor i_= 1
ptr: [00000116C1FD1BF4 ] copy ctor i_= 2
ptr: [00000116C1FD1A10 ] dector i_= 1
ptr: [00000116C1FD1A14 ] dector i_= 2
ptr: [0000000B4D58FB7C ] dector i_= 3
y.size()= 3
ptr: [00000116C1FD1BF0 ] dector i_= 1
ptr: [00000116C1FD1BF4 ] dector i_= 2
ptr: [00000116C1FD1BF8 ] dector i_= 3

从上面的结果我们能看出来,

  1. 调用vectorpush_back(),先生成A(1)临时对象,然后在std::vector<A>中的构造(拷贝构造函数),然后A(1)临时对象析构;
  2. 当牵扯到内存重新分配的时候,确实会把std::vector<A>中已有的元素重新复制到新的内存,例如结果中的多次调用拷贝构造函数(copy ctor i_= 1)。

2. std move

在c++11中,标准库新加了std::move,即移动语义,它的一个作用是减少对象拷贝的开销,具体少了多少没细研究,这里仅贴出来代码。

#include <iostream>
#include <vector>


class A {
public:
    A(int i) :i_(i) { std::cout << "ptr: [" << this << " ]" << " default ctor" << " i_= " << i_ << std::endl; }
    A(const A& x) {
        std::cout << "ptr: [" << this << " ]" << " copy ctor" << " i_= " << x.i_ << std::endl;
        i_ = x.i_;
    }
    A(const A&& x)noexcept {
        std::cout << "ptr: [" << this << " ]" << " move ctor" << " i_= " << x.i_ << std::endl;
        i_ = x.i_;
    }
    ~A() {
        std::cout << "ptr: [" << this << " ]" << " dector" << " i_= " << i_ << std::endl;
    }
private:
    int i_;
};

int main() {
    std::vector<A> y;
    //y.reserve(10);
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(1)));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(2)));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(3)));
    std::cout << "y.size()= " << y.size() << std::endl;
    return 0;
};

输出结果如下,

y.size()= 0
ptr: [00000080C32FF914 ] default ctor i_= 1
ptr: [000001FA06440850 ] move ctor i_= 1
ptr: [00000080C32FF914 ] dector i_= 1
y.size()= 1
ptr: [00000080C32FF918 ] default ctor i_= 2
ptr: [000001FA06451A04 ] move ctor i_= 2
ptr: [000001FA06451A00 ] move ctor i_= 1
ptr: [000001FA06440850 ] dector i_= 1
ptr: [00000080C32FF918 ] dector i_= 2
y.size()= 2
ptr: [00000080C32FF91C ] default ctor i_= 3
ptr: [000001FA06451968 ] move ctor i_= 3
ptr: [000001FA06451960 ] move ctor i_= 1
ptr: [000001FA06451964 ] move ctor i_= 2
ptr: [000001FA06451A00 ] dector i_= 1
ptr: [000001FA06451A04 ] dector i_= 2
ptr: [00000080C32FF91C ] dector i_= 3
y.size()= 3
ptr: [000001FA06451960 ] dector i_= 1
ptr: [000001FA06451964 ] dector i_= 2
ptr: [000001FA06451968 ] dector i_= 3

和1中的输出结果类似,依然会牵扯内存的移动(前面是拷贝)。

3. 如何解决这个问题

std::vector class provides a useful function reserve which helps user specify the minimum size of the vector.It indicates that the vector is created such that it can store at least the number of the specified elements without having to reallocate memory.

可以利用reserve()函数告诉系统最少准备多少内存,就不用来回分配内存,多次创建、析构了,如下所示,

#include <iostream>
#include <vector>


class A {
public:
    A(int i) :i_(i) { std::cout << "ptr: [" << this << " ]" << " default ctor" << " i_= " << i_ << std::endl; }
    A(const A& x) {
        std::cout << "ptr: [" << this << " ]" << " copy ctor" << " i_= " << x.i_ << std::endl;
        i_ = x.i_;
    }
    A(const A&& x)noexcept {
        std::cout << "ptr: [" << this << " ]" << " move ctor" << " i_= " << x.i_ << std::endl;
        i_ = x.i_;
    }
    ~A() {
        std::cout << "ptr: [" << this << " ]" << " dector" << " i_= " << i_ << std::endl;
    }
private:
    int i_;
};

int main() {
    std::vector<A> y;
    //预留10的空间
    y.reserve(10);
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(1)));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(2)));
    std::cout << "y.size()= " << y.size() << std::endl;
    y.push_back(std::move(A(3)));
    std::cout << "y.size()= " << y.size() << std::endl;
    return 0;
};

输出结果如下,少了很多多余的操作。

y.size()= 0
ptr: [000000954C4FF614 ] default ctor i_= 1
ptr: [00000291C41050A0 ] move ctor i_= 1
ptr: [000000954C4FF614 ] dector i_= 1
y.size()= 1
ptr: [000000954C4FF618 ] default ctor i_= 2
ptr: [00000291C41050A4 ] move ctor i_= 2
ptr: [000000954C4FF618 ] dector i_= 2
y.size()= 2
ptr: [000000954C4FF61C ] default ctor i_= 3
ptr: [00000291C41050A8 ] move ctor i_= 3
ptr: [000000954C4FF61C ] dector i_= 3
y.size()= 3
ptr: [00000291C41050A0 ] dector i_= 1
ptr: [00000291C41050A4 ] dector i_= 2
ptr: [00000291C41050A8 ] dector i_= 3
上一篇:汉诺塔问题--Java实现


下一篇:一个小游戏