2.2 Vector<T> 动态数组(模板语法)

C++数据结构与算法 目录

本文前驱课程

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

2 动态数组 Vector(难度1)

其中,2 是 1 中的一个作业。2 中详细讲解了动态数组实现的基本原理。

本文目标

1 学会写基本的C++类模板语法;

2 为以后熟练使用 STL 打下基础;

3 为更进一步的阅读和掌握更多的C++库打下基础;

模板语法的学习最恰当的方式就是和非模板代码对比学习。

本文的内容只是在 2 动态数组 Vector(难度1)的基础上,把代码改造为模板语法。

除此之外,不添加任何内容。

模板语法介绍

当类的成员变量是不确定的类型的时候,我们使用模板类( template class)来实现这样的类。

模板类的定义如下:

    template<typename T>  
    struct Vector{
        T data;//成员变量 data 的类型不确定,写成 T
    };

解释说明:

(1)template 表示后续代码中有类型是不确定的,先用typename T当中的T表示类型;

(2)typename表示T是一个未来才能确定的类型

(3)确定T的类型的方式就是创建一个Vector对象的时候在<>中指定:

 Vector<int> arr;  //这条语句表示用int替换代码中的T

非模板语法与模板语法差异对比图

模板语法和非模板语法对比1

模板语法和非模板语法对比2

代码与注释如下

#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添加成员变量;
//可以添加私有成员函数,如果你认为需要的话

template<typename T>
struct Vector
{
public:
    Vector();
    Vector(int n, T value);
    Vector(const Vector& from);
    Vector(int* begin, int* end);
    ~Vector();
    int size() const;
    //只读元素
    //参考 https://zhuanlan.zhihu.com/p/539451614
    const T& operator[](int n)const { return m_data[n]; }
    //写入元素
    T& operator[](int n) { return m_data[n]; }
    void push_back(T value);
    bool empty() const;// your job 1
    void clear();// your job 2
    Vector& operator = (const Vector& from);// your job 4
private:
    void copy(const Vector& from);// your job 3
private:
    int m_element_cout;
    int m_capacity;
    T* m_data;//定义一个元素类型待定的数组起始元素的指针
    //请忽略下面这个成员变量,这个成员变量不影响你的实现,当它不存在即可。
};

//模板类的成员函数都要以下面的template语法开始,和类声明的地方一样
template<typename T>
Vector<T>::Vector()
{
    m_element_cout = 0;
    m_capacity = 10;
    m_data = new int[m_capacity];
}

template<typename T>
Vector<T>::Vector(int n, T value) :Vector()
{
    for (int i = 0; i < n; i++)
        push_back(value);
}

template<typename T>
Vector<T>::Vector(const Vector& from)
{
    m_element_cout = from.m_element_cout;
    m_capacity = from.m_capacity;
    m_data = new int[m_capacity];
    for (int i = 0; i < m_element_cout; i++)
    {
        m_data[i] = from.m_data[i];
    }
}

template<typename T>
Vector<T>::Vector(int* begin, int* end) :Vector()
{
    for (int* p = begin; p < end; p++)
    {
        push_back(*p);
    }
}

template<typename T>
Vector<T>::~Vector()
{
    delete[] m_data;
}

template<typename T>
int Vector<T>::size() const
{
    return m_element_cout;
}

template<typename T>
void Vector<T>::push_back(T value)
{
    if (m_element_cout < m_capacity)
    {
        m_data[m_element_cout] = value;
        m_element_cout++;
    }
    else
    {
        int* p;
        p = new T[2 * m_capacity];
        for (int j = 0; j < m_element_cout; j++)
        {
            p[j] = m_data[j];
        }
        p[m_element_cout] = value;
        m_element_cout = m_element_cout + 1;
        m_capacity = 2 * m_capacity;
        delete[]m_data;
        m_data = p;
    }
}

//(1) your code for : Vector empty()






//(2) your code for : Vector clear()







//(3) your code for : Vector copy()







//(4) your code for : Vector operator =









void test1(void)
{
    Vector<int> v;//创建一个存放int变量的容器v
    int i;
    check(v.size() == 0);
    for (i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
    for (int i = 0; i < 10; i++)
    {
        check(v[i] == i);
    }
    check(v.size() == 10);
}
void test2(void)
{
    int n = 100000;
    Vector<int> 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);
    }
    check(v.size() == n);
}
void print(Vector<int>& 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<int> a;

    Vector<int> first;                   // empty vector of ints
    check(first.empty() == true && first.size() == 0);
    Vector<int> second(4, 100);                       // four ints with value 100
    check(second.empty() == false);
    check(second.size() == 4);
    Vector<int> fourth(second);                       // a copy of third
    check(fourth.size() == second.size());

    int myints[] = { 16,2,77,29 };
    Vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));
    check(fifth.empty() == false);
    check(fifth[0] == 16);
    check(fifth[3] == 29);
    check(fifth.size() == sizeof(myints) / sizeof(int));
    print(fifth, "fifth");//The contents of fifth are:16 2 77 29 
    fifth.push_back(30);
    check(fifth[4] == 30);
    check(fifth.size() == 5);
    print(fifth, "fifth");//The contents of fifth are:16 2 77 29 30 
    check(fifth.size() == sizeof(myints) / sizeof(int) + 1);
    first = fifth = fifth;
    print(first, "first");//The contents of first are:16 2 77 29 30 
    check(first.empty() == false && first.size() == fifth.size());
    Vector<int> a1(myints, myints + sizeof(myints) / sizeof(int));
    //下面大括号是作用域,用来把代码分开互不干扰,这样就可以创建相同的变量名字了,就不用为了起很多不同的变量名而烦恼了。
    {
        Vector<int> b(a1);
        b.push_back(2);
        check(b[4] == 2);
    }
    {
        Vector<int> c;
        c = a1;
    }
    check(a1.size() == sizeof(myints) / sizeof(int));
    {
        Vector<int> c;
        c = fifth;
        c[0] = 1;
        check(c[0] == 1);
    }
}

int main()
{
    test1();
    test2();
    test3();

    return 0;
}

预期输出:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity中Shader的混合模式Blend

文章目录 前言一、混合的作用就是实现各种半透明效果二、混合操作三、在 Shader 中暴露两个属性 来调节 混合的效果 前言 Unity中Shader的混合模式Blend 一、混合的作用就是实现各种半透明效果 这里用PS里的混合作为例子 没选择混合效果前&#xff0c;显示的效果是这样 选择…

2 | Window 搭建单机 Hadoop 和Spark

搭建单机 Hadoop 和 Spark 环境可以学习和测试大数据处理的基础知识。在 Windows 操作系统上搭建这两个工具需要一些配置和设置,下面是一个详细的教程: 注意: 在开始之前,请确保你已经安装了 Java 开发工具包(JDK),并且已经下载了 Hadoop 和 Spark 的最新版本。你可以从…

GitLab启动失败:fail: alertmanager: runsv not running

问题描述 sudo gitlab-ctl restart &#xff0c;报错如下 &#xff1a; summergaoubuntu:/etc/gitlab$ sudo gitlab-ctl start fail: alertmanager: runsv not running fail: gitaly: runsv not running fail: gitlab-exporter: runsv not running fail: gitlab-workhorse: run…

一句话画出动漫效果

链接&#xff1a; AI Comic Factory - a Hugging Face Space by jbilcke-hfDiscover amazing ML apps made by the communityhttps://huggingface.co/spaces/jbilcke-hf/ai-comic-factory 选择类型&#xff1a; Japanese 输入提示词&#xff1a; beauty and school love st…

WebSocket(一)

一.什么是WebSocket 【1】WebSocket是一种协议&#xff0c;设计用于提供低延迟&#xff0c;全双工和长期运行的连接。 全双工&#xff1a;通信的两个参与方可以同时发送和接收数据&#xff0c;不需要等待对方的响应或传输完成。 【2】比较 传统通信&#xff08;http协议&am…

【Linux】文件

Linux 文件 什么叫文件C语言视角下文件的操作文件的打开与关闭文件的写操作文件的读操作 & cat命令模拟实现 文件操作的系统接口open & closewriteread 文件描述符进程与文件的关系重定向问题Linux下一切皆文件的认识文件缓冲区缓冲区的刷新策略 stuout & stderr 什…

【小沐学Unity3d】3ds Max 骨骼动画制作(CAT、Character Studio、Biped、骨骼对象)

文章目录 1、简介2、 CAT2.1 加载 CATRig 预设库2.2 从头开始创建 CATRig 3、character studio3.1 基本描述3.2 Biped3.3 Physique 4、骨骼系统4.1 创建方法4.2 简单示例 结语 1、简介 官网地址&#xff1a; https://help.autodesk.com/view/3DSMAX/2018/CHS https://help.aut…

11.物联网lwip,网卡原理

一。LWIP协议栈内存管理 1.LWIP内存管理方案 &#xff08;1&#xff09;堆heap 1.灰色为已使用内存 2.黑色为未使用内存 3.紫色为使用后内存 按照某种算法&#xff0c;把数据放在内存块中 &#xff08;2&#xff09;池pool 设置内存池&#xff0c;设置成大小相同的内存块。 2…

element-plus指定el-date-picker的弹出框位置

此处记录一下,通过popper-options指定popper出现的位置

【051】基于Vue、Springboot电商管理系统(含源码、详细论文、数据库)

基于Vue、Springboot、Mysql的前后端分离的电商管理系统&#xff0c;不仅功能完善&#xff0c;还有详细课设报告供查看&#xff0c;这不收藏起来&#xff0c;源码和论文获取见文末结尾 部分报告内容如下&#xff08;省略图片&#xff09; c 目录 1 引言 4 1.…

【数学建模】清风数模正课5 相关性分析

相关系数 相关性分析的关键是计算相关系数&#xff0c;在本节课中将会介绍两种常用的相关系数&#xff1a;皮尔逊相关系数&#xff08;Pearson&#xff09;和斯皮尔曼相关系数&#xff08;Spearman&#xff09;。 它们可以用来衡量两个变量间相关性的大小&#xff0c;对于不同…

深入探讨梯度下降:优化机器学习的关键步骤(一)

文章目录 &#x1f340;引言&#x1f340;什么是梯度下降&#xff1f;&#x1f340;损失函数&#x1f340;梯度(gradient)&#x1f340;梯度下降的工作原理&#x1f340;梯度下降的变种&#x1f340;随机梯度下降&#xff08;SGD&#xff09;&#x1f340;批量梯度下降&#xf…

Java“牵手”京东商品列表数据,关键词搜索京东商品数据接口,京东API申请指南

京东商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取京东商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问京东商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…

Linux 学习笔记(1)——系统基本配置与开关机命令

目录 0、起步 0-1&#xff09;命令使用指引 0-2&#xff09;查看历史的命令记录 0-3&#xff09;清空窗口内容 0-4&#xff09;获取本机的内网 IP 地址 0-5&#xff09;获取本机的公网ip地址 0-6&#xff09;在window的命令行窗口中远程连接linux 0-7&#xff09;修改系…

docker安装jenkins

运行jenkins docker run -d \--name jenkins \ --hostname jenkins \-u root \-p 29090:8080 \--restart always \-v D:\springcloud\学习\jekins\jenkins\jks_home:/var/jenkins_home \ jenkins/jenkins获取root登录密码 密码在jekins_home/secrets/initalAdminPassword文件…

设计模式—原型模式(Prototype)

目录 一、什么是原型模式&#xff1f; 二、原型模式具有什么优缺点吗&#xff1f; 三、有什么缺点&#xff1f; 四、什么时候用原型模式&#xff1f; 五、代码展示 ①、简历代码初步实现 ②、原型模式 ③、简历的原型实现 ④、深复制 ⑤、浅复制 一、什么是原型模式&…

Ubuntu学习---跟着绍发学linux课程记录(第二部分)

文章目录 7 文件权限7.1 文件的权限7.2 修改文件权限7.3 修改文件的属主 8、可执行脚本8.2Shell脚本8.3python脚本的创建 9Shell9.1Shell中的变量9.2 环境变量9.3用户环境变量 学习链接: Ubuntu 21.04乌班图 Linux使用教程_60集Linux课程 所有资料在 http://afanihao.cn/java …

单调递增的数字【贪心算法】

单调递增的数字 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 public class Solution {public int monotoneIncreasingDigits…

stm32---用外部中断实现红外接收器

一、红外遥控的原理 红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传 输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是 家用电器广泛采用&#xff0c;并越来越多的应用到计算机系…

【USRP】调制解调系列6:16APSK、32APSK 、基于labview的实现

APSK APSK是&#xff0c;与传统方型星座QAM&#xff08;如16QAM、64QAM&#xff09;相比&#xff0c;其分布呈中心向外沿半径发散&#xff0c;所以又名星型QAM。与QAM相比&#xff0c;APSK便于实现变速率调制&#xff0c;因而很适合目前根据信道及业务需要分级传输的情况。当然…