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

news2024/9/9 0:32:15

概述

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

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

//栈空间(在栈上原地生成长度为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.coloradmin.cn/o/1958461.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CentOS7安装最新版vim;vim自动补齐配置

想练习一会vim&#xff0c;结果发现敲代码没有空号自动补齐和缩进很难受&#xff0c;所以想配置一下。 配置vim&#xff1a; 可以通过 vim ~/.vimrc 来给 vim 加启动的设定&#xff08;比如set nu&#xff0c;这样就会在每次启动的时候都加上行号。当然过程中可以在底行模式输…

基于SpringBoot+Vue的学生考勤管理系统(带1w+文档)

基于SpringBootVue的学生考勤管理系统(带1w文档) 系统为了数据库结构的灵活性选择MySQL来设计&#xff0c;而java技术&#xff0c;B/S架构则保证了较高的平台适应性。本文主要介绍了系统开发背景&#xff0c;需要完成的功能与开发过程&#xff0c;说明系统设计重点与设计思想。…

如何在linux系统中用conda安装R环境及R包

一、miniconda3的安装不再赘述 二、安装R环境 1. 提前准备好conda的R单独环境 conda env list #查看已有环境 查看R的最新版本&#xff1a;r-project ##创建环境和激活环境 conda create -n R4.4.1 conda activate R4.4.1 备注&#xff1a;激活环境Linux&#xff0c;OS X…

企业如何保证公司内网安全

1. 加强网络安全防护 部署防火墙和入侵检测系统&#xff1a;作为内网安全的第一道防线&#xff0c;防火墙和入侵检测系统能够有效阻止外部攻击和恶意软件的入侵。 数据加密&#xff1a;采用先进的加密技术保护敏感数据&#xff0c;确保数据在传输和存储过程中的安全性。 访问…

【Android面试八股文】荣耀面试算法题: 输出一个给定的字符串的最长回文子序列及其长度!

文章目录 一、真题链接二、如何解决2.1算法思路2.2 算法步骤2.3 Java算法实现 一、真题链接 还好我以前刷过这道题&#xff0c; 其实题目就是LeetCode的 516.最长回文子序列&#xff0c; 地址&#xff1a;https://leetcode.cn/problems/longest-palindromic-subsequence/des…

哈希表专题

题解之前&#xff1a; 1.有关unordered_map的count功能&#xff1a;查询key&#xff01; Leetcode 1.两数之和 解题思路&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {vector<int> res;// key:具体的数值(便…

AI需求海量涌现,Scaleway如何用Spectrum-X 网络从容应对?

“没有好网络&#xff0c;别玩AIGC。” 如今&#xff0c;随着AI需求的大量涌现&#xff0c;越来越多用户意识到网络在AI集群中的重要性。一个超大规模、超高带宽、超强可靠的网络&#xff0c;可以为AI训练提供强有力支撑&#xff0c;从而节约训练成本、缩短训练时间&#xff0…

VBA快速对比数据行

实例需求&#xff1a;对于存在多行数据&#xff08;示例中为双行&#xff09;的项目&#xff0c;对比同一个项目的每列数据&#xff0c;高亮显示数据不同的单元格。 示例代码如下。 Function GetDiff(ByRef rng1 As Range, ByRef rng2 As Range) As RangeDim i As LongFor i …

dpdk发送udp报文

dpdk接收到udp报文后&#xff0c;自己构造一个udp报文&#xff0c;将收到的报文中的源mac&#xff0c;目的mac&#xff0c;源ip&#xff0c;目的ip&#xff0c;源端口和目的端口交换下顺序填充到新的udp报文中&#xff0c;报文中的负载数据和收到的udp保持一致。 注&#xff1…

CATIA V5R21安装包下载及图文安装教程

大家好&#xff0c;今天给大家分享下catia安装教程 注意安装前请退出杀毒软件&#xff0c;防止误报影响安装进程 下载链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;ypc6 01 在电脑D盘新建文件夹命名为CATIA,将下载的软件压缩包放置在该文件夹。 鼠标右击【C…

进行良好的文献综述能否提高学术研究的可信度

VersaBot一键生成文献综述 进行良好的文献综述 对于从多个方面提高学术研究的可信度至关重要&#xff1b; 1. 展示专业知识&#xff1a; 全面的回顾表明您对您所在领域的现有知识和相关理论有深入的了解。这将使您成为权威&#xff0c;并将您的研究置于更广泛的背景下。 2.…

初步入门C ++之类的概念

文章目录 0 Hello World!1 编译过程2 类2.1 类的概念2.2 构造函数与析构函数 0 Hello World! #include <iostream> //相当于#include <stdio.h>int main(int argc, char argv[]) {char c;std::cout << "Hello World!\n" <<…

入门 PyQt6 看过来(案例)14~ 分组

本文分享一个分组框功能&#xff0c;采用pyqt6里的QGroupBox​控件&#xff0c;效果如下&#xff1a;性别和专业分开为两个分组框内&#xff1a; ​ 1 功能实现思路 ui页面布局设计 性别和专业要设计成两个分组框&#xff1a; ​ 逻辑实现 引入信号和槽函数来实现点击单选…

Cybersecurity ASPICE实施策略-基于ISO/SAE 21434-亚远景科技

近几年&#xff0c;随着软件定义汽车和汽车的智能化和网联化&#xff0c;使得汽车融合了现代通信与网络通信技术&#xff0c;实现了车与人、车与车、车与道路、车与云端等智能信息交互和共享&#xff0c;也让车具备了环境感知、协同控制、智能决策等功能&#xff1b;与此同时&a…

构建可定制的表情选择器组件

你好呀&#xff0c;我是小邹。 概述 在当今的交互式Web应用中&#xff0c;表情符号&#xff08;Emoji&#xff09;已成为一种流行的沟通方式。为了提升用户体验并简化开发流程&#xff0c;本教程将引导您如何构建一个可高度定制的表情选择器组件。此组件将允许用户在Web表单中…

力扣621.任务调度器

力扣621.任务调度器 桶思想当桶放不满时 答案为桶面积 maxcount(最后一行) (max - 1)(n1)当桶放的满时 答案为任务总数 tasks.size()最终两者取大即可 class Solution {public:int leastInterval(vector<char>& tasks, int n) {int len tasks.size();vector<…

QT--聊天室

一、设计要求 用QT做一个聊天室&#xff0c; 制作一个服务器和客户端。可以进行注册、登录&#xff0c; 登陆成功后可以使用昵称进行发送、接收消息。 能根据昵称、聊天内容查询历史记录&#xff0c;也可以查询全部聊天记录。 。 二、客户端三级ui界面 三、项目代码 //在…

测试用例:确保软件质量的基石

大家好&#xff0c;我是一名测试开发工程师&#xff0c;已经开源一套【自动化测试框架】和【测试管理平台】&#xff0c;欢迎大家联系我&#xff0c;一起【分享测试知识&#xff0c;交流测试技术】 在当今这个数字化时代&#xff0c;软件已经成为人们日常生活、工作和学习中不可…

Hive3:Centos7环境部署Hive服务

一、安装说明 1、Hadoop集群情况 3台机器&#xff1a;4G2C、2G2C、2G2C 安装教程&#xff1a;Centos7环境安装Hadoop集群 2、安装MySQL&#xff0c;用于存储Hive的元数据 在102机器上安装MySQL 安装MySQL使用服务器的root账号 3、最后安装Hive 安装hive过程使用服务器的atgu…

fatal: Could not read from remote repository. 解决方法

问题描述&#xff1a; Git : fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists。 解决方法&#xff1a; 当在网上尝试大量方法仍然失败的时候&#xff0c;不妨试试这个方法。 在 github 上&…