前言

学习C++的过程中,动态数组是绕不开的核心知识点。很多同学一开始会混淆静态数组、动态数组,也搞不懂底层原理和以及其法,这篇文章就顺着学习思路,从零开始,一步步吃透动态数组,从普通手写,到vector底层,再到智能指针进阶,全程干货,保证轻松看懂。


一、先分清:静态数组VS动态数组

静态数组
编译时就确定大小,内存开辟在栈区,大小固定不可修改。

// 大小固定为5,不能更改
int arr[5];

优点:写法简单,速度快;缺点:不够灵活,无法扩容,栈区空间有限。

动态数组
运行时确定大小,内存开辟在堆区,大小可灵活调整,能按需扩容。
优点:灵活通用,适合未知数据量的场景;缺点:手动写法需要管理内存,容易出现内存泄漏。

二、基础写法:用普通指针来实现动态数组

1.核心步骤

  1. 定义指针,指向堆区开辟的内存
  2. 赋值、访问、遍历,用法和普通数组一致
  3. 手动扩容(申请新空间→拷贝数据→释放旧空间)
  4. 用完释放内存,避免内存泄漏
#include <iostream>
using namespace std;

int main() {
    // 1. 定义数组大小,开辟堆内存
    int size = 5;
    int* arr = new int[size];

    // 2. 赋值
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }

    // 3. 遍历访问
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    // 4. 手动扩容示例
    int newSize = size * 2;
    int* newArr = new int[newSize];
    // 拷贝旧数据
    for (int i = 0; i < size; i++) {
        newArr[i] = arr[i];
    }
    // 释放旧空间,指针指向新空间
    delete[] arr;
    arr = newArr;
    newArr = nullptr;
    size = newSize;

    // 5. 最终释放内存,指针置空
    delete[] arr;
    arr = nullptr;

    return 0;
}

重点:new[] 必须搭配 delete[],少了括号会导致内存泄漏;释放后指针置空,防止野指针。

三、进阶底层:vector三指针模型

平时用的vector(STL),就是封装好的动态数组,不用手动管理内存,它的底层靠三指针实现内存管理,也是面试高频考点。

3.1三个核心指针

start:指向数组起始地址
finish:指向最后一个有效元素的下一位
end_of_storage:指向整个内存空间的末尾

关键概念
size(元素个数):finish - start
capacity(总容量):end_of_storage - start

当元素个数等于容量时,vector会自动扩容(通常扩容1.5倍或2倍),全程不用手动操作,安全省心。

其基本的操作逻辑如下:
666

3.2vector常用操作

操作 代码 作用
尾部添加 v.push_back(x) 末尾插入元素,自动扩容
尾部删除 v.pop_back() 删除最后一个元素
获取个数 v.size() 返回有效元素数量
指定位置插入 v.insert(pos, x) 在指定位置插入元素
删除指定元素 v.erase(pos) 删除指定位置元素
清空数组 v.clear() 清空所有元素,不释放空间

四、智能指针管理动态数组

普通指针需要手动释放内存,很容易忘记写delete[],造成内存泄漏。C++提供了智能指针,能自动释放内存,兼顾底层灵活性和安全性。

核心注意事项
管理动态数组,必须用 unique_ptr<T[]>(带方括号),普通unique_ptr会调用delete,而不是delete[],会导致程序出错。

标准写法:

#include <iostream>
#include <memory>  // 智能指针必备头文件
using namespace std;

int main() {
    // 1. 创建智能指针管理的动态数组(C++14及以上推荐)
    auto arr = make_unique<int[]>(5);

    // 2. 赋值、访问,和普通数组用法完全一样
    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 30;

    // 3. 遍历
    for (int i = 0; i < 5; i++) {
        cout << arr[i] << " ";
    }

    // 4. 无需手动delete[],离开作用域自动释放
    return 0;
}

拓展:
unique_ptr<ClassRoom[]> 和 unique_ptr<unique_ptr[]> 有什么区别,该怎么选?

普通智能指针数组
unique_ptr<ClassRoom[]> arr;
特点:数组直接存放对象,内存连续,写法简单,速度快,自动创建和销毁对象。
适用场景:单纯存储对象数据,不需要多态,不需要空指针,普通业务场景首选。

嵌套智能指针数组
unique_ptr<unique_ptr[]> arr;
特点:数组存放指针,不直接存对象,支持空指针,支持多态(父类指针指向子类)。
适用场景:对象体积大,避免拷贝;需要用到继承多态;数组元素需要置空的场景。

总结

  1. 静态数组大小固定,动态数组运行时可调整,存于堆区
  2. 普通指针实现动态数组,必须new[]搭配delete[],防止内存泄漏
  3. vector底层是三指针模型,自动扩容自动释放,日常开发首选
  4. 智能指针版动态数组用unique_ptr<T[]>,兼顾底层和安全,不用手动释放
  5. 普通场景用普通智能指针数组,多态/大对象场景用嵌套写法
  6. 模板能实现万能动态数组,适配所有数据类型
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐