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

news2024/10/7 8:32:05

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 copy
    Vector& operator=(const Vector& from);//deep copy
    ~Vector();
    int size() const;

    //只读元素
    //参考 https://zhuanlan.zhihu.com/p/539451614
    const 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 ints
    assert(first.empty() == true && first.size() == 0);
    Vector second(4, 100);                       // four ints with value 100
    assert(second.empty() == false);
    assert(second.size() == 4);
    Vector fourth(second);                       // a copy of third
    assert(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 Pass

Memory leak report:
No memory leak.

答案在此

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

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

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

相关文章

数据大小无限制!海量倾斜摄影三维模型在线查看及分享

通常&#xff0c;倾斜摄影三维模型数据量都较大&#xff0c;这是由其高精度、对地表全覆盖的真实影像所决定的。如何将海量倾斜摄影模型数据加载到地图中并进行在线查看是行业用户一直关心的内容&#xff0c;现在通过「四维轻云」就可以在线查看及分享倾斜摄影三维模型。 1、倾…

静态类方法的同步

由于在调用静态方法时&#xff0c;对象实例不一定被创建。因此&#xff0c;就不能使用this来同步静态方法&#xff0c;而必须使用Class对象来同步静态方法。代码如下&#xff1a; 通过synchronized块同步静态方法 public class StaticSyncBlock { public static void…

nacos配置超级管理员账户,只能mysql存储数据(或者其他数据库)

nacos本身是不允许授权超级管理员账号的&#xff0c;也就是角色名“ROLE_ADMIN”。作者在页面上试过了&#xff0c;不必再次尝试改的方式是直接改数据库里面的数据

十八、责任链模式

一、什么是责任链模式 责任链&#xff08;Chain of Responsibility&#xff09;模式的定义&#xff1a;为了避免请求发送者与多个请求处理者耦合在一起&#xff0c;于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链&#xff1b;当有请求发生时&#xff0…

element ui-Pagination

页面分为两个表格&#xff0c;当两边的表格数据量大时&#xff0c;分页样式就会受到影响&#xff0c;可以将跳转按钮的个数减少 页面分页代码如下 页面效果

QT基础教程之七Qt消息机制和事件

QT基础教程之七Qt消息机制和事件 事件 事件&#xff08;event&#xff09;是由系统或者 Qt 本身在不同的时刻发出的。当用户按下鼠标、敲下键盘&#xff0c;或者是窗口需要重新绘制的时候&#xff0c;都会发出一个相应的事件。一些事件在对用户操作做出响应时发出&#xff0c…

【YOLOV5】YOLOV5添加SPPCSPC

当前YOLOV5版本为7.0 第一步 在models/common.py添加SPPCSPC class SPPCSPC(nn.Module):# CSP https://github.com/WongKinYiu/CrossStagePartialNetworksdef __init__(self, c1, c2, n1, shortcutFalse, g1, e0.5, k(5, 9, 13)):super(SPPCSPC, self).__init__()c_ int(2 *…

docker安装及使用-Linux

前提 确保docker支持当前系统版本&#xff0c;docker支持centos 7及以上版本&#xff0c;要求Linux内核版本不低于3.10 cat /etc/redhat-release #查看系统版本 查看内核版本三种方式 cat /proc/version uname -a uname -r 一、安装docker 0、卸载docker&#xff08;根…

二十一、中介者模式

一、什么是中介者模式 中介者&#xff08;Mediator&#xff09;模式的定义&#xff1a;定义一个中介对象来封装一系列对象之间的交互&#xff0c;使原有对象之间的耦合松散&#xff0c;且可以独立地改变它们之间的交互。中介者模式又叫调停模式&#xff0c;它是迪米特法则的典型…

Linux:ansible-playbook配置文件(剧本)

如果你还没有配置基础的ansible和一些基础用法可以去下面的链接 playbook是基于ansible的 Linux&#xff1a;ansible自动化运维工具_鲍海超-GNUBHCkalitarro的博客-CSDN博客 Linux&#xff1a;ansible自动化运维工具_鲍海超-GNUBHCkalitarro的博客-CSDN博客 Linux&…

4.5 放映演示文稿

制作完成的演示文稿最终是为了向观众进行展示&#xff0c;因此掌握演示文稿放映的技巧至关重要。本节主要介绍演示文稿放映的相关技巧&#xff0c;包括排练计时、自定义放映、设置放映方式以及墨迹画笔等内容。 4.5.1 排练计时 当进行演讲汇报时&#xff0c;常常要使用到幻灯…

【PHP】文件操作

文章目录 文件编程的必要性目录操作其它目录操作递归遍历目录PHP5常见文件操作函数PHP4常见文件操作函数其他文件操作函数 文件编程的必要性 文件编程指利用PHP代码针对文件&#xff08;文件夹&#xff09;进行增删改查操作。 在实际开发项目中&#xff0c;会有很多内容&…

kafka原理与应用

架构图 Broker Kafka集群包含多个服务器&#xff0c;服务器节点称为BrokerBroker存储Topic数据 如果某topic有N个partition&#xff0c;集群有N个broker&#xff0c;那么每个broker存储该topic的一个partition。如果某topic有N个partition&#xff0c;集群有(NM)个broker&#…

【期末复习笔记】计算机操作系统

计算机操作系统 进程的描述与控制程序执行进程进程的定义与特征相关概念定义特征进程与程序的区别 进程的基本状态和转换PCBPCB中的信息作用PCB的组织方式 线程进程与线程的比较 处理机调度与死锁处理机调度处理机调度的层次 调度算法处理机调度算法的目标处理机调度算法的共同…

C++ 多重继承

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

内网隧道技术学习

1. 隧道技术 在进行渗透测试以及攻防演练的时候&#xff0c;通常会存在各种边界设备、软硬件防火墙、IPS等设备来检测外部连接情况&#xff0c;这些设备如果发现异常&#xff0c;就会对通信进行阻断。 那么隧道技术就是一种绕过端口屏蔽的通信方式&#xff0c;在实际情况中防…

自用Eclipse配置记录

喜欢用eclipse写代码&#xff0c;由于现在的eclipse配置导出的功能缺失较多。这里开一帖把本人常用的配置记录一番&#xff0c;省得再到处找。 另&#xff1a;工作空间中有个.metadata 目录保存了相关的插件及配置&#xff0c;可以复制到其他空工作间中复用配置。 设置工作空间…

【Tkinter系列07/15】小部件Message、下拉菜单、移动窗

17. 小部件Message 此小部件类似于小部件 &#xff08;请参见第 12 节 “标签小部件”&#xff09;&#xff0c;但它适用于 在多行上显示消息。所有文本将 以相同的字体显示;如果需要显示文本 使用多种字体&#xff0c;请参见第 24 节 “文本小部件”。Label 创建新构件作为子…

【AI】数学基础——数理统计(概念参数估计)

概率论 文章目录 3.6 数理统计概念与定理3.6.1 概率论与数理统计区别3.6.2 基本定理大数定理马尔科夫不等式切比雪夫不等式中心极限定理 3.6.3 统计推断的基本问题 3.7 参数估计3.7.1 频率派点估计法矩阵估计法极大似然估计点估计量的评估 区间估计 3.7.2 贝叶斯派贝叶斯定理条…

26、ADS瞬时波形仿真-TRANSIENT仿真(以共射放大器为例)

26、ADS瞬时波形仿真-TRANSIENT仿真&#xff08;以共射放大器为例&#xff09; 在本科期间&#xff0c;学习模电的时候总是要对各种三极管电路进行MULTISIM仿真&#xff0c;其实ADS具备相同的功能&#xff0c;而且对于射频电路&#xff0c;使用ADS进行仿真可以结合版图进行&am…