「数组」实现动态数组的功能(C++)

概述

动态数组,顾名思议即可变长度的数组。数组这种数据结构的实现是在栈空间或堆空间申请一段连续的可操作区域。

实现可变长度的动态数组结构,应该有以下操作:申请一段足够长的空间,如果数据的存入导致空间已满,则申请一段更长的空间,将原有数据复制过去后加入新数据,同时释放原空间。

//栈空间(在栈上原地生成长度为len的数组空间)
int arr[len];//堆空间(在堆上申请长度为len的空间,获得首地址交给指针arr)
int *arr=new int[len];//在堆上实现动态数组
int *temp=new int[len*2];//申请更长的空间
strcpy(temp,arr,sizeof(int)*len)//strcpy以字节为单位将arr指向的数据复制到temp中
delete[]arr;//释放原空间
arr=temp;//temp赋值给arr

接下来我们通过封装array类,实现动态数组的一些基本功能 。

成员变量

定义class类array,封装三个成员变量:T*val;int val_size;int val_capacity;

template <typename T>泛型,作为数组这种数据结构,他的数据单位应该是任意一种类型(int,double,class等),template <typename T>的作用区域是它随后的一个{}内,意为T将代表某种类型,至于某种类型到底是那种类型,将在class实例化时告知(如arrar<double> arr,这句话的意思是创建一个储存double类型的动态数组类)

T* val表示指向array维护的存放数据的空间的指针val。

int val_size表示数组的当前存入内容的长度(数据长度)。

int val_capacity是数组的空间大小(真实长度)。

为什么维护了size和capacity两种长度?试想:每次申请长度都只是当前长度+1,则会在内存空间中反复向后利用函数申请新空间来维护数据,这同时耗费了时间和空间。

那么如果每次都申请真实长度size的两倍空间capacity,则每次申请后都有一段空闲区域容纳新元素,此时不需要申请新空间,只有当再次填满时才申请新空间,则减少了时间成本,提高了空间利用率。

template <typename T>
class array {
private:T* val;int val_size;int val_capacity;
public:...
}

创建销毁

我们提供五种构造函数(创建动态数组)和一个析构函数(销毁动态数组)。

无参构造:arary(),初始化val_size=0,val_capaciy=1(如果等于0的话,后续申请两倍capacity会失效),val=nullptr(空指针)。

(初始化列表array():成员变量(x),...{}在构造函数后加上:即为初始化列表,表示为成员变量赋为括号内的值x,而无须写入{}中)

提供初始空间的构造:array(int num),表示申请初始空间为num的动态数组。

(断言assert(num>0),定义在assert.h头文件中,在运行时会判断括号内的条件为真,否则抛错,保护程序运行的安全)

提供初始长度和初值的构造:array(int size,T target_val),为val指针申请两倍size的空间,利用for循环填充长度为size的初始值。

提供源数据地址的构造:array(T* begin, T* end),从定长度数组中获取数据进行初始化,定义int size=end-begin(指针减法获得两指针的间隔长度(以指向的数据类型为单位))

(内存拷贝函数memcpy(指针1,指针2,以字节为单位的内存长度len),将指针2指向的长度为len的内容以字节为单位复制到指针1指向的空间)

拷贝构造:array(const array& another),将another整体赋值给新array。

(const array&表示这个函数保证接受一个array类型的常量引用(即该array自身而非他的拷贝),常量const保证本函数不修改another)

析构函数:~array()销毁数组释放空间,在主函数结束时自动调用,但在堆上申请的空间需要写入函数体否则无法释放。

array() : val_size(0), val_capacity(1), val(nullptr) {};//无参构造
array(int num) : val_size(0), val_capacity(num) {//提供初始空间的构造assert(num > 0);val = new T[num];
}
array(int size, T target_val) : val_size(size), val_capacity(size * 2) {//提供初始长度和初值的构造assert(size > 0);val = new T[size * 2];for (int i = 0; i < size; i++)val[i] = target_val;
}
array(T* begin, T* end) {//从固定长度数组中获取数据进行构造assert(begin != nullptr && end != nullptr);int size = end - begin;val = new T[size * 2];val_size = size;val_capacity = size * 2;memcpy(val, begin, sizeof(T)*size);
}
array(const array& another) {//拷贝构造val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;
}
~array() {delete[]val;
}

整体赋值

作为class类,array应该同时拥有类的性质的数组的性质,那么他就应该可以被视为整体来进行操控。
我们提供三种整体赋值的函数。

重载等于号:const array& operator=(const array& another),等于号作为一个的成员函数被我们重新定义,接收另一个array another的常量引用并整体赋值并返回一个自身的常量引用(这是为连等=...=...=...服务的)。

(this指针:返回这个array类的对象本身)

提供新长度和填充值的内存分配:void assign(int size, T target_val),为array分配长度为size*2的空间,保留长度为size的数据,若size大于原长则新空间内填充target_val。

数据交换:void swap(array& another),交换两个array的值。

const array& operator=(const array& another) {//重载等于号(这并不是构造,不能在初始化时使用)delete[]val;val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;return *this;
}
void assign(int size, T target_val) {assert(size > 0);T* temp = new T[size * 2];for (int i = 0; i < size; i++)temp[i] = target_val;if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;
}
void swap(array& another) {array temp(another);*this = another;another = temp;
}

内存管理 

我们提供四个size相关的函数和两个capacity相关的函数维护内存。

获取有效数据长度:int size()const,返回val_size。

(函数()与{ }之间的const表示函数体内部不进行数据的更改操作)

判断有效空间是否为空:bool empty()const,返回val_size是否不等于0,是则false,否则true。

提供新长度的长度重置:void resize(int size),与assign类似。

提供新长度和填充值的长度重置:void resize(int size, T target_val),与assign类似。

获取实际空间长度:int capacity()const,返回val_capacity。

延长空间:void reserve(int num),如果申请的新空间小于val_capacity,无事发生,否则申请更大的空间。

int size()const {return val_size;
}
bool empty()const {return val_size ? false : true;
}
void resize(int size) {assert(size > 0);T* temp = new T[size * 2];if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;
}
void resize(int size, T target_val) {assert(size > 0);T* temp = new T[size * 2];for (int i = 0; i < size; i++)temp[i] = target_val;if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;
}
int capacity()const {return val_capacity;
}
void reserve(int num) {if (num > val_capacity) {T* temp = new T[num];memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_capacity = num;}
}

数据控制

与固定长度数组不同,我们需要调用成员函数来实现数据增删。

压入新元素:void push_back(const T & elem),在末尾追加新元素elem,如果有剩余空间大于1则直接写入,否则申请两倍的新空间。

插入新元素:void insert(int pos, T element),在给定位置插入元素elem。

删除末元素:void pop_back(),删除最后一个元素。

清空元素:void clear(),将val_size置为0。

(memet(指针p,char val,以字节为单位的内存长度len),以字节为单位将val赋给p指向的长度为len的空间)

void push_back(const T & elem) {if (val_capacity - val_size <= 1)reserve(val_capacity*2);val[val_size++] = elem;
}
void insert(int pos, T elem) {if (val_capacity - val_size <= 1)reserve(val_capacity*2);val_size++;T temp;for (int i = pos; i < val_size; i++) {temp = val[i];val[i] = elem;elem = temp;}
}
void pop_back() {assert(val_size > 0);val[val_size--] = 0;
}
void clear() {memset(val, 0, sizeof(T) * val_size);val_size = 0;
}

数据访问

我们通过编写返回值为引用类型的函数来实现数据的读写功能。

引用返回这个数据本身而非他的拷贝,它是一种比指针更安全的数据传递手段。

重载[]号:T& operator[](int pos),为了使我们的数组类有定长数组的特征,重载[]号使arr[]具有作用。[]接收一个int pos,返回pos对应位置的引用。

获取头元素:T& front(),获取第一个元素的引用。

获取尾元素:T& back(),获取最后一个元素的引用。

T& operator[](int pos) {assert(pos >= 0);//这是一个断言 它要求pos合法 否则抛异常return val[pos];
}
T& front() {assert(val_size > 0);return val[0];
}
T& back() {assert(val_size > 0);return val[val_size - 1];
}

Code

#include <cassert>
template <typename T>
class array {
private:T* val;int val_size;int val_capacity;
public:array() : val_size(0), val_capacity(1), val(nullptr) {};//无参构造array(int num) : val_size(0), val_capacity(num) {//提供初始空间的构造assert(num > 0);val = new T[num];}array(int size, T target_val) : val_size(size), val_capacity(size * 2) {//提供初始长度和初值的构造assert(size > 0);val = new T[size * 2];for (int i = 0; i < size; i++)val[i] = target_val;}array(T* begin, T* end) {//从固定长度数组中获取数据进行构造assert(begin != nullptr && end != nullptr);int size = end - begin;val = new T[size * 2];val_size = size;val_capacity = size * 2;memcpy(val, begin, sizeof(T)*size);}array(const array& another) {//拷贝构造val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;}~array() {delete[]val;}const array& operator=(const array& another) {//重载等于号(这并不是构造,不能在初始化时使用)delete[]val;val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;return *this;}void assign(int size, T target_val) {assert(size > 0);T* temp = new T[size * 2];for (int i = 0; i < size; i++)temp[i] = target_val;if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;}void swap(array& another) {array temp(another);*this = another;another = temp;}int size()const {return val_size;}bool empty()const {return val_size ? false : true;}void resize(int size) {assert(size > 0);T* temp = new T[size * 2];if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;}void resize(int size, T target_val) {assert(size > 0);T* temp = new T[size * 2];for (int i = 0; i < size; i++)temp[i] = target_val;if (size < val_size)memcpy(temp, val, sizeof(T) * size);else memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_size = size;val_capacity = size * 2;}int capacity()const {return val_capacity;}void reserve(int num) {if (num > val_capacity) {T* temp = new T[num];memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_capacity = num;}}void push_back(const T & elem) {if (val_capacity - val_size <= 1)reserve(val_capacity*2);val[val_size++] = elem;}void insert(int pos, T elem) {if (val_capacity - val_size <= 1)reserve(val_capacity*2);val_size++;T temp;for (int i = pos; i < val_size; i++) {temp = val[i];val[i] = elem;elem = temp;}}void pop_back() {assert(val_size > 0);val[val_size--] = 0;}void clear() {memset(val, 0, sizeof(T) * val_size);val_size = 0;}T& operator[](int pos) {assert(pos >= 0);//这是一个断言 它要求pos合法 否则抛异常return val[pos];}T& front() {assert(val_size > 0);return val[0];}T& back() {assert(val_size > 0);return val[val_size - 1];}
};

测试 

#include <iostream>
#include "array.h"
using namespace std;
template <typename T>
void show(::array<T>& arr) {if (arr.empty())cout << "EMPTY";else for (int i = 0; i < arr.size(); i++)cout << arr[i] << ' ';cout << endl;
}
int main()
{//c++有自己内部的STL类型<array>,这里::表示是自己定义的array类::array<int>a;show<int>(a);::array<int>b(5); show<int>(b);::array<int>c(5, 3); show<int>(c);double x[] = {1.1,2.2,3.3};::array<double>d(x, x + 3); show<double>(d);::array<double>e(d); show(e);cout << endl;a = c; show<int>(a);b.assign(6); cout << b.size() << endl;c.assign(8, 2); show<int>(c);b.swap(c); show<int>(b);cout << endl;cout << b.size() << endl;cout << ( b.empty() ? "YES" : "NO") << endl;b.resize(10, 5); show<int>(b);cout << b.capacity()<<endl;b.reserve(50);cout << b.capacity()<<endl;cout << endl;a.push_back(10); show<int>(a);a.insert(1, 7); show<int>(a);a.pop_back(); show<int>(a);a.clear(); cout << (a.empty() ? "YES" : "NO") << endl;cout << endl;show<double>(d);d[1] = 8.8; show<double>(d);cout << endl;cout << d.front() << ' ' << d.back() << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/387034.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PackagesNotFoundError 错误表明 conda 在当前使用的镜像源中找不到 contourpy 版本 1.2.1。以下是可能的解决方法:

PackagesNotFoundError 错误表明 conda 在当前使用的镜像源中找不到 contourpy 版本 1.2.1。以下是可能的解决方法&#xff1a; PackagesNotFoundError 错误表明 conda 在当前使用的镜像源中找不到 contourpy 版本 1.2.1。以下是可能的解决方法&#xff1a; 1. 更换镜像源 虽…

rust 桌面 sip 软电话(基于tauri 、pjsip库)

本文尝试下rust 的tauri 桌面运用 原因在于体积小 1、pjsip 提供了rust 接口官方的 rust demo 没编译出来 在git找了个sip-phone-rs-master https://github.com/Charles-Schleich/sip-phone-rs 可以自己编译下pjsip lib库替换该项目的lib 2、创建一个tauri demo 引用 [depe…

计算机毕业设计选题推荐-某炼油厂盲板管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

实战:Zookeeper 简介和单点部署ZooKeeper

Zookeeper 简介 ZooKeeper是一个开源的分布式协调服务&#xff0c;它是Apache软件基金会下的一个项目&#xff0c;旨在解决分布式系统中的协调和管理问题。以下是ZooKeeper的详细简介&#xff1a; 一、基本定义 ZooKeeper是一个分布式的、开放源码的分布式应用程序协调服务&a…

SpringBoot的基础配置

目录 SpringBoot快速搭建web程序 第一步&#xff1a;导包 第二步&#xff1a;配置SpringBoot引导类 第三步&#xff1a;编写controller类 第四步&#xff1a;在SpirngBoot引导类中启动项目 起步依赖 SpringBoot基础配置 配置文件格式 yaml语法规则 读取yml配置文件的方…

UE5+OpenCV配置(Windows11系统)

一、概述 因为需要在UE5中使用OpenCV这些工具进行配置&#xff0c;所以在网络上参考借鉴一些资料进行配置。查询到不少的资料&#xff0c;最后将其配置成功。在这里顺便记录一下自己的配置成功的过程。 二、具体过程 &#xff08;一&#xff09;版本 使用Windows11系统、UE5.…

ONLYOFFICE 协作空间 2.6 已发布:表单填写房间、LDAP、优化房间和文件管理等

更新后的 ONLYOFFICE 协作空间带来了超过 20 项新功能和优化&#xff0c;让工作更加高效和舒适。阅读本文了解详情。 表单填写房间 这次更新增加了一种新的房间类型&#xff0c;可在 ONLYOFFICE 协作空间中组织简单的表单填写流程。 通过表单填写房间&#xff0c;目前可以完成…

将控制台内容输出到文本文件

示例代码&#xff1a; Imports System.IO Module Module1Sub Main()Dim fs As New FileStream("D:\Desktop\test\输出结果.txt", FileMode.Create, FileAccess.Write, FileShare.None)Dim sw As New StreamWriter(fs)Console.SetOut(sw)Console.SetError(sw)For i …

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第四篇 嵌入式Linux系统移植篇-第六十九章uboot移植

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…

移动UI:排行榜单页面如何设计,从这五点入手,附示例。

移动UI的排行榜单页面设计需要考虑以下几个方面&#xff1a; 1. 页面布局&#xff1a; 排行榜单页面的布局应该清晰明了&#xff0c;可以采用列表的形式展示排行榜内容&#xff0c;同时考虑到移动设备的屏幕大小&#xff0c;应该设计合理的滚动和分页机制&#xff0c;确保用户…

在线教育数仓项目(数据采集部分1)

文章目录 数据仓库概念项目需求及架构设计项目需求分析系统数据流程设计框架版本选型集群规模估算集群资源规划设计 数据生成模块目标数据页面事件曝光启动播放错误 数据埋点主流埋点方式&#xff08;了解&#xff09;埋点数据上报时机埋点数据日志结构 服务器和JDK准备服务器准…

Linux:shell的基础用法

shell的基础用法 shell变量 Shell 支持以下三种定义变量的方式&#xff1a; valueabcvalue‘abc’value“abc”(注意&#xff0c;赋值号的周围不能有空格) Shell 变量的命名规范 变量名由数字、字母、下划线组成必须以字母或者下划线开头不能使用 Shell 里的关键字&#xff08…

IDEA的pom.xml显示ignored 的解决办法

问题&#xff1a; idea中创建Maven module时&#xff0c;pom.xml出现ignored。 原因&#xff1a; 相同名称的module在之前被创建删除过&#xff0c;IDEA会误以为新的同名文件是之前删除掉的&#xff0c;将这个新的module的pom.xml文件忽略掉显示ignored. 解决&#xff1a; 在…

springboot超市商品管理系统-计算机毕业设计源码55289

摘 要 随着信息技术的快速发展和普及&#xff0c;传统的超市管理模式已经无法满足现代商业的需求。为了提高超市的管理效率&#xff0c;优化商品销售流程&#xff0c;本文提出了一种基于SpringBoot框架的超市商品管理系统。该系统结合了现代软件开发技术&#xff0c;包括MySQL数…

WATLOW Power Series SSR User’s Manual

WATLOW Power Series SSR User’s Manual

【Java】字符串String类(011)

目录 ♦️API和API帮助文档 ♦️创建String &#x1f38f;直接赋值类 &#x1f38f;new类 &#x1f421;空参类 构造方法&#xff1a; 举例代码&#xff1a; &#x1f421;有参类 构造方法&#xff1a; 举例代码&#xff1a; &#x1f421;字符数组类 构造方法&…

【C++】类和对象——流插入和流提取运算符重载

目录 前言ostream和istream自定义类型的流插入重载自定义类型的流提取重载解决私有问题日期类总接口 前言 我们在上一节实现日期类时&#xff0c;在输入和输出打印时&#xff0c;经常会调用两个函数&#xff1a; void Insert()//输入函数{cin >> _year;cin >> _mo…

项目比赛经验分享:如何抓住“黄金一分钟”

项目比赛经验分享&#xff1a;如何抓住“黄金一分钟” 前言引起注意&#xff1a;用事实和故事开场明确痛点&#xff1a;描述问题和影响介绍解决方案&#xff1a;简明扼要激发兴趣&#xff1a;使用视觉辅助概述演讲结构&#xff1a;清晰的路线图我的开场白示例结语 前言 在创新的…

(源码分析)springsecurity认证授权

了解 1. 结构总览 SpringSecurity所解决的问题就是安全访问控制&#xff0c;而安全访问控制功能其实就是对所有进入系统的请求进行拦截&#xff0c;校验每个请求是否能够访问它所期望的资源。 根据前边知识的学习&#xff0c;可以通过Filter或AoP等技术来实现&#xff0c;Spr…

鸿蒙应用框架开发【简单时钟】 UI框架

简单时钟 介绍 本示例通过使用ohos.display接口以及Canvas组件来实现一个简单的时钟应用。 效果预览 使用说明 1.界面通过setInterval实现周期性实时刷新时间&#xff0c;使用Canvas绘制时钟&#xff0c;指针旋转角度通过计算得出。 例如&#xff1a;"2 * Math.PI / …