STL?vector!!!

STL?vector!!!

一、前言

        之前我们借助手撕string加深了类和对象相关知识,今天我们将一起手撕一个vector,继续深化类和对象、动态内存管理、模板的相关知识

二、vector相关的前置知识

        1、什么是vector?


        vector是一个STL库中提供的类模板,它是存储元素对象的顺序表,其中提供了一些有关增删查改的接口,它的特点是可以通过下标的方式在表中的任意位置进行读、写

        2、vector中的相关接口

        在本文接下来的部分会介绍vector的常用接口,事实上借助这些接口就可以解决平常所能遇到的大部分问题,如果还需要了解vector提供的更多接口及使用方法的话,可以跳转到一下网页:
        vector - C++ Referencehttps://legacy.cplusplus.com/reference/vector/vector/?kw=vector

三、手撕一个vector类

        1、成员变量与整体框架

        注意:之前的顺序表我们都是通过记录指针、元素个数和空间大小来完成的,顺序表的实现自然也可以这样,但是STL库中不是这样实现的,所以今天我们学习一下STL库中的实现方式

        

        上面的_start、_finish、_endofstorge分别代表着指向有效元素起始位置、终止位置的下一个位置和空间终止位置的迭代器,所以在上面我们已经定义了迭代器,事实上就是指针类型,这是由于顺序表的一大特点就是在内存中连续存储,所以可以直接使用未封装的指针;另一方面,我们在声明成员变量的同时都给了一个缺省值,这是由于它们都是指针类型,在初始化时我们都要先初始化成空指针再进行下一步操作,所以我们可以直接在这里给一个缺省值,这样避免了在初始化列表多次的显示初始化成空指针

        2、构造函数

        库中的函数头:
        

        以上的三个构造函数我们都会一一实现,在这里补充一点:在上面的构造函数中出现了"clloc",事实上,这是一个内存池,而后面所给的缺省值是STL库中提供的一个默认的内存池,它可以有效的提高开辟空间的时间消耗,但是比较复杂,所以在这里我们直接new空间,在之后,我们会专门的进行讲解

                (1).空构造:
                

                在这里实现的空构造是非常简单的,这是由于我们在声明时已经给了缺省值,但是该空构造是一定要写的,这是因为我们还要实现别的构造函数,这时候编译器就不在自动生成默认构造函数了

                (2).用n个元素对象进行初始化:
                

                在使用n个元素对象进行初始化时,我们在参数部分给了一个缺省值T(),很明显,这是一个匿名对象,同时调用了对应的默认构造函数,这是没有问题的,但是如果T是int、float等内置类型呢?事实上,这点不用担心,这是由于C++将这些内置类型都进行了升级,使它们可以象自定义类型一样调用默认构造函数,举个例子:如果T是int类型,那么此时T()就会返沪一个0进行初始化

                (3).使用一个迭代器区间进行初始化:
                

                在上面的构造函数中,我们再次使用了模板,但这里是函数模板,这是由于我们不仅要支持顺序表的迭代器类型初始化,还要支持其它类型的迭代器初始化

        3、返回顺序表一些性质的函数

                (1).size函数:返回此时vector中的元素个数

                库中的函数头:
                

                实现:
                

                (2).capacity函数:返回此时vector中的空间大小

                库中的函数头:
                

                实现:
                

        4、拷贝构造函数

        库中的函数头:

        

        实现:
        

        这里需要注意:我们不可以使用memcpy,这是由于memcpy会按字节将x的内容拷贝到_start,但如果T类型是动态开辟内存,也就是说是深拷贝的话,那么虽然vector整体进行了深拷贝,但是vector中的内容却只完成了浅拷贝,会出现深浅拷贝的问题,此时选择上面的方式就万无一失了,这要求元素对象重载了赋值运算符,这一点是必须的

        5、迭代器相关函数

                (1).begin函数:


                库中的函数头:
                

                begin函数进行了两个重载,分别是普通begin和const begin它们分别可以返回一个可读可写的迭代器和一个只读不写的迭代器,都指向vector的首元素位置:
                

                (2).end函数:


                库中的函数头:
                

                end函数仍然进行了两个重载,返回的迭代器都指向末尾元素的下一个位置:
                

        6、[]操作符重载

        库中的函数头:
        

        库中对[]操作符进行了两个重载,与上面的begin类似:
        

        库中对于很多类型进行了封装,事实上上面我们实现的与库中的本质是一样的

        7、交换函数

        库中的函数头:
        

        这里的交换一定是要涉及深拷贝的,在这里我们借用一下算法库中的swap函数,因为它恰好就是深拷贝:
        

        8、赋值运算符重载

        库中的函数头:
        

        在这里我们对该函数实现进行一点小改动,但是对于用户的使用来说是相同的:
        

        在上面的实现中,我们采用了传值传参,此时形参是实参的拷贝,拷贝会调用我们之前实现过的拷贝构造函数进行深拷贝,再调用swap函数,它也会进行深拷贝之下的交换,最终*this就变成了x的拷贝,最后x这一拷贝出作用域销毁,调用析构函数(后面会实现),进行了空间的释放

        9、开空间相关函数

                (1).reserve函数:会开辟指定大小的空间,如果原来有元素,会进行拷贝,否则不进行任何处理

                库中的函数头:
                

                实现:
                

                在上面我们又遇到了象拷贝构造函数同样的问题,所以再次使用了赋值运算符重载的方式进行处理

                (2).resize函数:会开辟指定大小的空间,用val进行初始化,缺省值为T()

                库中的函数头:
                

                实现:
                

                这里我们直接对之前的函数进行复用即可

        10、插入相关函数

                (1).insert函数:在迭代器指定的位置直接插入一个值,返回最后该位置的迭代器

                库中的函数头:
                

                实现:
                

                从上面的代码可以看出:在insert函数内部可能开辟空间,这时候pos就不是原来的pos了,所以在函数外部,传给过insert函数的迭代器是不能再次使用的,因为使用过的迭代器可能已经失效,这时候我们通过反回值的方式解决了这个问题,所以如果想再次使用的话,要接受函数的返回值

                (2).push_back函数:在vector尾部插入一个对象

                库中的函数头:
                

                实现:
                

                事实上在这个位置我们直接复用刚才写过的insert就非常方便

        11、删除相关函数

                (1).erase函数:删除迭代器指定位置的元素,返回该位置的迭代器

                库中的函数头:
                

                实现:
                

                erase所删除的位置有可能是最后一个,此时删除之后传入的迭代器·就失效了,所以要接收返回值并判断

                (2).pop_back函数

                库中的函数头:

                

                实现:
                

                直接复用刚才写过的erase函数即可

        12、析构函数

        由于析构函数的特殊性,这里就不提供库中的函数头了:

        

四、vector

        下面就是我们今天一起完成的vector了:
        

#include <iostream> #include <cassert> #include <algorithm> using namespace std; namespace bea { template<class T> class vector { typedef T* iterator; typedef const T* const_iterator; public: //空构造 vector() {} //用n个元素对象进行初始化 vector(size_t n, const T& val = T()) { _start = new T[n + 5]; for (size_t i = 0; i < n; i++) { _start[i] = val; } _finish = _start + n; _endofstorge = _start + n + 4; } //使用一个迭代器区间进行初始化 template<class InputIterator> vector(InputIterator first, InputIterator last) { size_t n = last - first; _start = new T[n + 5]; InputIterator it1 = first, it2 = _start; while (it1 < last) { *it2 = *it1; it1++, it2++; } _finish = _start + n; _endofstorge = _start + n + 4; } //拷贝构造函数 vector(const vector& x) { size_t sz = x.size(); _start = new T[sz + 5]; for (size_t i = 0; i < sz; i++) { _start[i] = x._start[i]; } _finish = _start + sz; _endofstorge = _start + sz + 4; } //size函数 size_t size() const { return _finish - _start; } //capacity函数 size_t capacity() const { return _endofstorge - _start + 1; } //begin函数 iterator begin() { return _start; } const_iterator begin() const { return _start; } //end函数 iterator end() { return _finish; } const_iterator end() const { return _finish; } //[]操作符重载 T& operator[](size_t pos) { assert(pos < size()); return _start[pos]; } const T& operator[](size_t pos) const { assert(pos < size()); return _start[pos]; } //交换函数 void swap(vector& x) { std::swap(x._start, _start); std::swap(x._finish, _finish); std::swap(x._endofstorge, _endofstorge); } //赋值运算符重载 vector& operator=(const vector x) { swap(x); return *this; } //开空间相关函数 void reserve(size_t n) { size_t sz = size(); if (n <= size()) return; iterator tmp = new T[n + 5]; if (_start) { for (size_t i = 0; i < sz; i++) { tmp[i] = _start[i]; } delete[] _start; } _finish = tmp + sz; _start = tmp; _endofstorge = tmp + n + 4; } //insert函数 iterator insert(iterator pos, const T& val) { assert(pos <= _finish); int n = pos - _start; if (capacity() < size() + 1) { reserve(size() + 5); } pos = _start + n; iterator end = _finish - 1, next = _finish; while (next > pos) { *next = *end; end--, next--; } *pos = val; _finish++; return pos; } void push_back(const T& val) { insert(_finish, val); } void resize(size_t n, T& val = T()) { vector(n, val); } //erase函数 iterator erase(iterator pos) { assert(pos < _finish); iterator end = pos + 1, front = pos; while (end < _finish) { *front = *end; front++, end++; } _finish--; if (pos == _finish) return nullptr; else return pos; } //pop_back函数 void pop_back() { erase(_finish - 1); } //析构函数 ~vector() { delete[] _start; _start = nullptr; _finish = nullptr; _endofstorge = nullptr; } private: iterator _start = nullptr; iterator _finish = nullptr; iterator _endofstorge = nullptr; }; } 

五、结语

        这就是我们所实现的vector的全部内容了,我们的目的仍然是了解vector的用法、加深类和对象和模板等知识点的理解,感谢大家的阅读,欢迎各位于晏、亦菲和我一起交流、学习、进步!

                

Read more

鸿蒙金融理财全栈项目——基础架构、数据安全、用户体验

鸿蒙金融理财全栈项目——基础架构、数据安全、用户体验

《鸿蒙APP开发从入门到精通》第17篇:鸿蒙金融理财全栈项目——基础架构、数据安全、用户体验 📊🔒🎨 内容承接与核心价值 这是《鸿蒙APP开发从入门到精通》的第17篇——基础架构、数据安全、用户体验篇,完全承接第16篇的鸿蒙电商购物车项目架构,并基于金融场景的高安全、高合规、高性能要求,设计并实现鸿蒙金融理财全栈项目的核心架构与用户体验基础。 学习目标: * 掌握鸿蒙金融理财项目的整体架构设计; * 实现高可用、高安全、高可扩展的金融级架构; * 理解数据安全在金融场景的核心设计与实现; * 实现数据加密、身份认证、安全审计; * 掌握用户体验在金融场景的设计与实现; * 实现无障碍设计、响应式布局、性能优化; * 优化金融理财项目的用户体验(安全性、响应速度、用户反馈)。 学习重点: * 鸿蒙金融理财项目的架构设计原则; * 数据安全在金融场景的应用; * 用户体验在金融场景的设计要点。 一、 金融理财项目架构基础 🎯 1.1 金融理财项目特点 金融理财项目具有以下特点: * 高安全:需要严格的数据加密和身份认证; * 高合规:

By Ne0inhk
Flutter 组件 angel3_auth 适配鸿蒙 HarmonyOS 实战:多策略身份验证,构建全栈式安全鉴权与身份防腐架构

Flutter 组件 angel3_auth 适配鸿蒙 HarmonyOS 实战:多策略身份验证,构建全栈式安全鉴权与身份防腐架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 angel3_auth 适配鸿蒙 HarmonyOS 实战:多策略身份验证,构建全栈式安全鉴权与身份防腐架构 前言 在鸿蒙(OpenHarmony)生态迈向全栈式开发、涉及跨端统一登录、多因子安全验证(MFA)及高性能服务端 API 保护的背景下,如何构建一套坚固、可扩展且具备“多策略适配”能力的身份验证架构,已成为决定全栈系统安全等级与用户信任度的基石。在鸿蒙设备这类强调分布式安全域与跨端信任链的环境下,如果应用依然依赖硬编码的简单鉴权逻辑,由于由于身份上下文的复杂性,极易由于由于“鉴权粒度过粗”导致越权访问或遭受 CSRF/XSS 等复合型攻击。 我们需要一种能够解耦认证逻辑、支持多种插拔式策略(如 JWT、Local、OAuth2)且具备高度可定制性的鉴权中间件。 angel3_auth 为 Dart 全栈开发者引入了“

By Ne0inhk
未来的鸿蒙 App,还需要“首页”吗?

未来的鸿蒙 App,还需要“首页”吗?

子玥酱(掘金 / 知乎 / ZEEKLOG / 简书 同名) 大家好,我是子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。 我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括前端工程化、小程序、React / RN、Flutter、跨端方案, 在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。 技术方向:前端 / 跨端 / 小程序 / 移动端工程化 内容平台:掘金、知乎、ZEEKLOG、简书 创作特点:实战导向、源码拆解、少空谈多落地 文章状态:长期稳定更新,大量原创输出 我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、

By Ne0inhk
【Linux】vim编辑器

【Linux】vim编辑器

vim是什么? vim就是命令行模式下的文本编辑器,相当于windows中的记事本,可以用来进行文本编辑。 vim有三种运行模式,分别可以执行不同的操作: * 普通模式(Normal Mode):用于浏览和编辑文本。 * 插入模式(Insert Mode):用于插入编辑文本,按esc键返回普通模式。 * 命令模式(Command Mode):用于执行命令,如保存、关闭文件,搜索文本等。  vim使用 1、进入vim编辑器 语法:vim [文件名] 在命令行中输入上述语句就能进入对应文件的文本编辑器: 2、进入插入模式 vim打开文件后默认是普通模式。可以通过一些快捷键切换至插入模式 示例: 这时就可以对文本进行编辑了(通过方向键移动光标): 按esc键即可退出插入模式,返回普通模式: 3、退出 同样也需要在普通模式下执行。在普通模式下进行输入操作,就会进入命令模式,可以输入各种命令来执行各种操作: :q:不保存直接退出 :w:保存文件 :wq:保存并退出

By Ne0inhk