浅析vector容器(1)-vector内存分配策略

版权声明:原创文章,欢迎转载,但请注明出处,谢谢。
https://blog.csdn.net/qiuguolu1108/article/details/107146184

浅析vector容器(1)-vector内存分配策略

浅析vector容器(2)-减少vector内存占用

浅析vector容器(3)-使用移动语义提高性能


vector是一个封装了动态大小数组的顺序容器,它能够存放各种类型的对象。 可以删除元素、可以插入元素、可以查找元素,做这些工作我们无需管理容器内存。容器内存管理,这种脏活累活全部交由vector管理。了解一下vector的内存管理策略,能够更加充分的利用内存。

1、定义一个用于测试的类

class A
{
public:
    A(int data = 100)
        :data_(data)
    {
        construct_count_++;
        cout<<"constructor : "<<this<<endl;
    }

    A(const A& a)
    {
        copy_construct_count_++;
        cout<<this<<" : copy constructor form : "<<&a<<endl;
    }

    static void dis_construct_count()
    {
        cout<<"construct count: "<<construct_count_<<endl;
    }

    static void dis_copy_construct_count()
    {
        cout<<"copy construct count: "<<copy_construct_count_<<endl;
    }

    void display()
    {
        cout<<data_<<endl;
    }

    ~A()
    {
        cout<<"deconstructor : "<<this<<endl;
    }

private:
    int data_;
    static int construct_count_;
    static int copy_construct_count_;
};

int A::construct_count_ = 0;
int A::copy_construct_count_ = 0;

定义一个类,加上一些测试打印信息,帮助我们测试vector。

2、vector内存分配策略

vector是一个动态数组,它会根据元素的个数,适当的去申请内存。可以简单的把vector理解为,其内部有一个void*指针,用于指向在堆上申请的空间。void*指向的空间用于存放vector元素。vector一直维护着void*空间的大小,当void*指向的堆空间没有空间存放新插入的元素时,它都会去系统申请之前空间大小的两倍空间,并把之前的元素全部拷贝到新的空间,释放掉原空间,在新空间中插入新的元素。
在这里插入图片描述

3、vector使用示例

通过一个vector示例,说明vector是如何管理内存的。

vector<A> va;

A a,b,c,d,e;

cout<<endl<<"======================"<<endl;
va.push_back(a);

cout<<endl<<"======================"<<endl;
va.push_back(b);

cout<<endl<<"======================"<<endl;
va.push_back(c);
va.push_back(d);

cout<<endl<<"======================"<<endl;
va.push_back(e);

cout<<endl;

A::dis_construct_count();
A::dis_copy_construct_count();

cout<<endl;

运行结果如下图:
在这里插入图片描述

结合运行的结果,分析一下这些结果是怎么打印出来的。

3.1 生成vector<A> va容器对象
vector<A> va;

这条语句仅生成了va对象,并没有为va对象分配堆上空间。可以假设其内部的数据成员A*指向了nullptr。

在这里插入图片描述

3.2 构造5个A类型的对象

在这里插入图片描述

A a,b,c,d,e;

这个运行结果很好理解,就是通过构造器生成了5个类A对象。

3.3 向vector中推入对象a
cout<<endl<<"======================"<<endl;
va.push_back(a);

在这里插入图片描述
在向vector中推入元素之前,vector还没有分配保存类A对象的空间,再调用push_back()的时候,发现vector空间为空,则先去堆上申请一个空间,用于存放对象a。再将对象a存入vector的时候,发生了一次拷贝构造。
在这里插入图片描述

3.4 向vector中推入对象b
cout<<endl<<"======================"<<endl;
va.push_back(b);

在这里插入图片描述
再次向vector推入对象b,push_back()函数发现va容器中没有空间了,则它会再向系统申请两倍之前的空间。将之前的元素a拷贝到新空间,并将新元素b插入到a元素之后。最后将原来的空间释放掉,也就是原来的a对象会被释放掉。
在这里插入图片描述

3.5 向vector中推入对象c、d
    cout<<endl<<"======================"<<endl;
    va.push_back(c);
    va.push_back(d);

在这里插入图片描述

向va容器推入c对象,push_back()发现va没有空间了,现在容器中有两个元素,按照内存分配策略,这次push_back()内部的函数会向系统申请4个元素的空间,并把之前容器中的元素a、b全部拷贝到新空间,并释放原空间。之后插入元素c。再次插入d对象时,此时va容器正好有一个元素空间空闲,所以刚好把d对象放下。
在这里插入图片描述
在这里插入图片描述

3.6 向vector中推入对象e
cout<<endl<<"======================"<<endl;
va.push_back(e);

在这里插入图片描述
插入e对象的过程,和上面类似,如下图:
在这里插入图片描述

3.7 对象a、b、c、d、e和va容器的析构

在这里插入图片描述
类A对象的析构,直接调用其析构器就可以了。va容器的析构,首先要调用容器元素的析构器,再释放va所占用的空间。

3.8 vector会发生元素的大量搬动
vector<A> va;

A a;

for(int i=0;i<1024;i++)
{
    va.push_back(a);
}

A::dis_construct_count();
A::dis_copy_construct_count();

在这里插入图片描述
理想的情况下,应该只发生1024次拷贝构造,但这里却发生了2047次拷贝,多了近一半的"多余"拷贝构造。这不是我们想要的,大量的搬动元素,会早造成性能降低。

4、size()和capacity()函数

vector的成员函数size(),表示容器中实际存放元素的个数。capacity()函数表示vector此刻总共可以容纳元素的个数。其中capacity()函数的返回值才是vector实际占用空间的大小。

vector<A> va;

A a,b,c,d,e;

va.push_back(a);
va.push_back(b);
va.push_back(c);
va.push_back(d);
va.push_back(e);

cout<<"size     = "<<va.size()<<endl;
cout<<"capacity = "<<va.capacity()<<endl;

在这里插入图片描述
根据上面的分析,我们可知,现在的va向系统申请了8个可以存放类A的空间,其中va只使用了前5个空间,最后3个空间并没有使用。

重点capacity()才是vector占用的实际空间,size()仅是vector使用空间的个数,极端情况下capacity()是size()的两倍左右。

vector<int> vi;

for(int i=0;i<1024+1;i++)
{
    vi.push_back(i);
}

cout<<"size     = "<<vi.size()<<endl;
cout<<"capacity = "<<vi.capacity()<<endl;

在这里插入图片描述

虽然存放了1025个元素,但vi容器却占用着2048个元素,空间有些浪费。

5、总结

本文通过一个示例,介绍了vector内存分配的策略。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
docker-compose 是 Docker 官方的一个用于定义和运行容器化应用的工具。它使用 YAML 文件来配置应用的服务、网络和卷等方面的设置。 当使用 docker-compose 部署 MySQL 时,可能会遇到无法访问 MySQL 的问题。出现这种情况一般有以下几个可能的原因: 1. 网络配置问题:docker-compose 在默认情况下会创建一个默认的网络,并将所有定义的服务连接到该网络。如果服务的网络配置不正确,可能导致无法访问 MySQL。可以通过检查网络配置或创建自定义网络来解决此问题。 2. 端口映射问题:MySQL 默认使用 3306 端口进行通信,但是在容器内部的端口与宿主机上的端口之间可能存在映射问题。可以通过检查端口映射配置或使用容器的 IP 地址来解决此问题。 3. 认证问题:MySQL 服务通常需要进行身份验证才能访问。在 docker-compose 文件中,可以通过设置环境变量来指定 MySQL 的用户名和密码。如果未正确设置这些环境变量,可能导致无法访问 MySQL。可以检查环境变量配置或者在容器内部手动配置用户名和密码来解决此问题。 4. 容器启动顺序问题:如果在 docker-compose 文件中定义了多个服务,并且它们之间有依赖关系,那么容器启动的顺序可能会影响 MySQL 的访问。可以通过在容器之间添加依赖或者设置延迟启动来解决此问题。 总结起来,当 docker-compose 部署的 MySQL 无法访问时,通常是由于网络配置、端口映射、认证配置或容器启动顺序等问题造成的。通过检查这些配置,并进行适当的调整或修复,通常可以解决无法访问 MySQL 的问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值