动态数组 Vector(难度1)(V)

C++数据结构与算法实现(目录)

前驱课程

C++ 精简教程 目录(必读)

堆数组 heap array

面相对象的堆数组

1 原始堆数组的缺点:

1) 原始堆数组 其长度是固定不变的。

2) 使用指针管理元素,数据和操作散落一地。这样的数组,使用起来也很不方便。

2 如何实现一个面向对象的动态数组

下面我们要实现的类叫 Vector。这个类有下面的一些功能。

现在,我们也开始考虑如何实现下面的这些功能。

1 返回元素数量 size

数组的元素数量,我们可以用一个成员变量来记录。

每当元素增加一个的时候,我们记得把这个成员变量的值加1。

class Vector
{int size();// 返回元素数量
private:int element_cout;// 存储元素数量
};

Vector的内存结构

2 添加元素 push_back()

为了盛放元素,我们需要创建一个动态数组。但是一开始这个数组多大比较好呢?

我们把数组初始大小设为10. 当这些元素用完了以后再扩容。

第一次push_back的时候,容器的容量是10,元素个数为0,因为还没有存任何数据;

class Vector
{
private:int element_cout=0;int capacity=0;int* data=nullptr;
};

第一次push_back开辟10个元素的空间

3 添加元素到容器里

使用push_back(i)来把元素放到容器里的过程如下

添加整数11到容器中:

添加整数11到容器中

添加整数12到容器中:

添加整数12到容器中

一直添加,直到添加20到容器中:

添加20到容器中

此时容器再也没有剩余的空间可以添加元素了,需要扩容。

扩容如果要保持元素挨在一起存放,只能另起一个更大的炉灶,把现在的数据拷贝过去。

(1)先开辟更大的空间

先开辟1.5bei

(2)拷贝原来的数据到新地方:

拷贝原来的数据11~20到新地方

(3)添加新元素到新空间的末尾,更新元素数量

添加新元素到新空间的末尾,更新元素数量,更新容量

(4)释放原来的空间,接管新空间

delete[] m_data;
m_data = p;

释放原来的空间,接管新空间

4 访问元素

自定义的类型class/struct Vector是不可以使用下标操作符[]的。

Vector a;
a[0];// 编译报错!!!!

为此需要使用操作符重载

对操作符[]的重载是一个特殊的成员函数,有固定的格式。

给你的类添加一个下面的成员函数,就可以使用下标操作符了:

int& operator[](int n) { return data[n]; }

有了上面的成员函数,下面的代码就可以正常工作了:

Vector a;
a[0];// OK

5 清空元素 clear

清空元素会让数组回到初始状态,也就是capacity为0,size为0;

如果原来有元素,需要释放原来的全部动态内存。

此时 容量为0,大小为0。

注意:用户可以连续两次调用clear,而不应该出现问题。

完整代码和测试用例如下

#include<iostream>
#include <iomanip>
#include <cassert>
using namespace std;//------下面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------
#include <algorithm>
#include <cstdlib>
#include <iostream> 
#include <vector>
#include <utility>
using namespace std;
struct Record { Record(void* ptr1, size_t count1, const char* location1, int line1, bool is) :ptr(ptr1), count(count1), line(line1), is_array(is) { int i = 0; while ((location[i] = location1[i]) && i < 100) { ++i; } }void* ptr; size_t count; char location[100] = { 0 }; int line; bool is_array = false; bool not_use_right_delete = false; }; bool operator==(const Record& lhs, const Record& rhs) { return lhs.ptr == rhs.ptr; }std::vector<Record> myAllocStatistic; void* newFunctionImpl(std::size_t sz, char const* file, int line, bool is) { void* ptr = std::malloc(sz); myAllocStatistic.push_back({ ptr,sz, file, line , is }); return ptr; }void* operator new(std::size_t sz, char const* file, int line) { return newFunctionImpl(sz, file, line, false); }void* operator new [](std::size_t sz, char const* file, int line)
{ return newFunctionImpl(sz, file, line, true); }void operator delete(void* ptr) noexcept {Record item{ ptr, 0, "", 0, false }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) {auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; }else { myAllocStatistic[ind].count = 0; }std::free(ptr);}
}void operator delete[](void* ptr) noexcept {Record item{ ptr, 0, "", 0, true }; auto itr = std::find(myAllocStatistic.begin(), myAllocStatistic.end(), item); if (itr != myAllocStatistic.end()) { auto ind = std::distance(myAllocStatistic.begin(), itr); myAllocStatistic[ind].ptr = nullptr; if (!itr->is_array) { myAllocStatistic[ind].not_use_right_delete = true; } else { myAllocStatistic[ind].count = 0; }std::free(ptr); }}
#define new new(__FILE__, __LINE__)
struct MyStruct { void ReportMemoryLeak() { std::cout << "Memory leak report: " << std::endl; bool leak = false; for (auto& i : myAllocStatistic) { if (i.count != 0) { leak = true; std::cout << "leak count " << i.count << " Byte" << ", file " << i.location << ", line " << i.line; if (i.not_use_right_delete) { cout << ", not use right delete. "; }	cout << std::endl; } }if (!leak) { cout << "No memory leak." << endl; } }~MyStruct() { ReportMemoryLeak(); } }; static MyStruct my; void check_do(bool b, int line = __LINE__) { if (b) { cout << "line:" << line << " Pass" << endl; } else { cout << "line:" << line << " Ohh! not passed!!!!!!!!!!!!!!!!!!!!!!!!!!!" << " " << endl; exit(0); } }
#define check(msg)  check_do(msg, __LINE__);
//------上面的代码是用来测试你的代码有没有问题的辅助代码,你无需关注------//注意:禁止修改Vector的定义,包括禁止给Vector添加成员变量;
//可以添加私有成员函数,如果你认为需要的话
struct Vector
{
public:Vector();Vector(int n, int value);Vector(const Vector& from);//deep copyVector& operator=(const Vector& from);//deep copy~Vector();int size() const;//只读元素//参考 https://zhuanlan.zhihu.com/p/539451614const int& operator[](int n)const { return m_data[n]; }//写入元素int& operator[](int n) { return m_data[n]; }void push_back(int value);bool empty() const;void clear();
private:void copy(const Vector& from);
private:int m_size = 0;int m_capacity = 0;int* m_data = nullptr;//请忽略下面这个成员变量,这个成员变量不影响你的实现,当它不存在即可。
};//默认构造函数什么也不需要做,只用来保证可以创建对象的时候无需提供参数
Vector::Vector()
{
}Vector::Vector(int n, int value)
{for (int i = 0; i < n; i++){push_back(value);}
}Vector::Vector(const Vector& from)
{copy(from);
}Vector::~Vector()
{//释放动态内存,需用用 delete[]//(4) your code}int Vector::size() const
{return m_size;
}void Vector::push_back(int value)
{//1 如果capacity为0,则一次性开辟10个元素//2 如果capacity容量没有用完 追加到最后//3 如果capacity容量已经用完,开辟两倍capacity大小的容量,拷贝老数据,追加新数据if (m_capacity == 0){//(1) your code}else if (m_size < m_capacity){//给最后一个元素的后面赋值为新元素value//增加元素数量//(5) your code}else{//每次内存不够用就翻倍int* p = new int[2 * m_capacity];//先把原来的每个元素拷贝到新地方int j;for (int j = 0; j < m_size; j++){p[j] = m_data[j];}//把新添加的元素也添加到新地方//(6) your code//记得元素数量加1//(7) your code//容量翻倍//(8) your code//释放原来的内存//(9) your code//成员变量接管新开普的内存//(10) your code}
}bool Vector::empty() const
{return m_size == 0;
}void Vector::clear()
{//(11) your code 参考 清空元素部分的介绍;如果原来已经有容量了,需要先释放原来的空间;}void Vector::copy(const Vector& from)
{//(2) your code 先调用 clear}
Vector& Vector::operator = (const Vector& from)
{if (&from == this){return *this;}copy(from);return *this;
}void test1(void)
{Vector v;int i;check(v.size() == 0);for (i = 0; i < 10; i++){v.push_back(i);}check(v.size() == 10);for (int i = 0; i < 10; i++){check(v[i] == i);}check(v.size() == 10);
}
void test2(void)
{int n = 100000;Vector v;int i;check(v.size() == 0);for (i = 0; i < n; i++){v.push_back(i);}for (int i = 0; i < n; i++){assert(v[i] == i);//这里必须用assert,否则会疯狂打印pass}check(v.size() == n);
}
void print(Vector& v, const std::string& msg)
{std::cout << "The contents of " << msg.c_str() << " are:";for (int i = 0; i != v.size(); ++i){std::cout << ' ' << v[i];}std::cout << '\n';
}
void test3()
{Vector a;Vector first;                   // empty vector of intsassert(first.empty() == true && first.size() == 0);Vector second(4, 100);                       // four ints with value 100assert(second.empty() == false);assert(second.size() == 4);Vector fourth(second);                       // a copy of thirdassert(fourth.size() == second.size());int myints[] = { 16,2,77,29 };Vector fifth;fifth.push_back(16);fifth.push_back(2);fifth.push_back(77);fifth.push_back(29);assert(fifth.empty() == false);assert(fifth[0] == 16);assert(fifth[3] == 29);assert(fifth.size() == sizeof(myints) / sizeof(int));print(fifth, "fifth");//The contents of fifth are:16 2 77 29 fifth.push_back(30);assert(fifth[4] == 30);assert(fifth.size() == 5);print(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 assert(fifth.size() == sizeof(myints) / sizeof(int) + 1);first = fifth = fifth;print(first, "first");//The contents of first are:16 2 77 29 30 assert(first.empty() == false && first.size() == fifth.size());Vector a1;a1.push_back(16);a1.push_back(2);a1.push_back(77);a1.push_back(29);{Vector b(a1);b.push_back(2);check(b[4] == 2);check(b[0] == 16);}{Vector c;c = a1;std::cout << std::endl;}assert(a1.size() == sizeof(myints) / sizeof(int));{Vector c;c = fifth;c[0] = 1;assert(c[0] == 1);}
}int main()
{test1();test2();test3();return 0;
}

正确完整输出如下

line:180 Pass
line:185 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:188 Pass
line:190 Pass
line:197 Pass
line:206 Pass
The contents of fifth are: 16 2 77 29
The contents of fifth are: 16 2 77 29 30
The contents of first are: 16 2 77 29 30
line:258 Pass
line:259 PassMemory leak report:
No memory leak.

答案在此

动态数组 Vector(难度1)(答案)

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

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

相关文章

简易虚拟培训系统-UI控件的应用1

目录 前言 UI结构总体介绍 建立初步的系统UI结构 Image控件 前言 前面的文章介绍了关于Oculus设备与UI控件的关联&#xff0c;从本文开始采用小示例的方式介绍基本的UI控件在系统中的基本作用&#xff08;仅介绍“基本作用”&#xff0c;详细的API教程可参考官方文档&#x…

从0到1学会Git(第一部分):Git的下载和初始化配置

1.Git是什么: 首先我们看一下百度百科的介绍:Git&#xff08;读音为/gɪt/&#xff09;是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 …

前端调用电脑摄像头

项目中需要前端调用&#xff0c;所以做了如下操作 先看一下效果吧 主要是基于vue3&#xff0c;通过canvas把画面转成base64的形式&#xff0c;然后是把base64转成 file文件&#xff0c;最后调用了一下上传接口 以下是代码 进入页面先调用一下摄像头 navigator.mediaDevices.ge…

NTP时钟同步服务器

目录 一、什么是NTP&#xff1f; 二、计算机时间分类 三、NTP如何工作&#xff1f; 四、NTP时钟同步方式&#xff08;linux&#xff09; 五、时间同步实现软件&#xff08;既是客户端软件也是服务端软件&#xff09; 六、chrony时钟同步软件介绍 七、/etc/chrony.conf配置文件介…

构建 NodeJS 影院预订微服务并使用 docker 部署(04/4)

一、说明 构建一个微服务的电影网站&#xff0c;需要Docker、NodeJS、MongoDB&#xff0c;这样的案例您见过吗&#xff1f;如果对此有兴趣&#xff0c;您就继续往下看吧。 我们前几章的快速回顾 第一篇文章介绍了微服务架构模式&#xff0c;并讨论了使用微服务的优缺点。第二篇…

RHCE——十二、Mysql服务

Mysql服务 一、什么是数据库1、数据&#xff1a;2、数据库&#xff1a; 二、mysql概述三、版本及下载四、yum仓库安装1、添加yum源2、安装3、后续配置 五、本地RPM包安装1、使用迅雷下载集合包2、上传数据3、安装 六、生产环境中使用通用二进制包安装1、作用2、软件包下载3、使…

结算日-洛谷

结算日 - 洛谷 解释&#xff1a; 1.用sum记录贝西走到某位置的累计的总钱&#xff0c;flag标记是否有欠债还不了的情况&#xff08;1为有&#xff09;&#xff0c;ans记录步数。 2.若sum<0&#xff0c;则欠债无法还&#xff0c;flag标记为1&#xff0c;并记录下此刻的位置…

方面级别情感分析之四元组预测

情感四元组预测现有方法 阅读本文之前我们默认你对情感分析有基本的认识。 如果没有请阅读文章(https://tech.tcl.com/post/646efb5b4ba0e7a6a2da6476) 情感分析四元组预测涉及四个情感元素: 方面术语a&#xff0c;意见术语(也叫观点术语)o&#xff0c; 方面类别ac&#xff0c…

Kotlin入门1. 语法基础

Kotlin入门1. 语法基础 一、简介二、在Idea创建一个示例项目三、基本语法1. 第一个程序2. 基本数据类型(1) 数字(2) 类型转换(3) 数学运算位运算 &#xff08;4&#xff09;可空类型 3. 函数4. 字符串(1) 字符串拼接(2) 字符串查找(3) 字符串替换(4) 字符串分割 5. null 安全的…

C++ 多重继承

所谓多重继承就是一个儿子有好几个爹&#xff0c;然后一个人继承了这几个爹的财产。只需注意构造顺序即可&#xff0c;反正析构的顺序也是一样的。 #include <iostream> #include <string.h> using namespace std;class base_a { public:base_a(const char *str){…

SpringBoot中自定义starter

SpringBoot自动装配原理&#xff1a; EnableAutoConfiguration注解开启自动装配功能&#xff0c;该注解通常放在应用的主类上。spring.factories文件位于META-INF目录下的配置文件中定义各个自动装配类的全限定名 当SpringBoot启动时&#xff0c;会加载classpath下所有的spri…

智能化新十年,“全栈智能”定义行业“Copilot智能助手”

“智能化转型是未来十年中国企业穿越经济周期的利器”&#xff0c;这是联想集团执行副总裁兼中国区总裁刘军在去年联想创新科技大会上做出的判断&#xff0c;而2023年正值第四次工业革命第二个十年的开端&#xff0c;智能化是第四次工业革命的主题。2023年初&#xff0c;基于谷…