vector模拟

news2025/1/18 9:45:26
先来看看vector的源码,string没有看是因为string严格意义上来讲不属于STL。
源代码之间也是存在区别的,大同小异,可以去网上查如何下载STL的源码库。
先看看<vector>文件中的内容(当做参考即可):
内容是一些头文件和防止重复包含的内容。看内容大概能猜出vector的函数实现在<stl_vector.h>中
<stl_vector.h>中的内容有500+行,一行一行看是没法看的,首先要学会看它的框架,等有一个具体的问题在去看实现的细节。
vector的基本内容:
与string不同,vector的成员是三个迭代器:start,finish,end_of_storage。
这三个迭代器表示的意义从下面的代码找:
由begin函数和end函数可以确定,start是vector数据的起始位置,end是vector数据的结束位置。由capacity函数可以确定end_of_storage是vector空间的结束位置,三者的关系如下图:
在微软编写的vs编译器中,有模拟出size和capacity;
也可以看原始视图:_Myfirst _Mylast _Myend(不同版本的STL源码之前存在区别)
但是看源码现阶段而言存在一些困难,比如下面这种情况
由函数名可知这是一个尾插函数,但是construct(finish, x)表示什么又要找对应的函数,而且construct还不在当前文件中。
但是一定要学会看源码,在工作中看源码已经成为日常了(不管是开始工作时,还是已经工作很长时间了)。
如何看源码是在学习中逐渐掌握的技能。
下面来模拟实现一下vector,顺便说一下,模拟实现vector是为了加深对vector的理解,不是为了写出更好的vector。
vector.h中
#pragma once
#include<iostream>
#include<algorithm>
#include<assert.h>
using namespace std;
namespace my_vector
{
        template<class T>
        class vector
        {
        public:
               typedef T* iterator;
               typedef const T* const_iterator;
               vector()
                       :_start(nullptr)
                       ,_finish(nullptr)
                       ,_end_of_storage(nullptr)
               {}
               template<class inputiterator> //模板中可以再添加模板
               vector(inputiterator first, inputiterator last)
                       :_start(nullptr)
                       ,_finish(nullptr)
                       ,_end_of_storage(nullptr)
               {
                       while (first != last)
                       {
                              push_back(*first);
                              first++;
                       }
               }
               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(const vector<T>& x) //拷贝构造现代写法
               //vector(const vector& x)没有模板参数也是合法的,虽然很不舒服。写的时候最好把模板参数加上。
                       :_start(nullptr)
                       ,_finish(nullptr)
                       ,_end_of_storage(nullptr)
               {
                       vector<T> tmp(x._start, x._finish); //这里的模板参数也可以省略,但不推荐。
                       swap(tmp);
               }
               vector(size_t n, const T& val = T())
                       :_start(nullptr)
                       , _finish(nullptr)
                       , _end_of_storage(nullptr)
               {
                       reserve(n); //减少扩容次数
                       for (size_t i = 0; i < n; i++)
                       {
                              push_back(val);
                       }
               }
               vector(int n, const T& val = T()) //添加这个函数的原因在test_vector9()中
                       :_start(nullptr)
                       , _finish(nullptr)
                       , _end_of_storage(nullptr)
               {
                       reserve(n);
                       for (int i = 0; i < n; i++)
                       {
                              push_back(val);
                       }
               }
               vector<T>& operator=(vector<T> v) //赋值运算符现代写法
               {
                       swap(v);
                       return *this;
               }
               ~vector()
               {
                       if (_start)
                       {
                              delete[] _start;
                              _start = _finish = _end_of_storage = nullptr;
                       }
               }
               iterator begin()
               {
                       return _start;
               }
               const_iterator begin() const
               {
                       return _start;
               }
               iterator end()
               {
                       return _finish;
               }
               const_iterator end() const
               {
                       return _finish;
               }
               size_t size() const
               {
                       return _finish - _start;
               }
               size_t capacity() const
               {
                       return _end_of_storage - _start;
               }
               void push_back(const T& x)
               {
                       if (_finish == _end_of_storage) //空间满了
                       {
                              size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
                              reserve(newCapacity);
                       }
                       *_finish = x;
                       _finish++;
               }
               void pop_back()
               {
                       if (_finish - _start)
                              _finish--;
               }
               void reserve(size_t n)
               {
                       if (n > capacity())
                       {
                              T* tmp = new T[n];
                              size_t sz = _finish - _start;
                               //提前储存vector数据的大小,在新空间开辟完成后要重新确立finish的位置
                              if (_start) //_start可能为nullptr
                              {
                                       /*memcpy(tmp, _start, size() * sizeof(T));
                                      delete[] _start;*///不使用这部分的代码的原因在test_vector_finally()函数中
                                      for (size_t i = 0; i < size(); i++)
                                      {
                                             tmp[i] = *(_start + i); //tmp是指针,所以能通过下标访问。完成深拷贝。
                                      }
                                      delete[] _start;
                              }
                              _start = tmp;
                              _finish = tmp + sz;
                              _end_of_storage = tmp + n;
                       }
               }
                /*STL库中为:void resize(size_type n, value_type val = value_type());*/
               void resize(size_t n, const T& val = T())
                //有时候存在编译不通过的情况,换成:void resize(size_t n, T val = T())
               //T不知道是什么类型,所以不传参时,调用T的默认构造函数
               //包括int等内置类型,在c++中也是有默认构造函数和析构函数的,因为要支持模板
               {
                       if (n > capacity())
                       {
                              reserve(n);
                       }
                       while (size() < n)
                       {
                              *_finish = val;
                              _finish++;
                       }
                       if(size() > n)
                       {
                              _finish = _start+ n;
                       }
               }
               T& operator[](size_t pos)
                //下标必须size()下,所以下标只能访问_start,_finish之间的数据,reserve提前开辟的空间不能用下标访问。
               {
                       assert(pos < size());
                       return *(_start + pos);
               }
               const T& operator[](size_t pos) const
               {
                       assert(pos < size());
                       return *(_start + pos);
               }
               iterator insert(iterator pos, const T& x)
                        //pos是值传递,pos的改变不会影响it,用引用会报错,比如实参为v.begin(),传入pos的是返回值的中间变量,
                       //具有常属性,iterator无法接收(权限放大),用const_iterator接收pos就不能修改,无法更新位置。
               {
                       assert(pos <= _finish && pos >= _start);
                       if (size() == capacity())
                       {
                              size_t sz = pos - _start;
                              size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
                              reserve(newCapacity);
                              pos = _start + sz; //如果不写这句会发生一个经典的迭代器失效问题
                               //reserve后_start,_finish,_end_of_storage都更新了,vector指向一个新的空间,
                              //但是pos没有更新,还是指向原来的位置,这就导致了在移动数据时,end一直大于pos。
                              //迭代器失效大致分为两种,一种是空间改变导致迭代器指向的位置变化,
                              //另一种是迭代器指向的意义发生变化,比如1,2,3,4,5,迭代器指向2,在2之前插入20,变成1,20,3,4,5,此时
                          //pos指向20,此时pos指向的位置变成了20,空间依然有效,但表示的意义已经变了,这也是一种迭代器失效。
                              //vs中会强制检查(断言)出空间改变所导致的迭代器失效,报错。
                              //Linux则相对随便一点,不会检查出来,甚至还可以修改已经失效空间的数据,不报错。
                              //对于意义失效的迭代器,vs和Linux都可以正常访问(读写),意义的失效更多表现为操作逻辑的错误。
                       }
                       iterator end = _finish - 1;
                        //移动数据
                       while (end >= pos)
                       {
                              end[1] = end[0];
                              end--;
                       }
                       *pos = x;
                       _finish++;
                       return pos;
               }
               iterator erase(iterator pos) //一般vector删除数据都不考虑缩容。
                       //缩容方案在:size() < capacity()/2 时可以考虑。开一个size()大小的空间,拷贝数据,释放旧空间。
                       //缩容方案的本质是时间换空间,现在一般考虑时间效率,不考虑空间效率,所以缩容方案很少用。
               {
                       assert(pos >= _start && pos < _finish);
                       int sz = pos - _start;
                       while (pos != _finish - 1)
                       {
                              *pos = *(pos + 1);
                              pos++;
                       }
                       _finish--;
                       iterator ret = _start + sz;
                       return ret; //这个返回值看起来似乎没有意义,实际上,你没法规定erase结束后是否缩容。
                       //如果不缩容,那么返回值当然没有意义,但是如果缩容,返回值就有意义了。
               }
               void clear()
               {
                       _finish = _start;
               }
        private:
               iterator _start;
               iterator _finish;
               iterator _end_of_storage;
        };
}
test.cpp中
#define _CRT_SECURE_NO_WARNINGS 1
#include"vector.h"
#include<vector>
void test_vector1()
{
        my_vector::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);
        v.pop_back();
        cout << v[3] << endl;
        v.resize(10, 5);
        v.resize(2);
        my_vector::vector<int>::iterator it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector2()
{
        my_vector::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.insert(v.begin(), 0);
        my_vector::vector<int>::iterator it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector3()
{
         //要求在所有偶数前插入数字999
        //用于解释为什么insert要返回一个iterator类型
        my_vector::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        v.push_back(5);
        v.push_back(6);
        my_vector::vector<int>::iterator it = v.begin();
        while (it != v.end())
        {
               if (*it % 2 == 0)
               {
                       it = v.insert(it, 999);
                        //如果insert没有返回值,it就没有办法更新,如果插入数据导致扩容的话,
                       //_start,_finish,_end_of_storage都更新了,it没有办法更新,导致迭代器失效。
                       it++;
               }
               it++;
        }
        it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector4()
{
         //my_vector::vector<int> v;
        std::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        cout << v.size() << ":" << v.capacity() << endl;
        auto it = find(v.begin(), v.end(), 4);
        if (it != v.end())
        {
               v.erase(it); //删除了4,_finish指向的位置变成了3的下一个位置,也是it此时的位置
        }
        cout << v.size() << ":" << v.capacity() << endl;
        *it = 10; //虽然it已经不指向有效数值,但是仍然在合法空间中(这里使用的my_vector中的erase,没有缩容),不会报错
        //在vs下:
        //如果it是标准库中vector<int>的迭代器,那么这样会报错。vs下会进行强制检查,比如删除3,此时it指向4,
        //这时使用迭代器修改数据,编译器认为迭代器意义改变,迭代器失效,会被检查出来,报错。而且erase没有缩容。
        //相同情况下(迭代器意义改变,空间不改变),insert就没有被检查出来。
        //在Linux下:
        //迭代器失效没有被检查出来。it可以正常的被读写。但是这里的it还是属于迭代器失效的,只是Linux对迭代器失效的反应
        //有点不同,用的时候统一以失效的角度去看待。
        cout << *it << endl;
        it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector5() //报错代码
{
        std::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
         //v.push_back(5);标记1
        //删除偶数
        std::vector<int>::iterator it = v.begin();
         //string也存在迭代器失效问题,但是在string的学习中没有提到迭代器失效的问题,原因在于string的insert和erase函数
        //主要使用下标来实现函数接口,虽然也有迭代器实现的,但是不常用,所以不怎么关注。
        //迭代器会持续使用,而下标的改变很明显,变化很容易就能看出来。vector的函数接口几乎都是由迭代器实现的。
        while (it != v.end())
        {
               if (*it % 2 == 0)
               {
                       v.erase(it);
               }
               it++;
        }
         //上面这部分的代码在vs下必报错,it的意义变了。vs的编译器会检查出来。
        //但是相同的代码再加上标记1的代码,在Linux下就能正常运行起来(没有标记1那行代码,会报段错误(数组越界))
     //所以迭代器的意义变了,放在实际环境中也会发生各种问题,比如第一个数据改为10,加上标记1那行代码,Linux中会得到
        //2,3,5这样的结果。2作为偶数,并没有被删除。
        //段错误的成因:数据:1,2,3,4 第一次找到2,删除,数据变成1,3,4 it指向3,++后指向4,4为偶数,删除,数据变成1,3。
        //it指向_finish,++后指向_finish的下一个位置,无法再满足循环终止条件it != v.end()。it不断++,发生段错误。
        //正确的删除方式如test_vector6
        it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector6()
{
        std::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        std::vector<int>::iterator it = v.begin();
        while (it != v.end())
        {
               if (*it % 2 == 0)
               {
                       it = v.erase(it);
               }
               else
               {
                       it++;
               }
        }
        it = v.begin();
        while (it != v.end())
        {
               cout << *it << " ";
               it++;
        }
}
void test_vector7()
{
        my_vector::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        my_vector::vector<int> copy(v.begin(), v.end());
        for (size_t i = 0; i < copy.size(); i++)
        {
               cout << copy[i] << " ";
        }
        cout << endl;
        for (size_t i = 0; i < v.size(); i++)
        {
               cout << v[i] << " ";
        }
}
void test_vector8()
{
        my_vector::vector<int> v;
        v.push_back(1);
        v.push_back(2);
        v.push_back(3);
        v.push_back(4);
        my_vector::vector<int> copy;
        copy = v;
        for (size_t i = 0; i < copy.size(); i++)
        {
               cout << copy[i] << " ";
        }
        cout << endl;
        for (size_t i = 0; i < v.size(); i++)
        {
               cout << v[i] << " ";
        }
}
void test_vector9()
{
         //使用v(10, 5)初始化调用
        //template<class inputiterator>
        //vector(inputiterator first, inputiterator last)
        // 而不是调用:vector(size_t n, const T& val = T())
        //编译错误指向vector(inputiterator first, inputiterator last)中的push_back(*first);
        //换一组变量测试vector<char> v(10, 'a');结果正常
        //原因在于:不同的实参,表示不同的类型,会匹配到最合适的函数中
        //vector<int> v(10,5)实参类型为int int,vector<char> v(10, 'a')实参类型为int char
        //vector<int> v(10,5)实参类型相同,比起vector(size_t n, const T& val = T())需要一次类型转换,
        //vector(inputiterator first, inputiterator last)更匹配,报错是因为first类型变成int了,没法解引用。
        //标准库中的解决方案是写一个重载函数vector(int n, const T& val = T())
        my_vector::vector<int> v(10,5);
        for (auto e : v)
        {
               cout << e << " ";
        }
}
void test_vector_finally()
{
        class Solution
        {
        public:
                //std::vector<vector<int>> generate(int numRows)
               my_vector::vector<vector<int>> generate(int numRows)
               {
                        //std::vector<vector<int>> vv(numRows);std中的vector可以实现杨辉三角的打印
                       my_vector::vector<vector<int>> vv(numRows);
                       for (int i = 0; i < numRows; i++)
                       {
                              vv[i].resize(i + 1);
                              vv[i][0] = 1;
                              vv[i][vv[i].size() - 1] = 1;
                       }
                       for (int i = 0; i < numRows; i++)
                       {
                              for (size_t j = 0; j < vv[i].size(); j++)
                              {
                                      if (vv[i][j] == 0)
                                      {
                                             vv[i][j] = vv[i - 1][j - 1] + vv[i - 1][j];
                                      }
                              }
                       }
                        //在类中打印杨辉三角来观察是类里面导致的问题还是类外面导致的问题
                       for (size_t i = 0; i < vv.size(); i++)
                       {
                              for (size_t j = 0; j < vv[i].size(); j++)
                              {
                                      cout << vv[i][j] << " ";
                              }
                              cout << endl;
                       } //运行后发现是外边的遍历存在问题
                       return vv;
               }
        };
        int num = 0;
        cin >> num;
        Solution s;
         //std::vector<vector<int>> ret = s.generate(num);
        my_vector::vector<vector<int>> ret = s.generate(num); //generate()是传值拷贝,优化为vv直接构造ret
        //其中涉及了一个点叫更深层次的深浅拷贝问题,要是自己找原因会比较困难,直接说结论:函数调用过程
        //vector(const vector<T>& x) -> vector(inputiterator first, inputiterator last) -> void push_back(const T& x) ->
        //void reserve(size_t n)(push_back满时调用)-> memcpy(tmp, _start, size() * sizeof(T));delete[] _start;
         //详细过程:在push_back时,前4个vector<int>空间正常push_back,直到空间满了,需要扩容,新空间的值是通过memcpy 完成值的拷贝的,是浅拷贝,对vector这种需要深拷贝的类型就不能合理完成拷贝。delete后数据清除,空间非法。
        //解决方案也很简单,通过一个一个赋值来完成数据的深拷贝
        for (size_t i = 0; i < ret.size(); i++)
        {
               for (size_t j = 0; j < ret[i].size(); j++)
               {
                       cout << ret[i][j] << " ";
               }
               cout << endl;
        }
}
int main()
{
        test_vector_finally();
        return 0;
}
最后看一下vector<vector<int>>具体样子:

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

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

相关文章

springboot服务端接口公网远程调试 - 实现HTTP服务监听【端口映射】

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…

【分布式系统】分布式锁实现之Redis

锁有资源竞争问题就有一定有锁的存在&#xff0c;存储系统MySQL中&#xff0c;有锁机制保证数据并发访问。而编程语言层面Java中有JUC并发工具包来实现&#xff0c;那么锁解决的问题是什么&#xff1f;主要是在多线程环境下&#xff0c;对共享资源的互斥。从而保证数据一致性。…

SVG在前端中的常见应用

SVG在前端中的常见应用 一、svg标签1. svg2. g 二、描边属性三、模糊和阴影效果1. 模糊2. 阴影效果 四、线性渐变和径向渐变1. 线性渐变2. 径向渐变 五、绘制1. 内置形状元素2. 绘制矩形3. 绘制圆形4. 绘制椭圆5. 绘制线条6. 绘制多边形7. 绘制多线条8. 绘制文本9. 绘制路径 只…

【C/C++】动态内存管理/泛型编程

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

SQLlite教程(第一篇)

SQLlite教程(第一篇 SQLlite是什么?SQLlite工作原理是什么?SQLlite有什么功能和特性?使用SQLlite有哪些注意事项?附加资料 SQLlite是什么? SQLite&#xff0c;是一款轻型的数据库&#xff0c;是遵守ACID的关系型数据库管理系统&#xff0c;它包含在一个相对小的C库中。它是…

Mysql审核查询平台Archery部署

目录 1 Archery产品介绍2 基于docker搭建Archery2.1 系统环境2.2 安装 Docker2.2.1 安装 Docker Compose2.2.2 下载Archery2.2.3 安装并启动2.2.4 表结构初始化2.2.5 数据初始化2.2.6 创建管理用户2.2.7 退出重启2.2.8 日志查看和问题排查2.2.9 启动成功查看2.2.10 端口占用情况…

基于Maven的profiles多环境配置

一个项目通常都会有多个不同的运行环境&#xff0c;例如开发环境&#xff0c;测试环境、生产环境等。而不同环境的构建过程很可能是不同的&#xff0c;例如数据源配置、插件、以及依赖的版本等。每次将项目部署到不同的环境时&#xff0c;都需要修改相应的配置&#xff0c;这样…

day07_数组初识

数组的概述 数组就是用于存储数据的长度固定的容器&#xff0c;保证多个数据的数据类型要一致。 数组适合做一批同种类型数据的存储 数组中的元素可以是基本数据类型&#xff0c;也可以是引用数据类型。当元素是引用数据类型是&#xff0c;我们称为对象数组。 容器&#xff…

从0开始学C语言的个人心得笔记(10w字)

大学的计算机相关专业第一门教学的计算机语言就是c语言&#xff0c;很多大学生面对从未接触过的计算机语言&#xff0c;可能会觉得很难以上门&#xff0c;从而放弃学习c语言。这篇博客写的主要是个人学习C语言时候的知识总结点&#xff0c;不能保证全部是正确的&#xff0c;如有…

Kafka灵魂28问

第 1 题 Kafka 数据可靠性如何保证&#xff1f; 对于 kafka 来说&#xff0c;以下几个方面来保障消息分发的可靠性&#xff1a; 消息发送的可靠性保障(producer) 消息消费的可靠性保障(consumer) Kafka 集群的可靠性保障&#xff08;Broker&#xff09; 生产者 目前生产者…

Leetcode每日一题——“用队列实现栈”

各位CSDN的uu们你们好呀&#xff0c;好久没有更新本专栏啦&#xff0c;甚是想念&#xff01;&#xff01;&#xff01;今天&#xff0c;小雅兰的学习内容是用队列实现栈&#xff0c;下面&#xff0c;让我们进入Leetcode的世界吧&#xff01;&#xff01;&#xff01; 这是小雅兰…

本地 docker 发布 java 项目,连接本地 redis 配置

1、本地项目 install 相应的 jar 包到 target 目录下&#xff0c;jar 包的路径步骤 2 要填写 2、项目根目录下创建 Dockerfile 文件 # 使用官方的 Java 11 镜像作为基础镜像 FROM openjdk:11-jdk# 设置工作目录 WORKDIR /app# 复制应用程序 JAR 文件到镜像中的 /app 目录下 C…

用LangChain实现一个ChatBlog

文章目录 前言环境一、构建知识库二、将知识库向量化三、召回四、利用LLM做阅读理解五、效果总结 前言 通过本文, 你将学会如何使用langchain来构建一个自己的知识库问答 其实大多数类chatpdf产品的原理都差不多, 我将其简单粗暴地分为以下四步: 构建知识库将知识库向量化召回…

vue diff算法与虚拟dom知识整理(11) 书写patch父级新旧为同一节点 子节点与文字交换逻辑实现

上文我们简单描述了patch处理同一节点的大体逻辑 这次 我们就来看一下text替换的情况 我们更改案例入口文件 src下的 index.js 代码如下 import h from "./snabbdom/h"; import patch from "./snabbdom/patch";const container document.getElementById(…

Maven概念及搭建

1.为什么我们要学习 maven? maven 还未出世的时候&#xff0c;我们有很多痛苦的经历 。 痛点 1&#xff1a; jar 包难以寻找 痛点 2&#xff1a; jar 包依赖的问题 痛点 3&#xff1a; jar 不方便管理 痛点 4&#xff1a;项目编译 2.Maven 简介 Maven 是 Apache 软件基金…

Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理

目录 管道&#xff08;channel&#xff09; 无缓冲管道 有缓冲管道 需要注意 goroutine与channel实现并发 单向管道 定义单向管道 将双向管道转换为单向管道 单向管道作为函数参数 单向管道的代码示例 select多路复用 案例演示 goroutine panic处理 案例演示 管道…

APP服务端架构的演变

大家好&#xff0c;我是易安&#xff01; 早期2013年的时候&#xff0c;随着智能设备的普及和移动互联网的发展&#xff0c;移动端逐渐成为用户的新入口&#xff0c;各个电商平台都开始聚焦移动端App&#xff0c;如今经历了10年的发展&#xff0c;很多电商APP早已经没入历史的洪…

日语文法PPT截图31-45

31 形式名词 とき ところ 作为形式名词的话&#xff0c;一般是要写假名不写汉字的 相对时态 如果是一般时/将来时とき&#xff0c;就是先做后面的动作&#xff0c;在做前面的动作。 出教室的时候&#xff0c;关灯。 如果是过去时とき那么&#xff0c;是先做前面的动作&#…

Linux安装elk

稍后补充。 目录 01【安装elk】 es单机 es集群 esHead插件 kibana logstash elastic search:https://www.elastic.co/cn/downloads/elasticsearchlogstash:https://www.elastic.co/cn/downloads/logstashkibana:https://www.elastic.co/cn/downloads/kibana linux下安装E…

vector的介绍

vector的介绍&#xff1a;(vector翻译是向量&#xff0c;但是表示的是顺序表) vector是表示可以改变大小的数组的序列容器。 就像数组一样&#xff0c;vector对其元素使用连续的存储位置&#xff0c;这意味着也可以使用指向其元素的常规指针上的偏移量来访问它们的元素&#xf…