模拟实现string【C++】

news2024/7/5 0:26:13

文章目录

  • 全部的实现代码放在了文章末尾
  • 准备工作
    • 包含头文件
    • 定义命名空间和类
      • 类的成员变量
  • 构造函数
    • 默认构造
    • 拷贝构造
  • 重载赋值拷贝函数
  • 析构函数
  • 迭代器和获取迭代器
    • 迭代器
    • 获取迭代器
  • resize【调整size】
    • 图解
  • reserve【调整capacity】
  • empty【判断串是否为空】
  • operator[]
  • append
  • push_back
  • operator+=
  • insert【在pos之前插入字符串】
  • erase【删除从pos开始的len个字符】
  • swap
  • find
  • substr
  • operator+
  • compare
  • 比较运算符重载
    • operator>
    • operator==
    • operator<
    • operator>
    • operator<=
    • operator!=
  • c_str
  • operator<<【输出运算符重载】
  • operator>>【输入运算符重载】
  • 全部代码
    • mystring.h
    • mystring.cpp

全部的实现代码放在了文章末尾

准备工作

创建三个文件,一个头文件mystring.h,两个源文件mystring.cpp tesr.cpp
因为是简单实现,所以只实现了string里放char这一种实现,就没有用模板了

  1. mystring.h:存放包含的头文件,命名空间的定义
  2. mystring.cpp:存放成员函数和命名空间中的函数的定义
  3. test.cpp:存放main函数,以及测试代码

包含头文件

  1. iostream:用于输入输出
  2. string.h:C语言的头文件,用于使用其中的操作字符数组的函数
  3. assert.h:用于使用报错函数

定义命名空间和类

在文件mystring.hmystring.cpp中都定义上一个命名空间mystring
mystring.h中类的声明放进命名空间,把mystring中的函数实现也放进命名空间

注意:
不同源文件的同名的命名空间经过编译链接之后可以合成在一起

类的成员变量

在这里插入图片描述


构造函数

默认构造

在这里插入图片描述

给默认构造的str加上缺省值(“”)就可以实现不传参数时就是空串了


拷贝构造

因为成员str申请了堆区空间,所以要手写拷贝构造,并且要实现深拷贝
在这里插入图片描述


重载赋值拷贝函数

因为成员str申请了堆区空间,所以要手写赋值拷贝,并且要实现深拷贝
在这里插入图片描述
为什么要防止自己给自己赋值?
【this指针指向接收赋值的对象,所以只要this传入的参数的地址相等就是自己赋值给自己】

因为要深拷贝,所以要把一个对象中的所有成员都拷贝一遍,时间复杂度是
O(N),所以有必要防止自己给自己赋值


析构函数

因为成员str申请了堆区空间,所以要手写析构,不能用编译器给的默认析构

在这里插入图片描述


迭代器和获取迭代器

迭代器

因为存放字符的空间的地址是连续的,且只是简单模拟
所以我用了char*作为普通迭代器,const char*作为const迭代器

直接把char*重命名为iterator,把const char*重命名为const_iterator就完成了迭代器的实现


获取迭代器

在这里插入图片描述
因为使用了char*作为迭代器
所以str就是第一个有效元素,str+size就是最后一个有效字符的下一个位置(\0)

又因为const修饰的对象只能调用const修饰的成员函数
所以如果是const修饰的对象调用begin()和end()的时候,就会自动调用到const修饰的begin和end.
在这里插入图片描述


resize【调整size】

void string::resize(size_t n,char c='\0')
{
    if (n > _capacity) 当要调整的容量n大于最大容量时,就要扩容
    {
        char* tmp = new char[n +1];申请size+1个空间,那多出来的1是给'\0'strcpy(tmp, _str);拷贝字符串(把右参数拷贝给左参数)

        delete[] _str;释放扩容前str指向的空间

        _str = tmp;完成扩容

        for (; _size <n; _size++)把多出来的有效字符用c补上
        {
            _str[_size] = c;
        }

        _str[_size] = '\0'; 补完之后加上字符串结束标志'\0'

        _capacity = n;更新最大容量
    }
    else
    {
        if (n > _size)如果调整之后的size  大于  原来的size
                      就要把少(n-size)的有效字符用c补上
        {
            for (; _size < n; _size++)把多出来的有效字符用c补上
            {
                _str[_size] = c;
            }
            _str[_size] = '\0';补完之后加上字符串结束标志'\0'
        }
        else 如果调整之后的size  小于  原来的size
            就要把多的字符删除
        {
            _size = n;调整有效字符大小

            _str[_size] = '\0';直接把n位置改成'\0'即可删除多余字符
        }
    }
}

图解

在这里插入图片描述

在这里插入图片描述


reserve【调整capacity】

在这里插入图片描述


empty【判断串是否为空】

在这里插入图片描述


operator[]

返回值要是char&这样才能像字符数组一样访问和修改串中的字符
在这里插入图片描述


append

在这里插入图片描述

在这里插入图片描述


push_back

可以直接服用append
在这里插入图片描述


operator+=

也可以直接服用append

在这里插入图片描述


insert【在pos之前插入字符串】

在这里插入图片描述


erase【删除从pos开始的len个字符】

在这里插入图片描述


swap

因为存放字符串的空间是在堆区开辟的,用成员str去指向的
所以直接交换两个对象的str中存放的地址就可以完成存储的字符串的交换了

不需要拷贝
在这里插入图片描述

在这里插入图片描述


find

在这里插入图片描述


substr

在这里插入图片描述


operator+

直接复用operator+=
因为+不改变字符串本身,所以要用一个临时对象存储+=之后的字符,再用传值返回即可

在这里插入图片描述


compare

在这里插入图片描述


比较运算符重载

operator>

复用compare
在这里插入图片描述


operator==

复用compare
在这里插入图片描述


operator<

复用operator>operator==
在这里插入图片描述


operator>

复用operator>operator==
在这里插入图片描述


operator<=

复用operator>

在这里插入图片描述


operator!=

复用operator==
在这里插入图片描述


c_str

作用是获取string对象中的str成员。

一般是要在C++中使用C语言函数时使用,因为C语言不支持string,所以只能用字符指针代替一下

在这里插入图片描述

operator<<【输出运算符重载】

重载了之后就可以
直接用cout输出string实例化的对象了

string a(“aaaaa”);
cout<<a<<endl;

在这里插入图片描述


operator>>【输入运算符重载】

重载了之后就可以
直接用cin把字符串输入到string实例化的对象里面了

istream是输入流对象,比如我们常用的  cin  就是  istream实例化的对象

istream& operator>>(istream& is, string& obj)
{
    char str[100];  存储  输入的  每一行的字符

    int sum = 0;  使用sum记录输入的  每一行  的字符个数
    char c = 0;

    \n是换行符(回车),所以  没遇到  \n就  没换行
    while ((c = is.get()) != '\n')  一次读取一个字符
    {
        if (sum == 99)  当sum等于99时说明  str数组存不下了
        {
            str[sum] = '\0';  末尾加上了\0才是字符串

            obj += str;  使用+=把str数组中的字符先加上去

            sum = 0;  sum置成0,继续记录
        }
        else  否则
        {
            把读取到的字符存进str数组里
            str[sum++] = c;
        }
    }

    sum!=0说明最后输入的最后一行字符个数  小于99个,没有+=if (sum != 0)
    {
        str[sum] = '\0';  末尾加上了\0才是字符串

        obj += str;  使用+=把str数组中的字符先加上去
    }
    return is;  为支持链式编程,返回  输入流对象  的引用
}

全部代码

mystring.h

#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;

namespace mystring
{
    class string
    {
    public:
        typedef char* iterator;
        typedef const char* const_iterator;
        

        string(const string& obj);
        string(const char* str = "");
        string& operator=(const string&obj);

        ~string();

        iterator begin();
        const_iterator begin() const;

        iterator end();
        const_iterator end() const;

        size_t size() const;
        size_t capacity() const;
        void resize(size_t n, char c='\0');

        void reserve(size_t n = 0);

        void clear();
        bool empty() const;

        char& operator[](size_t pos);
        const char& operator[](size_t pos)const;

        string& append(const string& obj);
        string& append(char ch);
        void push_back(char c);
        string& operator+=(const string& obj);
        string& operator+=(char c);
        string& assign(const string& str);
        string& insert(size_t pos, const string& str);
        string& insert(size_t pos,char c);
        string& erase(size_t pos = 0, size_t len = npos);
        void swap(string& str);
        const char* c_str() const;
        size_t find(const string& str, size_t pos = 0) const;
        size_t find(char c, size_t pos = 0) const;
        string substr(size_t pos = 0, size_t len = npos) const;
        int compare(const string& str) const;
        string operator+(const string& obj)const;
        string operator+(char c)const;
        bool operator>(const string&obj)const;
        bool operator<(const string& obj)const;
        bool operator>=(const string& obj)const;
        bool operator<=(const string& obj)const;
        bool operator==(const string& obj)const;
        bool operator!=(const string& obj)const;
       
    private:
        char* _str;//指向存放字符串的堆区空间的指针
        size_t _size;//字符串的有效字符个数
        size_t _capacity;//字符串的最大容量

        static const size_t npos;//理论上可以存储的最大字符个数
    };
    ostream& operator<< (ostream& os, const string& obj);
    istream& operator>>(istream& is,string& obj);
}

mystring.cpp

#include"string.h"

namespace mystring
{
    const size_t string::npos = -1;

    string::string(const string& obj)//因为成员在堆区申请了空间所以要写深拷贝的拷贝构造
    {
        _str = new char[obj._size + 1];// 申请size + 1个空间,那多出来的1是给'\0'的


        strcpy(_str, obj._str);//使用该函数把字符串把obj中的字符串拷贝过来


        _capacity = obj._capacity;


        _size = obj._size;
    }

    string::string(const char* str) //让str的缺省值为""(空字符串)

        :_size(strlen(str))//构造函数会先走成员初始化列表,借此先计算出size
    {
         assert(str != nullptr);//防止传入的字符指针是空的

         _str = new char[_size + 1];//申请size+1个空间,那多出来的1是给'\0'的

         strcpy(_str, str);//使用该函数把字符串str中的字符拷贝过来

         _capacity = _size;//设置初始最大容量和size一样大
    }

    string& string::operator=(const string&obj)
    {
        if (this != &obj)//防止自己赋值给自己
        {
            delete[] _str;//先释放接收赋值之前str指向的堆区空间
                          //因为接收赋值之后,str中存放的地址就被覆盖了

            _str = new char[obj._size + 1];//申请size+1个空间,那多出来的1是给'\0'的

            strcpy(_str, obj._str);//拷贝字符串(把右参数拷贝给左参数)

            _size = obj._size;

            _capacity = obj._capacity;
        }
        return *this;
    }


    string::~string()
    {
        delete[] _str;//释放str指向的堆区空间

        _str = nullptr;

        _size = 0;

        _capacity = 0;
    }


    string::iterator string::begin()//普通起始迭代器
    {
        return _str;
    }

    string::const_iterator string::begin() const//const起始迭代器
    {
        return _str;
    }

    string::iterator  string::end()//普通结束迭代器
    {
        return _str + _size;
    }

    string::const_iterator  string::end() const//const结束迭代器
    {    
        return _str + _size;
    }

    
    size_t  string::size() const
    {
        return _size;
    }


    void string::resize(size_t n,char c)
    {
        if (n > _capacity)//当要调整的size,n大于最大容量时,就要扩容
        {
            char* tmp = new char[n +1];//申请size+1个空间,那多出来的1是给'\0'的

            strcpy(tmp, _str);//拷贝字符串(把右参数拷贝给左参数)

            delete[] _str;//释放扩容前str指向的空间

            _str = tmp;//完成扩容

            for (; _size <n; _size++)//把多出来的有效字符用c补上
            {
                _str[_size] = c;
            }

            _str[_size] = '\0'; //补完之后加上字符串结束标志'\0'

            _capacity = n;//更新最大容量
        }
        else
        {
            if (n > _size)//如果调整之后的size  大于  原来的size
                          //就要把少(n-size)的有效字符用c补上
            {
                for (; _size < n; _size++)//把多出来的有效字符用c补上
                {
                    _str[_size] = c;
                }
                _str[_size] = '\0';//补完之后加上字符串结束标志'\0'
            }
            else//如果调整之后的size  小于  原来的size
                //就要把多的字符删除
            {
                _size = n;//调整有效字符大小

                _str[_size] = '\0';//直接把n位置改成'\0'即可删除多余字符
            }
        }
    }


    size_t string::capacity() const
    {
        return _capacity;
    }


    void string::reserve(size_t n)//注意:n是指元素个数,不是字节数
    {
        if (n > _capacity)//当要调整的容量n大于capacity时,才扩容
        {
            char* tmp = new char[n + 1];//申请size+1个空间,那多出来的1是给'\0'的

            strcpy(tmp, _str);//拷贝字符串(把右参数拷贝给左参数)

            delete[] _str;//拷贝之后再释放旧空间

            _str = tmp;//指向新开辟的空间

            _capacity = n;//更改最大容量
        }
    }


    void string::clear()
    {
        _size = 0;
        _str[0] = '\0';
    }



    bool string::empty() const
    {
        return _size == 0;//如果size==0就为真,就会return true
                          //如果size!=0就为假,就会return false
    }


    char& string::operator[](size_t pos)
    {
        assert(pos<_size);//防止越界访问



        return _str[pos];//因为存放字符的空间是连续的
                         //所以直接像数组一样,使用pos位置的字符就可以
    }

    //const修饰的对象会自动调用下面这个
    const char& string::operator[](size_t pos)const
    {
        assert(pos < _size);//防止越界访问


        return _str[pos];
    }

    //在串尾加上一个string对象或者  字符串【可以隐式类型转换为string对象】
    string& string::append(const string& obj)
    {
        if (_capacity < obj._size + _size)//如果容量不够了
        {
            reserve(obj._size + _size);//就扩容
        }

        //从指定地址开始  拷贝字符串(把右参数拷贝给左参数)
        strcpy(_str + _size, obj._str);

        _size += obj._size;//改变有效字符个数

        return *this;
    }

    //在串尾加上一个字符
    string& string::append(char ch)
    {
        if (_capacity <_size+1)//如果容量不够了
        {
            reserve(_size + 1);//就扩容
        }

        _str[_size++] = ch;

        _str[_size] = '\0';//\0被ch覆盖了,再加回来

        return *this;
    }


    void string::push_back(char c)
    {

        append(c);//复用append,尾插一个字符c

    }

    string& string::operator+=(const string& obj)
    {
        append(obj);//复用append,尾插一个string对象/字符串

        return *this;
    }

    string& string::operator+=(char c)
    {
        append(c);//复用append,尾插一个字符c

        return *this;
    }



    string& string::assign(const string& obj)
    {
        *this = obj;
        return *this;
    }



    string& string::insert(size_t pos, const string& obj)
    {
        if (_capacity < obj._size + _size)//如果容量不够了
        {
            reserve(obj._size + _size);//就扩容
        }

        //从要插入的pos位置开始,把pos和其之后的字符都
        //向后移动  传入的对象的size个位置
        for (int i = _size; i >=(int) pos; i--)
        {
            _str[i + obj._size] = _str[i];
        }

        //把字符串插入进去
        for (int i = pos,j=0; i<obj._size+pos; i++,j++)
        {
            _str[i] = obj._str[j];
        }

        _size += obj._size;
        _str[_size] = '\0';//再补上\0
        return *this;
    }

    string& string::insert(size_t pos, char c)
    {
        if (_capacity < _size + 1)
        {
            reserve(_size + 1);
        }
        for (int i = _size; i >=(int) pos; i--)
        {
            _str[i + 1] = _str[i];
        }
        _str[pos] = c;
        return *this;
    }


    string& string:: erase(size_t pos , size_t len)
    {
        if (len > _size - pos)//如果len大于pos及其之后的字符的长度  或者 len==npos
        {
            _str[pos] = '\0';//就直接把pos及其之后的字符  全部删除

            _size = pos;//把size更改成新的有效长度
        }
        else//否则
        {
            //把从pos开始的字符都  用它+len之后的字符进行覆盖,即可完成删除
            for (int i = pos; i <=_size-len; i++)
            {
                _str[i] = _str[i + len];
            }

            _size -= len;//把size更改成新的有效长度
        }
        return *this;
    }


    void string::swap(string& obj)
    {
        //使用库里面的swap把 两个对象的成员变量交换即可
        std::swap(_str, obj._str);
        std::swap(_size, obj._size);
        std::swap(_capacity, obj._capacity);
    }

    //获取string对象中的  str成员
    const char* string::c_str() const
    {
        return _str;
    }

    //从pos位置开始查找字符串
    size_t string::find(const string&obj, size_t pos) const
    {
        const char* p = NULL; //p为找到的字符串的首地址

        //使用string.h里面的strstr查找子串,如果  找不到  就返回NULL
        if ((p=strstr(_str + pos, obj._str)) == NULL)
        {
            return npos;//找不到  就返回  npos
        }
        else
        {
            return p - _str;//返回下标,p为找到的字符串的  首地址
                            //p-字符数组的首地址str  等于str到p之前的元素个数-1,即下标
        }
    }



    size_t string::find(char c, size_t pos ) const
    {
        for (int i = pos; i < _size; i++)
        {
            if (_str[i] == c)
                return i;
        }
        return npos;
    }

    //把从pos开始的长度为len的子串  作为一个新的字符串返回
    string string::substr(size_t pos, size_t len) const
    {
        if (len > _size - pos)//如果len大于pos及其之后的所有 字符的长度或者len==npos
        {
            string tmp(_str + pos);//直接用默认构造把pos及其之后的字符全部  做新串返回

            return tmp;
        }
        else//否则
        {
            //申请长度为len+1的空间,多出的1是给\0的
            char* p = new char[len + 1];

            strncpy(p, _str+pos, len);//把从pos开始的长度为len的子串拷贝给p

            p[len] = '\0';

            string tmp(p);//用默认构造创建出新字符串

            return tmp;
        }
    }

    //比较两个字符串的大小
    //返回值大于0就是  左>右
    // 返回值等于0就是  左=右
    // 返回值小于0就是  左<右
    int string::compare(const string& obj) const
    {
        //使用string.h里面的  strcmp即可完成判断
        return strcmp(_str, obj._str);
    }


    string string::operator+(const string& obj)const
    {
        string tmp(*this);//拷贝构造出一个临时对象

        tmp += obj;//让临时对象去+=,就不会改变字符串自己了

        return tmp;//传值返回
    }



    string string::operator+(char c)const
    {
        string tmp(*this);//拷贝构造出一个临时对象

        tmp += c;

        return tmp;
    }


    bool string::operator>(const string& obj)const
    {
        if (compare(obj) > 0)//返回值大于0就是  左>右
                             //即调用函数的对象>传入的对象
            return true;
        else
            return false;
    }


    bool string::operator<(const string& obj)const
    {
        if (!(*this >= obj))// < 就是>=取反
        {
            return true;
        }
        else
            return false;
    }

    bool string::operator>=(const string& obj)const
    {
        //大于等于  是  大于或者等于
        if (*this > obj || *this == obj)

            return true;
        else

            return false;
    }

    bool string::operator<=(const string& obj)const
    {
        //小于等于就是  不大于,即大于取反
        if (!(*this > obj))
            return true;
        else
            return false;
    }


    bool string::operator==(const string& obj)const
    {
        if (compare(obj) == 0)//返回值大于0就是  左=右
                              //即调用函数的对象=传入的对象
            return true;
        else

            return false;
    }


    bool string::operator!=(const string& obj)const
    {
        //不等于就是  等于取反
        if (!(*this==obj))

            return true;
        else

            return false;
    }

    //ostream是输出流对象,比如我们常用的cout就是  ostream实例化的对象
    ostream& operator<< (ostream& os, const string& obj)
    {

        const char* p = obj.c_str();//获取string对象中的 str成员

        os << p;//可以用字符指针直接输出  它指向  的字符串

        return os;//为支持链式编程,返回输出流对象的引用
    }

    //istream是输入流对象,比如我们常用的  cin  就是  istream实例化的对象
    istream& operator>>(istream& is, string& obj)
    {
        char str[100];//存储  输入的  每一行的字符

        int sum = 0;//使用sum记录输入的  每一行  的字符个数
        char c = 0;

        //\n是换行符(回车),所以  没遇到  \n就  没换行
        while ((c = is.get()) != '\n')//一次读取一个字符
        {
            if (sum == 99)//当sum等于99时说明  str数组存不下了
            {
                str[sum] = '\0';//末尾加上了\0才是字符串

                obj += str;//使用+=把str数组中的字符先加上去

                sum = 0;//把sum置成0,继续记录
            }
            else//否则
            {
                //把读取到的字符存进str数组里
                str[sum++] = c;
            }
        }

        //sum!=0说明最后输入的最后一行字符个数  小于99个,没有+=上
        if (sum != 0)
        {
            str[sum] = '\0';//末尾加上了\0才是字符串

            obj += str;//使用+=把str数组中的字符先加上去
        }
        return is;//为支持链式编程,返回  输入流对象  的引用
    }
}

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

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

相关文章

Stablediffusion SD最好用的图片放大方法 无损4K,8K放大 TILED

Tiled Diffusion Tiled VAE ControlNet Tile模型 只有图生图才能使用Tiled放大倍数。文生图没有放大倍数选项但是可以使用覆盖图像尺寸直接更改尺寸。&#xff08;文生图不容易控制&#xff0c;不如图生图&#xff09; 【采用接力的方法进行放大&#xff1a;先文生图高清修复…

ONLYOFFICE 8.1版本桌面编辑器测评:重塑办公效率的巅峰之作

在数字化办公日益普及的今天&#xff0c;一款高效、便捷且功能强大的桌面编辑器成为了职场人士不可或缺的工具。ONLYOFFICE 8.1版本桌面编辑器凭借其卓越的性能和丰富的功能&#xff0c;成功吸引了众多用户的目光。今天&#xff0c;我们将对ONLYOFFICE 8.1版本桌面编辑器进行全…

Ansys Zemax|在设计抬头显示器(HUD)时需要使用哪些工具?

附件下载 联系工作人员获取附件 汽车抬头显示器或汽车平视显示器&#xff0c;也被称为HUD&#xff0c;是在汽车中显示数据的透明显示器&#xff0c;不需要用户低头就能看到他们需要的重要资讯。这个名字的由来是由于该技术能够让飞行员在头部“向上”并向前看的情况下查看信息…

现如今软考通过率真的很低吗?

刚开始机考&#xff0c;10个人中有3个人表示想要尝试考试&#xff0c;这样通过率能高吗&#xff1f;就拿PMP证书来说吧&#xff0c;一下子就得花费三千多块&#xff0c;有几个人会轻易去尝试呢&#xff1f; 说到底&#xff0c;考试的难度是一个方面&#xff0c;考试的成本低是…

基于边缘智能的沉浸式元宇宙关键技术与展望

源自&#xff1a;大数据 作者&#xff1a;王智 夏树涛 毛睿 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整文章 摘 要 近年来&#xff0c;360度视频、增强现实、虚拟现实等应用蓬勃发展&#xff0c;并逐渐形成元宇宙沉浸…

大模型推理知识总结

一、大模型推理概念 大多数流行的only-decode LLM&#xff08;例如 GPT-3&#xff09;都是针对因果建模目标进行预训练的&#xff0c;本质上是作为下一个词预测器。这些 LLM 将一系列tokens作为输入&#xff0c;并自回归生成后续tokens&#xff0c;直到满足停止条件&#xff0…

瑜伽健身舞蹈教育辅导班培训约课扣课消课课时项目排课管理系统

瑜伽健身舞蹈教育辅导班培训约课扣课消课课时项目排课管理系统 &#x1f31f; 引言&#xff1a;为什么我们需要一个高效的管理系统&#xff1f; 在瑜伽、健身、舞蹈等教育辅导班培训领域&#xff0c;课程的安排、学员的约课、扣课以及消课等管理事务繁琐且重要。传统的人工管理…

Windows kubectl终端日志聚合(wsl+ubuntu+cmder+kubetail)

Windows kubectl终端日志聚合 一、kubectl终端日志聚合二、windows安装ubuntu子系统1. 启用wsl支持2. 安装所选的 Linux 分发版 三、ubuntu安装kubetail四、配置cmder五、使用 一、kubectl终端日志聚合 k8s在实际部署时&#xff0c;一般都会采用多pod方式&#xff0c;这种情况下…

gin中间件

在web应用服务中&#xff0c;完整的业务处理在技术上包含客户端操作&#xff0c;服务端处理&#xff0c;返回处理结果给客户端三个步骤。但是在在更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证&#xff0c;权限管理&#xff0c;安全检查&#xff0c;日志记录等多维…

Python输入与输出基础

Python输入与输出基础 引言 Python是一种非常直观且功能强大的编程语言&#xff0c;它允许用户轻松地处理输入和输出操作。无论是从用户那里获取数据&#xff0c;还是将结果展示给用户&#xff0c;Python都提供了简单易用的函数和方法。 一、输入数据 在Python中&#xff0c…

控制台扫雷(C语言实现)

目录 博文目的实现思路项目创建文件解释 具体实现判断玩家进行游戏还是退出扫雷棋盘的确定地图初始化埋雷玩家扫雷的实现雷判断函数 源码game.cgame.h扫雷.c 博文目的 相信不少人都学习了c语言的函数&#xff0c;循环&#xff0c;分支那我们就可以写一个控制台的扫雷小游戏来检…

AI在创造还是毁掉一些东西

今天突然闪现一个念头&#xff0c;AI真的能带来进步吧。AI能个我们带来什么&#xff1f; 突发这个想法的原因是早上乘车的时候看到一个7,8岁的小孩脖子上带了AI学习机。我在想&#xff0c;小孩都通过AI来学习了&#xff0c;还能提升创造吗&#xff1f;这引起了我的担忧。也许AI…

足底筋膜炎是怎么引起的,怎样治愈?

足底筋膜炎的引起原因及治愈方法可以按照以下几个方面进行清晰归纳&#xff1a; 一、引起原因 &#xff08;1&#xff09;生理因素&#xff1a; 足部缺陷&#xff1a;如扁平足、高弓足等足部问题&#xff0c;会导致足底筋膜受力不均或负荷过重&#xff0c;从而诱发足底筋膜炎。…

Intellij Idea显示回退和前进按钮的方法

方法1 使用快捷键&#xff1a; 回到上一步 ctrl alt <-&#xff08;左方向键&#xff09;回到下一步 ctrl alt ->&#xff08;右方向键&#xff09; 方法2&#xff1a; Preferences -> Appearance & Behavior -> Menus and Toolbars -> Navigation B…

STM32-hal库学习(4)--usart/uart通信 (同时显示在oled)

前言&#xff1a; 关于usart详解&#xff1a; stm32-USART通信-CSDN博客 因为在oled上显示&#xff0c;我们直接在上一个工程进行修改&#xff1a; STM32_hal库学习&#xff08;3&#xff09;-OLED显示-CSDN博客 其他配置与oled显示工程保持不变&#xff0c;打开oled文件的…

信我!这里有普通人也能理解的 Transformer

引言 如今爆火的大模型&#xff0c;GPT-3&#xff0c;BERT 等&#xff0c;通过大量的参数和数据&#xff0c;为我们提供了前所未有的自然语言处理能力&#xff0c;使得机器能够更好地理解和生成人类的语言。而注意力机制无疑是重要的基石之一&#xff0c;作为一种新的神经网络…

2024软件设计师笔记之考点版(一考就过):40-50

软件设计师之一考就过:成绩版 考点40:排序算法(必须记住:插冒归快堆) 1、直接插入排序(这里以从小到大排序为例) ◆要注意的是,前提条件是前i-1个元素是有序的,第i个元素依次从第i-1个元素往前比较,直到找到一个比第i个元素值小的元素,而后插入,插入位置及其后的…

基于51单片机的银行排队呼叫系统设计

一.硬件方案 本系统是以排队抽号顺序为核心&#xff0c;客户利用客户端抽号&#xff0c;工作人员利用叫号端叫号&#xff1b;通过显示器及时显示当前所叫号数&#xff0c;客户及时了解排队信息&#xff0c;通过合理的程序结构来执行排队抽号。电路主要由51单片机最小系统LCD12…

Ngnix内存池——高并发实现高效内存管理

目录 一、高并发下传统方式的弊端 1、常用的内存操作函数 2、弊端一 3、弊端二 4、弊端三 5、弊端四 二、弊端解决之道 1、内存管理维度分析 2、内存管理组件选型 三、高并发内存管理最佳实践 1、内存池技术 2、内存池如何解决弊端 3、高并发内存池如何实现 四、…

【力扣高频题】011. 盛最多水的容器

前面的算法文章&#xff0c;更新了许多 专题系列 。包括&#xff1a;滑动窗口、动态规划、加强堆、二叉树递归套路 等。 还没读过的小伙伴可以关注一下&#xff0c;在主页中点击对应链接查看哦~ 接下来的一段时间&#xff0c;将持续 「力扣高频题」 系列文章&#xff0c;想刷 …