C++ 动态数组_c++动态数组-程序员宅基地

技术标签: C++  c++  后端  开发语言  

C++语言和标准库提供了两种一次分配一个对象数组的方法。C++语言定义了另一种new表达式语法,可以分配并初始化一个对象数组。标准库中包含一个名为allocator的类,允许我们将分配和初始化分离。使用allocator通常会提供更好的性能和更灵活的内存管理能力。

new和数组

为了让new分配一个对象数组,我们要在类型名之后跟一对方括号,在其中指明要分配的对象的数目。在下例中,new分配要求数量的对象并(假定分配成功后)返回指向第一个对象的指针:

int get_size_new()
{
    
    return 42;
}
void new_array()
{
    
    int *p_array = new int[get_size_new()]();
    for (int i = 0; i < get_size_new(); i++)
    {
    
        cout << *(p_array + i) << " ";
    }

    delete[] p_array;
    cout << endl;
}

在main函数中调用new_array会输出42个0,因为new 分配的数组初始值都为0。
为了释放动态数组,我们使用一种特殊形式的delete——在指针前加上一个空方括号对.
方括号中的大小必须是整型,但不必是常量。也可以用一个表示数组类型的类型别名,来分配一个数组,这样,new表达式中就不需要方括号了:

void new_array()
{
    
    //定义数组类型
    typedef int array_type[10];
    //动态开辟数组空间
    int *p_array = new (array_type);
    delete[] p_array;
}

虽然我们通常称new T[]分配的内存为“动态数组”,但这种叫法某种程度上有些误导。
当用new分配一个数组时,我们并未得到一个数组类型的对象,而是得到一个数组元素类型的指针。
即使我们使用类型别名定义了一个数组类型,new也不会分配一个数组类型的对象。new返回的是一个元素类型的指针。
由于分配的内存并不是一个数组类型,因此不能对动态数组调用begin或end。
要记住我们所说的动态数组并不是数组类型,这是很重要的。
可以通过{}初始化动态数组

void new_array()
{
    
    //通过{}初始化动态数组
    int *p_array = new int[10]{
    1, 2, 3, 4};
    //释放动态数组
    delete[] p_array;
}

如果{}初始化列表小于数组长度,则默认补充空值,int补充0,string补充空字符串。
动态分配一个大小为0的数组是合法的

void new_array()
{
    
    int n = 0;
    //开辟一个大小为0的数组
    int *p_array = new int[n];
    for (int *p = p_array; p != n + p_array; p++)
    {
    
        cout << *p << " ";
    }

    delete[] p_array;
}

当n为0时,开辟了一个长度为0的动态数组,因为循环条件p != n+p_array,所以不会进入循环。
当我们用new分配一个大小为0的数组时,new返回一个合法的非空指针。此指针保证与new返回的其他任何指针都不相同。
对于零长度的数组来说,此指针就像尾后指针一样,我们可以像使用尾后迭代器一样使用这个指针。
可以用此指针进行比较操作,就像上面循环代码中那样。可以向此指针加上(或从此指针减去)0,也可以从此指针减去自身从而得到0。但此指针不能解引用——毕竟它不指向任何元素。

智能指针和动态数组

标准库提供了一个可以管理new分配的数组的unique_ptr版本。为了用一个unique_ptr管理动态数组,我们必须在对象类型后面跟一对空方括号:

void unique_array()
{
    
    //开辟一个20个整形的动态数组,用unique_ptr管理它。
    auto unarray = unique_ptr<int[]>(new int[20]);
    //释放这个动态数组
    unarray.release();
}

类型说明符中的方括号<int[]>指出up指向一个int数组而不是一个int。由于unarray指向一个数组,当unarray销毁它管理的指针时,会自动使用delete[]。
当一个unique_ptr指向一个数组时,我们可以使用下标运算符来访问数组中的元素:

void unique_array()
{
    
    //开辟一个20个整形的动态数组,用unique_ptr管理它。
    auto unarray = unique_ptr<int[]>(new int[20]);

    //可以通过下标访问数组元素
    for (size_t i = 0; i < 10; i++)
    {
    
        unarray[i] = 1024;
    }

    //释放这个动态数组
    unarray.release();
}

shared_ptr也可以管理动态数组,这一点在C++ primer 第5版里没有提及,但是我自己测试好用

void shared_array()
{
    
    // 开辟一个5个整形的动态数组,用shared_ptr管理它
    auto sharray = shared_ptr<int[]>(new int[5]{
    1, 2, 3, 4, 5});
    for (int i = 0; i < 5; i++)
    {
    
        cout << sharray[i] << " ";
    }
    sharray.reset();
    cout << endl;
}

C++ primer 第5版推荐的用法如下

void use_shared_array()
{
    
    shared_ptr<int> sharray = shared_ptr<int>(new int[5], [](int *p)
                                              {
     delete[] p; });
    sharray.reset();
}

上例中,shared_ptr管理一个动态数组并提供了删除器。

allocator类

当分配一大块内存时,我们通常计划在这块内存上按需构造对象。在此情况下,我们希望将内存分配和对象构造分离。这意味着我们可以分配大块内存,但只在真正需要时才真正执行对象创建操作(同时付出一定开销)。

类似vector,allocator是一个模板。为了定义一个allocator对象,我们必须指明这个allocator可以分配的对象类型。
当一个allocator对象分配内存时,它会根据给定的对象类型来确定恰当的内存大小和对齐位置:

void use_allocator()
{
    
    allocator<string> alloc;
    // allocator分配5个string类型对象的空间
    // 这些空间未构造
    auto const p = alloc.allocate(5);
    //销毁开辟的空间
    alloc.deallocate(p, 5);
}

上述代码用allocator构造alloc对象,说明开辟的空间是为string对象准备的,然后调用allocate开辟空间,但是这些空间不能直接使用,需要调用构造函数才能使用,我们用allocator类的construct来构造对象。

void use_allocator()
{
    
    allocator<string> alloc;
    // allocator分配5个string类型对象的空间
    // 这些空间未构造
    auto p = alloc.allocate(5);
    auto q = p;
    string str = "c";
    for (; q != p + 5; q++)
    {
    
        //构造字符串,每次字符串增加c字符
        alloc.construct(q, str);
        str += "c";
    }
    // //打印构造的字符串列表
    for (q = p; q != p + 5; q++)
    {
    
        cout << *q << endl;
    }

    //销毁开辟的空间
    alloc.deallocate(p, 5);
}

循环中通过construct为每个q指向的空间构造string对象,对象的内容就是str的内容,str会随着循环每次增加c,所以上面的代码输出如下

c
cc
ccc
cccc
ccccc

另外stl也提供了一些拷贝和填充内存的算法

void use_allocator()
{
    
    vector<int> ivec = {
    1, 2, 3, 4, 5};
    allocator<int> alloc;
    //开辟2倍ivec大小的空间
    auto p = alloc.allocate(ivec.size() * 2);
    //将ivec的内容copy至alloc开辟的空间里
    //返回q指向剩余未构造的内存空间的起始地址
    auto q = uninitialized_copy(ivec.begin(), ivec.end(), p);
    //将剩余元素初始化为42
    uninitialized_fill_n(q, ivec.size(), 42);
}

通过uninitialized_copy将ivec元素拷贝到p指向的空间,同样完成了构造。
uninitialized_fill_n将剩余ivec大小未构造的空间全部初始化为42。

总结

本文介绍了动态数组开辟的方法,利用new关键字可以开辟动态数组,利用delete[]可以回收数组。
也实现了通过shared_ptr和unique_ptr等智能指针管理动态数组的方案。
最后通过列举allocator的一些方法,展示了如何实现开辟空间和构造对象分离的方式动态构造对象。
源码连接
https://gitee.com/secondtonone1/cpplearn
想系统学习更多C++知识,可点击下方链接。
C++基础

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/secondtonone1/article/details/122620378

智能推荐

Linux性能测试工具_ubuntu显卡测试工具-程序员宅基地

文章浏览阅读2.1k次。Linux系统出现问题时,我们不仅需要查看系统日志信息,而且还要使用大量的性能监测工具来判断究竟是哪一部分(内存、CPU、硬盘……)出了问题。在Linux系统中,所有的运行参数保存在虚拟目录/proc中,换句话说,我们使用的性能监控工具取到的数据值实际上就是源自于这个目录,当涉及到系统高估时,我们就可以修改/proc目录中的相关参数了,当然有些是不能乱改的。下面就让我们了解一下这些常用的性能监控工_ubuntu显卡测试工具

平台治理开发:分布式系统的消息队列与事件驱动-程序员宅基地

文章浏览阅读247次,点赞5次,收藏9次。1.背景介绍1. 背景介绍分布式系统是现代软件架构中不可或缺的一部分,它们通过将应用程序分解为多个组件,以提高可扩展性、可靠性和可维护性。然而,分布式系统也带来了一系列挑战,包括数据一致性、故障转移和消息传递。消息队列和事件驱动架构是解决这些挑战的关键技术之一。消息队列是一种异步通信机制,它允许不同的组件在不同时间传递消息。事件驱动架构则是一种设计模式,它将系统的行为分解为一系列事件和...

基础练习 闰年判断 C语言_年份是4的倍数而不是100的倍数; 2. 年份是400的倍数。c-程序员宅基地

文章浏览阅读311次。基础练习 闰年判断 C语言描述:给定一个年份,判断这一年是不是闰年。当以下情况之一满足时,这一年是闰年:年份是4的倍数而不是100的倍数;年份是400的倍数。其他的年份都不是闰年。输入:输入描述:输入包含一个整数y,表示当前的年份。输入样例:2013输出:输出描述:输出一行,如果给定的年份是闰年,则输出yes,否则输出no。说明:当试题指定你输出一个字符串作为结果(比如本题的yes或者no,你需要严格按照试题中给定的大小写,写错大小写将不得分。输出样例:no提_年份是4的倍数而不是100的倍数; 2. 年份是400的倍数。c

【Java Web后台实验与开发】The server time zone value ‘�й���׼ʱ��‘ is unrecognized or represents more than one-程序员宅基地

文章浏览阅读1.2k次。文章目录1 错误环境2 错误原因3 解决方案:1 错误环境mysql版本:5.5.02 错误原因使用原mysql5.1.38不会出现该问题因使用了Mysql最新版驱动所以报错3 解决方案:方案1、在项目代码-数据库连接URL后,加上 (注意大小写必须一致)?serverTimezone=UTC方案2、在mysql中设置时区,默认为SYSTEMset global time_z..._the server time zone value

虚拟服务器与DMZ_vmware用dmz主机-程序员宅基地

文章浏览阅读4.7k次。1http://www.topoint.com.cn/html/article/2012/02/325965.html在设置wlan端口映射时,tp-link只有一个设置端口号,其实它只允许外部端口和内部端口的端口号一致。internet上面某台机器连接wlan的12345(外部端口)时,路由器会将所有数据发送给192.168.1.100的12345(内部端口)有的路由_vmware用dmz主机

sphinx 简介以及安装 以及php拓展开启-程序员宅基地

文章浏览阅读117次。一 sphinx 简介 在 使用mysql数据库过程中,如果想实现全文检索的优化,可以使用mysql自带全文索引,但是不支持中文。。关于sphinx的安装网上很多教程写的都 不错比如:http://www.coreseek.cn/products-install/。这里就不再说明安装方法了。有兴趣的可以自己参考。 MySQL在高并发连接、数据库记录数较多的情况下,S..._test -z "/usr/local/sphinx/lib" || mkdir -p -- "/usr/local/sphinx/lib

随便推点

51单片机智能电风扇控制系统proteus仿真设计( 仿真+程序+原理图+报告+讲解视频)_电风扇模拟控制系统设计-程序员宅基地

文章浏览阅读4.2k次,点赞4次,收藏51次。51单片机智能电风扇控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)仿真图proteus7.8及以上 程序编译器:keil 4/keil 5 编程语言:C语言 设计编号:S0042。_电风扇模拟控制系统设计

build.sh脚本-程序员宅基地

文章浏览阅读2.8w次,点赞7次,收藏73次。1开头程序必须以下面的行开始(必须方在文件的第一行):#!/bin/sh符号#!用来告诉系统它后面的参数是用来执行该文件的程序。在这个例子中我们使用/bin/sh来执行程序。当编写脚本完成时,如果要执行该脚本,还必须使其可执行。要使编写脚本可执行:编译chmod +x filename这样才能用./filename来运行2注释在进行shell编程时,以#开头的句子表示..._build.sh

标准库——bitset类型_bitset<32> bits(num);-程序员宅基地

文章浏览阅读338次。关于标准库中sring、vector、set、map、queue、stack 、bitset等,方法有些记不清楚,每次用每次查,很费时间,干脆自己整理一下,记不住的时候,查询更方便。// 需要包含头文件和声明:#include<bitset>using std::bitset;bitset对象的定义和初始化bitset类型对象的区别仅在其长度,而不在其类型。定义时,&..._bitset<32> bits(num);

帆软报表各种情形下引入js_帆软使用js-程序员宅基地

文章浏览阅读804次。3.EncryptionComponent.KEY——$HOST/v10/encryption/page 平台切换国密的时候才可能会用到,主要用于提示异常。8.MigrationComponent.KEY——$HOST/v10/migration/page FineDb迁移数据时的进度页面,在插件中用处不大。12.WorkflowComponent.KEY——$HOST/workflow/authority 单独访问多级上报权限控制页面时生效。被依赖组件——前端页面。被依赖组件——前端页面。_帆软使用js

高可用 | Xenon 实现 MySQL 高可用架构 部署篇_xaeon mysql-程序员宅基地

文章浏览阅读906次。在《高可用 | Xenon:后 MHA 时代的选择》一文中,我们对 Xenon 的实现原理、应用场景等做了简要介绍。文章发布后,社区小伙伴都在咨询 Xenon 如何与 MySQL 配合使用?本文来自知数堂投稿,是一篇基于 Xenon 架构原理,部署 一主两从 架构的 MySQL 高可用集群的实操文档。Xenon 架构图 环境信息:Redhat 7MySQL 5.7Xenon 1.0.7XtraBackup 24*另:Xenon 支持 MySQL 5.6/5.7/8.0 内核,本文以_xaeon mysql

移植Linux内核到阿尔法开发板(一)编译NXP官方Linux内核_nxp linux-程序员宅基地

文章浏览阅读944次,点赞7次,收藏20次。移植Linux内核到阿尔法开发板(一)编译NXP官方Linux内核_nxp linux

推荐文章

热门文章

相关标签