vector的自实现(迭代器失效问题)与继承续讲

引子:在生活中,我们使用vector很频繁,我们是不是可以自己来实现一个呢?,其实也不难,我们在学过数据结构,就很easy啦!

As we all kowmn,vector基础有以下函数!

from my  perspective<实现代码

#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
#include<algorithm>
using namespace std;

namespace My_vector
{
    template<class T>
    class vector
    {
    public:
        // Vector的迭代器是一个原生指针
        typedef T* iterator;//进行统一的命名
        iterator begin()
        {
            return _start;
        }
        iterator end()
        {
            return _finish;
        }

        typedef const T* const_iterator;
        const_iterator cbegin() const
        {
            return _start;
        }
        const_iterator cend() const
        {
            return _finish;
        }
        
        size_t capacity() const
        {
            return _end_of_storage - _start;
        }
        size_t size()  const
        {
            return _finish - _start;
        }

        void reserve(size_t n)
        {
            if (n > capacity())
            {
                size_t oldsize = size();
                T* tmp = new T[n];//创建临时变量,利用完后会进行析构
                if (_start)//进行拷贝数据
                {
                    for (size_t i = 0; i < oldsize; i++)
                    {
                        tmp[i] = _start[i];
                    }
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + oldsize;
                _end_of_storage = _start + n;
            }
        }
        void push_back(const T& x)
        {
            if (_finish == _end_of_storage)//进行扩容操作
            {
                size_t newcapacity = (_end_of_storage-_start)== 0 ? 4 :(_end_of_storage - _start)*2;
                reserve(newcapacity);
            }
            *_finish = x;
            _finish++;
        }
        void resize(size_t n, const T& value = T())//默认的升序的排序
        {
            if (n <= size())
            {
                _finish = _start+n;
            }
            else
            {
                for (int i = 0; i < (n - size()); i++)
                {
                    push_back(value);
                }
            }
        }
        void swap(vector<T>&v)
        {
            std::swap(_start, v._start);
            std::swap(_finish, v._finish);
            std::swap(_end_of_storage, v._end_of_storage);
        }
        vector() 
            : _start(nullptr), 
            _finish(nullptr), 
            _end_of_storage(nullptr)
        {}

        vector(int n, const T& value = T())
            : _start(nullptr),
            _finish(nullptr),
            _end_of_storage(nullptr)
        {
            reserve(n);
            for (int i = 0; i < n; i++)
            {
                push_back(value);
            }
        }

        template<class InputIterator>
        vector(InputIterator first=nullptr, InputIterator last=nullptr)
        {
            reserve(last - first);
            while (first != last)
            {
                 push_back(*first);
                 ++first;
            }
        }
        vector(const vector<T>& v)
            : _start(nullptr),
            _finish(nullptr),
            _end_of_storage(nullptr)
        {
            reserve(v.capacity());
            for (auto e : v)
            {
                push_back(e);
            }
        }
        // "=default"函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数
        //
        // vector():default;

        vector<T>& operator=(vector<T> v)
        {
            swap(v);//交换后,不需要的会被析构掉!
            return *this;
        }

        ~vector()
        {
            if (_start)
            {
                delete[] _start;
                _start = _finish = _end_of_storage = nullptr;
            }
        }

        T& operator[](size_t i)
        {
            assert(i < size());

            return _start[i];
        }

        const T& operator[](size_t i) const
        {
            assert(i < size());

            return _start[i];
        }
        void pop_back()
        {
            assert(size() > 0);

            --_finish;
        }

        void erase(iterator pos)
        {
            assert(pos >= _start);
            assert(pos < _finish);

            iterator it = pos + 1;
            while (it != _finish)
            {
                *(it - 1) = *it;
                ++it;
            }

            --_finish;
        }
        //注意迭代器失效问题
        iterator insert(iterator pos, const T& x)
        {
            assert(pos >= _start);
            assert(pos <= _finish);

            if (_finish == _end_of_storage)
            {
                size_t len = pos - _start;

                size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
                reserve(newcapacity);

                pos = _start + len;
            }

            iterator end = _finish - 1;
            while (end >= pos)
            {
                *(end + 1) = *end;
                --end;
            }

            *pos = x;
            ++_finish;

            return pos;
        }
    private:
        iterator _start; // 指向数据块的开始
        iterator _finish; // 指向有效数据的尾
        iterator _end_of_storage; // 指向存储容量的尾
    };
    void text1()
    {
        vector<int>c;

        c.push_back(1);
        c.push_back(2);
        c.push_back(3);
        c.push_back(14);
        c.insert(c.begin() + 2, 29);
        c.insert(c.begin() + 3, 99);
        c.erase(c.begin());
        for (auto e : c)
        {
            cout << e << " ";
        }
    }

}

效果:

疑问+解答:

可不可以用输出流呢?答案是可以的!

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& d)
{
    typename vector<T>::const_iterator cit = d.cbegin();

    while (cit != d.cend())
    {
        out << *cit << endl;
        ++cit;
    }
    //for (auto e : d)
    //{
    //    out << e << " ";
    //}
    return out;
}

迭代器失效问题

个人认为:失效情况一般发生在增容与缩容上

有以下几种情况

一,会引起其底层空间改变的操作,都有可能是迭代器失效,

比如:resize、reserve、insert、assign、 push_back等,也就是说vector底层原理旧空间被释放掉, 而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的 空间,而引起代码运行时崩溃。

解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新 赋值即可。

二,指定位置元素的删除操作--erase

删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代 器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是 没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效 了

也就是,原来的迭代器指向的位置变为了它的下一个。

三,与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

继承

接上回所说!我们讲到继承的默认构造函数!那我们讲讲继承的分类与默认函数!

分类:

一,多层继承是从一个派生类派生出另一个派生类的过程。

二,多重继承是从两个或更多类派生出一个新类的过程

三,混合继承是多种类型继承的组合。

四,分层继承是从一个基类派生出两个或更多个类的过程。

继承的默认函数!

一,默认构造函数
  • 如果基类有一个默认构造函数(即无参数的构造函数),并且派生类没有显式地调用基类的构造函数,那么编译器会隐式地调用基类的默认构造函数。
  • 如果基类没有默认构造函数,但派生类没有显式地调用基类的任何构造函数,那么编译器会报错,因为它不知道如何初始化基类部分
  • 在派生类的构造函数的初始化列表中,可以显式地调用基类的构造函数。
  • 总结:不管子类,都会调用父类的!然后才会调用子类
二,c++继承中默认析构函数
  1. 自动生成:如果派生类没有定义自己的析构函数,编译器会为它生成一个默认析构函数。这个默认析构函数会隐式地调用基类的析构函数(如果存在的话)。

  2. 清理工作当派生类对象被销毁时,其析构函数首先被调用。派生类的析构函数体中通常包含释放派生类成员变量所占用的资源的代码。然后,析构函数会自动调用基类的析构函数,以便释放基类部分所占用的资源。这个过程会一直递归进行,直到最顶层的基类。

  3. 虚析构函数:在涉及多态的情况下(即基类指针或引用可能指向派生类对象时),基类应该声明一个虚析构函数(virtual destructor)。这是因为如果没有虚析构函数,当使用基类指针删除派生类对象时,只有基类的析构函数会被调用,派生类的析构函数不会被调用,这可能导致资源泄露和其他问题。声明虚析构函数可以确保在删除对象时,首先调用派生类的析构函数,然后再调用基类的析构函数。

  4. 删除或禁用默认析构函数:和构造函数一样,析构函数也可以被声明为= delete;来禁用它,或者由于其他原因而变得不可用(如被声明为私有)。如果派生类的析构函数被禁用或不可用,那么该派生类的对象就不能被销毁。

三,c++继承中默认拷贝构造函数

在继承的上下文中,派生类的拷贝构造函数需要特别小心处理,以确保基类部分也被正确地拷贝。

如果派生类没有显式定义拷贝构造函数,编译器会生成一个默认的拷贝构造函数。这个默认的拷贝构造函数会调用基类的拷贝构造函数(如果存在的话)来拷贝基类部分,并逐个成员地拷贝派生类自己的成员。

然而,在某些情况下,你可能需要自定义派生类的拷贝构造函数,以确保正确地拷贝派生类特有的数据成员或执行其他必要的操作。在自定义拷贝构造函数时,你应该显式地调用基类的拷贝构造函数,以确保基类部分也被正确地拷贝。

好了,让我们相遇在下一篇博客!持续分享,大派送!

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

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

相关文章

git 初基本使用-----------笔记(结合idea)

Git命令 下载git 打开Git官网&#xff08;git-scm.com&#xff09;&#xff0c;根据自己电脑的操作系统选择相应的Git版本&#xff0c;点击“Download”。 基本的git命令使用 可以在项目文件下右击“Git Bash Here” &#xff0c;也可以命令终端下cd到指定目录执行初始化命令…

YOLOv1 中的一些计算

YOLOv1 中的一些计算 位置参数计算 在训练过程中&#xff0c;需要分别计算真实框的位置参数 t x t_x tx​、 t y t_y ty​、 w w w、 h h h。 已知真实框的左上角点和右下角点坐标&#xff1a; g t [ x 1 , y 1 , x 2 , y 2 ] gt [x_1, y_1, x_2, y_2] gt[x1​,y1​,x2​…

vue elementui简易侧拉栏的使用

目的&#xff1a; 增加了侧拉栏&#xff0c;目的是可以选择多条数据展示数据 组件&#xff1a; celadon.vue <template><div class"LayoutMain"><el-aside :width"sidebarIsCollapse ? 180px : 0px" class"aside-wrap"><…

【机器学习】深度概率模型(DPM)原理和文本分类实践

1.引言 1.1.DPM模型简介 深度概率模型&#xff08;Deep Probabilistic Models&#xff09; 是结合了深度学习和概率论的一类模型。这类模型通过使用深度学习架构&#xff08;如神经网络&#xff09;来构建复杂的概率分布&#xff0c;从而能够处理不确定性并进行预测。深度概率…

Linux通过expect实现免交互

免交互 Here Document 用于将多行字符串直接传递给命令的方式&#xff0c;不需要人为交互命令界面&#xff0c;实现免交互 当使用Here Document操作文件时&#xff0c;需要借助一个文件结束符 EOF&#xff1a;文件结束符 示例 在脚本文件中写入以下内容 <<&#x…

AFLNet入门教学——测试RTSP协议实现Live555(Ubuntu)

1、简介 本文旨在使用AFLNet对RTSP协议实现Live555进行模糊测试。实验环境为&#xff1a;Ubuntu22.04.4AFLNet安装参考&#xff1a;AFLNet入门教学——安装&#xff08;Ubuntu22.04.4&#xff09;-CSDN博客 2、安装Live555 本次实验采取的是live555在2018年8月28日上传的版本…

nacos漏洞汇总

1 nacos介绍 1.1 nacos是啥 Alibaba Nacos是阿里巴巴推出来的一个新开源项目&#xff0c;是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。致力于帮助发现、配置和管理微服务。Nacos提供了一组简单易用的特性集&#xff0c;可以快速实现动态服务发现、服…

【Knowledge Graph Context-Enhanced Diversified Recommendation(MSDM2024)】

Knowledge Graph Context-Enhanced Diversified Recommendation 摘要 推荐系统&#xff08;RecSys&#xff09;领域已被广泛研究&#xff0c;以通过利用用户的历史交互来提高准确性。 尽管如此&#xff0c;这种对准确性的持续追求常常导致多样性的减少&#xff0c;最终导致众所…

全网最强MyBatisPlus教程 | 万字长文爆肝MyBatisPlus

MyBatisPlus 今日目标 基于MyBatisPlus完成标准Dao的增删改查功能 掌握MyBatisPlus中的分页及条件查询构建 掌握主键ID的生成策略 了解MyBatisPlus的代码生成器 1&#xff0c;MyBatisPlus入门案例与简介 这一节我们来学习下MyBatisPlus的入门案例与简介&#xff0c;这个和其他…

在线AI聊天对话机器人,答你所问!

随着AI人工智能技术的不断进步&#xff0c;AI聊天对话机器人已经成为我们日常生活中常用的工具。这些智能助手不仅能够提供信息查询、生活娱乐、学习辅导等服务&#xff0c;还能通过自然语言处理和机器学习技术理解用户的需求&#xff0c;提供个性化的回答和建议。下面小编就和…

选择第三方软件测试机构做验收测试的好处简析

企事业单位在自行开发完软件系统或委托软件开发公司生产软件之后&#xff0c;有一个必经流程就是验收测试&#xff0c;以验证该产品是否符合用户需求、是否可以上线。为了客观评估所委托生产的软件质量&#xff0c;第三方软件测试机构往往成为企事业单位做验收测试的首选&#…

如何有效降低云消息使用成本?涂鸦Pulsar云消息史诗级大更新来了!超级干货攻略快收藏

月末了&#xff0c;相信大家都会有信用卡额度超支的担忧&#xff0c;生怕一不留神就会超出预算&#xff0c;并且事后还需要仔细核对消费情况。类似的焦虑&#xff0c;也会出现在使用涂鸦 Pulsar 云消息服务时。虽然涂鸦 Pulsar 云消息能满足开发者对设备各类事件实时性和持久化…

ADI-DSP|在指定内存写入数据

一、LDF文件设置内存空间 user_data_test { TYPE(BW RAM) START(0x00380010) END(0x0039bfff) WIDTH(8) }//usr data dxe_user_data_bw BW{INPUT_SECTION_ALIGN(4)INPUT_SECTIONS( $OBJS_LIBS(user_data) )} > user_data_test 二、在C文件中设置数据 /************…

使用LabVIEW和示波器测试IGBT参数

使用LabVIEW和示波器测试绝缘栅双极型晶体管&#xff08;IGBT&#xff09;参数的综合解决方案。过程包括硬件设置、示波器和其他必要设备的配置&#xff0c;以及开发LabVIEW程序以自动化数据采集、过滤、关键参数计算和结果显示。该方法确保了IGBT测试的准确性、可靠性和高效性…

巡检机器人智能联网,促进工厂自动化

随着工业4.0和智能制造的快速发展&#xff0c;企业引入自动化设备和智能机器人以提高生产效率和降低人工成本已成为大势所趋。其中&#xff0c;巡检机器人作为一种能够在复杂和危险环境中进行自动巡检的设备&#xff0c;受到了广泛关注。如何实现巡检机器人稳定、安全的联网是每…

【光伏开发】工商业光伏的优势

随着全球对可再生能源的日益重视和环保意识的增强&#xff0c;工商业光伏作为一种清洁、高效的能源利用方式&#xff0c;正在得到广泛的推广和应用。工商业光伏系统通过安装在工厂、仓库、办公楼等工商业场所的太阳能电池板&#xff0c;将太阳能转化为电能&#xff0c;以满足工…

C# OpenCvSharp 入门

摘要 C# OpenCvSharp 是一个基于OpenCV&#xff08;开源计算机视觉库&#xff09;的C#封装库&#xff0c;它提供了一组功能强大的工具和函数&#xff0c;用于图像处理、计算机视觉和计算机图形学等领域。通过使用OpenCvSharp库&#xff0c;您可以在C#应用程序中轻松地实现各种图…

SpringBoot整合Nacos并开启鉴权

关于Nacos的介绍就不在多说&#xff0c;不知道的可以看看Nacos的官网&#xff1a;nacos.io    1、下载安装Nacos 下载地址&#xff1a;https://nacos.io/download/nacos-server/&#xff0c;下载后将Nacos解压到本地磁盘即可&#xff0c;Nacos使用Java语言开发&#xff0c;因…

Ant Design Vue Upload 自定义上传 customRequest,这一篇很详细

Upload 常用属性和方法 示例上传接口 # 接口文档 url https://www.mocky.io/api/main/upload 头部 x-token: xxx 参数 file: File // 上传的文件 flag: xxx // 上传的标识// 文件上传 api 函数简单封装 export const uploadApi ({ file }) > {const formData new Fo…

Markdown的使用

这里写自定义目录标题 欢迎使用Markdown新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个注脚注…