【C++学习手札】模拟实现string

news2024/11/28 12:44:33

                                                     🎬慕斯主页修仙—别有洞天

                                                       ♈️今日夜电波:缶ビール—みゆな

                                                                0:41━━━━━━️💟──────── 2:52
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

一、string实际的底层原理

二、string的模拟实现

        基本成员函数 

        构造函数 

        拷贝构造函数

        析构函数

        重载赋值运算符 

        迭代器

        迭代器的概念 

        begin()

        end()

        空间管理

        修改相关

        push_back()

        append ()​编辑

        重载+=运算符

        c_str ()

        clear()

        swap()

         重载[ ](最爱的运算符!!!)

         关系运算符的重载

         基本操作

        查找find() 

        前置知识substr()

        插入insert()

        删除 erase()

        流插入流提取

 总体代码


一、string实际的底层原理

        string类型底层实现通常使用动态数组,因为它允许在运行时动态分配内存空间。这使得字符串的长度可以根据需要进行调整,而不需要提前分配固定大小的空间,并且可以避免缓冲区溢出和内存泄漏等问题。 string类型还提供了各种字符串操作函数,例如拼接、查找、替换、大小写转换等。

    底层实际的储存结构:

class string
{

private:

    char* _str;

    size_t _capacity;

    size_t _size;

};

二、string的模拟实现

        基本成员函数 

        构造函数 

        通过设置缺省参数以及初始化列表来默认构造string对象

string(const char* str = "")
            :_size(strlen(str))
            ,_capacity(_size)
        {
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        在模拟实现string的构造函数时,很多人包括作者我一开始都会想着将他分为有参以及无参两部分,但是这种做法是会产生一定的问题的,如下为作者写出的错误的代码:

class string
    {

    public:
        string()//无参构造函数
            //初始化列表
            :_str(nullptr)
            , _size(0)
            , _capacity(0)
        {

        }
        string(char* str)//带参构造函数->char a[] = "abc";string kk(a)
            :_str(str),
            _size(strlen(str)),
            _capacity(strlen(str))
        {

        }

        string(const char* str)//带参构造函数->string kk("abc")
            :_str(str),
            _size(strlen(str)),
            _capacity(strlen(str))
        {

        }
    private:

        char* _str;

        size_t _capacity;

        size_t _size;

    };

        对于以上代码,若调用如上的带参构造函数中string(const char* str)就会报错,因为str传给_str属于权限放大,现在有两种解决方法,一种是将它改为 string(char* str),但是这样就不能直接传递字符串,需要先生成字符串,还有一种方法是将_str改为const char*类型,但是此时就不能修改_str指向的内容,后续的一些操作会出现一些错误

         拷贝构造函数

        由于我们使用了开辟了空间来用于储存数据,因此我们需要进行深拷贝,防止对于另外一个对象的影响。

        传统写法: 

        开辟对应的空间,然后再给相应的值。

        // 传统写法
        // s2(s1)
        //string(const string& s)
        //{
        //	_str = new char[s._capacity+1];
        //	strcpy(_str, s._str);
        //	_size = s._size;
        //	_capacity = s._capacity;
        //}

        较为推荐的写法:

        通过让构造函数帮我们创建一个构造相应的字符的对象,然后我们再交换两个对象的数据即可得。

void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }

        string(const string& s)
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            string tmp(s._str);
            swap(tmp);
        }

        析构函数

        避免内存泄漏需要自己写一个析构函数用以销毁new在堆区所创建的空间 

        ~string()
        {
            delete[]_str;
            _str = nullptr;
            _size = _capacity = 0;
        }

        重载赋值运算符 

         同上面的拷贝构造函数一样,我们对此也是需要深拷贝的

        传统写法: 

        开辟对应的空间,然后再给相应的值。

        //string& operator=(const string& s)
        //{
        //	if (this != &s)
        //	{
        //		char* tmp = new char[s._capacity + 1];
        //		strcpy(tmp, s._str);

        //		delete[] _str;
        //		_str = tmp;
        //		_size = s._size;
        //		_capacity = s._capacity;
        //	}

        //	return *this;
        //}

        较为推荐的写法:

        通过让构造函数帮我们创建一个构造相应的字符的对象,然后我们再交换两个对象的数据即可得。


        string& operator=(const string& s)
        {
        	if (this != &s)
        	{
        		string tmp(s);
        		//this->swap(tmp);
        		swap(tmp);
        	}

        	return *this;
        }

        再简化一点:通过在传值时进行拷贝构造,然后交换数据。

string& operator=(string tmp)
        {
            swap(tmp);

            return *this;
        }

         迭代器

        迭代器的概念 

         C++迭代器是一种用于遍历容器中元素的对象,它提供了一种统一的接口,使得不同类型的容器都可以使用相同的方式进行遍历。迭代器可以指向容器中的某个元素,并用于访问该元素的值或者进行修改。迭代器通常是一个类,它重载了指针操作符(*和->),以及自增(++)和自减(--)运算符,使得迭代器可以像指针一样进行移动。

        string的迭代器实际实现原理:

        string的迭代器实际上是一个指向string底层存储空间中char类型数据的指针,不同的迭代器类型标志着不同的访问权限。例如,一个string::iterator对象可以用来读写字符,而一个const string::iterator对象只能用来读取字符。

        于是我们定义出以下的迭代器:

        typedef char* iterator;
        typedef const char* const_iterator;

         begin()

        用于返回第一个字符的地址。

        iterator begin()
        {
            return _str; 
        }
        const_iterator begin()const
        {
            return _str; 
        }

      end()

        用于返回后一个字符的后一个字符的地址的地址。其实就是‘\0’的地址

        iterator begin()
        {
            return _str; 
        }
        const_iterator begin()const
        {
            return _str; 
        }

        空间管理

        size_t size()const//大小
        {
            return _size;
        }

        size_t capacity()const//空间
        {
            return _capacity;
        }

        bool empty()const//空?
        {
            return _size == 0;
        }

        void reserve(size_t n)//扩容
        {
            if (n > _capacity)
            {
                char* tmp = new char[n + 1];
                strcpy(tmp, _str);
                delete[]_str;

                _str = tmp;
                _capacity = n;
            }
        }

        void resize(size_t n, char c = '\0')//重设大小
        {
            if (n <= _size)
            {
                _str[n] = '\0';
                _size = n;
            }
            else 
            {
                reserve(n);
                while (_size < n)
                { 
                    _str[_size] = c ;
                    ++_size;
                }

                _str[_size] = '\0';
            }
        }

         修改相关

         push_back()

         根据原型可知此函数是在当前字符串尾插一个字符,其中需要额外注意容量。

        void push_back(char c)
        {
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }

            _str[_size] = c;
            ++_size;
            _str[_size] = '\0';
        }

        append ()

         根据原型可知此函数有很多的函数重载,但是我们最常用的就是尾插一段字符串,对此就不过多进行阐述。也需要额外注意容量。

        void append(const char* str)
        {
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            strcpy(_str + _size, str);//拷贝到原字符串后
            _size += len;
        }

         重载+=运算符

         此处复用了以上的push_back和append函数,这也是根据C++中函数管用的复用习惯来做出的选择。

 string& operator+=(char c)
        {
            push_back(c);
        }

        string& operator+=(const char* str)
        {
            append(str);

            return *this;
        }

        c_str ()

        返回指向数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),该字符表示字符串对象的当前值。简而言之就是将string对象转换为C语言风格类型的字符串。

        const char* c_str()const
        {
            return _str;
        }

        clear()

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

        swap()

        void swap(string& s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }

         重载[ ](最爱的运算符!!!)

        这里其实就是按照数组的相关操作即可,但是要注意越界的判断!

        char& operator[](size_t index)
        {
            assert(index < _size);

            return _str[index];
        }

        const char& operator[](size_t index)const
        {
            assert(index < _size);

            return _str[index];
        }

         关系运算符的重载

        关系运算符的重载的关键就是根据所需进行相应的数据变化以及做出值的返回。 

        bool operator<(const string& s) const
        {
            return strcmp(_str, s._str) < 0;
        }

        bool operator==(const string& s) const
        {
            return strcmp(_str, s._str) == 0;
        }

        bool operator<=(const string& s) const
        {
            return *this < s || *this == s;
        }

        bool operator>(const string& s) const
        {
            return !(*this <= s);
        }

        bool operator>=(const string& s) const
        {
            return !(*this < s);
        }

        bool operator!=(const string& s) const
        {
            return !(*this == s);
        }

         基本操作

        查找find() 

        由原型知,分为按照字符查找以及字符串查找还有常量字符串查找,对此,此次实现字符以及字符串查找。

        对于字符查找: 

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

            return npos;
        }

        对于字符串查找: 

        前置知识substr()

         他的主要作用就是在str中从pos位置开始,截取n个字符,然后将其返回。其中的npos实际上是一个全局的静态变量,实际的赋值为-1,都是由于类型为size_t,因此实际上为最大值!

        string substr(size_t pos, size_t len = npos)
        {
            string s;
            size_t end = pos + len;
            if (len == npos || pos + len >= _size) // 有多少取多少
            {
                len = _size - pos;
                end = _size;
            }

            s.reserve(len);
            for (size_t i = pos; i < end; i++)
            {
                s += _str[i];
            }

            return s;
        }

        // 返回子串s在string中第一次出现的位置
        size_t find(const char* s, size_t pos = 0) const
        {
            const char* p = strstr(_str + pos, s);
            if (p)
            {
                return p - _str;
            }
            else
            {
                return npos;
            }
        }

         插入insert()

        根据所插入的空间大小挪字符



        void insert(size_t pos, char ch)
        {
            assert(pos <= _size);
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }

            size_t end = _size + 1;
            while (end > pos)
            {
                _str[end] = _str[end - 1];
                --end;
            }

            _str[pos] = ch;
            _size++;
        }

        void insert(size_t pos, const char* str)
        {
            assert(pos <= _size);

            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            // 挪动数据
            int end = _size;
            while (end >= (int)pos)
            {
                _str[end + len] = _str[end];
                --end;
            }

            strncpy(_str + pos, str, len);
            _size += len;
        }

        删除 erase()

         删除指定位置的字符,可根据调节删除的数量,默认是指定位置以后全删。

        void erase(size_t pos, size_t len = npos)
        {
            assert(pos < _size);

            if (len == npos || pos + len >= _size)
            {
                _str[pos] = '\0';
                _size = pos;
            }
            else
            {
                size_t begin = pos + len;
                while (begin <= _size)
                {
                    _str[begin - len] = _str[begin];
                    ++begin;
                }
                _size -= len;
            }
        }

        流插入流提取

         重载<<和>>是为了能够像内置一样调用,输入打印。

ostream& operator<<(ostream& out, const string& s)
{
    /*for (size_t i = 0; i < s.size(); i++)
    {
        out << s[i];
    }*/
    for (auto ch : s)
        out << ch;

    return out;
}

istream& operator>>(istream& in, string& s)
{

    s.clear();

    char buff[129];
    size_t i = 0;

    char ch;
    ch = in.get();
    while (ch != ' ' && ch != '\n')
    {
        buff[i++] = ch;
        if (i == 128)
        {
            buff[i] = '\0';
            s += buff;
            i = 0;
        }

        //s += ch;

        ch = in.get();
    }

    if (i != 0)
    {
        buff[i] = '\0';
        s += buff;
    }

    return in;
}

 总体代码

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


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

		friend ostream& operator<<(ostream& out, const string& s);
		friend istream& operator>>(istream& in, string& s);
		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		const_iterator begin() const
		{
			return _str;
		}

		const_iterator end() const
		{
			return _str + _size;
		}

		/*string()
			:_str(new char[1]{'\0'})
			,_size(0)
			,_capacity(0)
		{}*/


		string(const char* str = "")
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		// 传统写法
		// s2(s1)
		//string(const string& s)
		//{
		//	_str = new char[s._capacity+1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}


		 s2 = s3
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		char* tmp = new char[s._capacity + 1];
		//		strcpy(tmp, s._str);

		//		delete[] _str;
		//		_str = tmp;
		//		_size = s._size;
		//		_capacity = s._capacity;
		//	}

		//	return *this;
		//}

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		// s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);
			swap(tmp);
		}

		// s2 = s3
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		string tmp(s);
		//		//this->swap(tmp);
		//		swap(tmp);
		//	}

		//	return *this;
		//}

		// s2 = s3
		string& operator=(string tmp)
		{
			swap(tmp);

			return *this;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		char& operator[](size_t pos)
		{
			assert(pos < _size);

			return _str[pos];
		}

		const char& operator[](size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}

		size_t capacity() const
		{
			return _capacity;
		}

		size_t size() const
		{
			return _size;
		}

		const char* c_str() const
		{
			return _str;
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size] = ch;
					++_size;
				}

				_str[_size] = '\0';
			}
		}

		size_t find(char ch, size_t pos = 0)
		{
			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

		size_t find(const char* sub, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, sub);
			if (p)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos, size_t len = npos)
		{
			string s;
			size_t end = pos + len;
			if (len == npos || pos + len >= _size) // 有多少取多少
			{
				len = _size - pos;
				end = _size;
			}

			s.reserve(len);
			for (size_t i = pos; i < end; i++)
			{
				s += _str[i];
			}

			return s;
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		void insert(size_t pos, char ch)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}

			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			_size++;
		}

		void insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			// 挪动数据
			int end = _size;
			while (end >= (int)pos)
			{
				_str[end + len] = _str[end];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;
		}

		void erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
		}

		bool operator<(const string& s) const
		{
			return strcmp(_str, s._str) < 0;
		}

		bool operator==(const string& s) const
		{
			return strcmp(_str, s._str) == 0;
		}

		bool operator<=(const string& s) const
		{
			return *this < s || *this == s;
		}

		bool operator>(const string& s) const
		{
			return !(*this <= s);
		}

		bool operator>=(const string& s) const
		{
			return !(*this < s);
		}

		bool operator!=(const string& s) const
		{
			return !(*this == s);
		}

		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		//const static size_t npos = -1;  // 特例
		//const static double npos = 1.1;  // 不支持
	public:
		const static size_t npos;
	};

	const size_t string::npos = -1;

	ostream& operator<<(ostream& out, const string& s)
	{
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/
		for (auto ch : s)
			out << ch;

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		//s.reserve(128);

		char buff[129];
		size_t i = 0;

		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			//s += ch;

			ch = in.get();
		}

		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}


}

                      感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                         给个三连再走嘛~  

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

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

相关文章

[模版总结] - 树的基本算法2 - BST

BST定义 BST - Binary Search Tree, 即二叉搜索树(有序二叉树) 特性 中序遍历有序查找/插入/删除某个数值可以通过 即树的高度&#xff0c;最优,最坏 . 有多种改进BST可以动态维持插入删除后树结构能尽可能保持平衡 BST基本操作 查询 - 二分查找 搜索数值 - 二分法 class…

JTS: 25 Index 索引

文章目录 版本代码 版本 org.locationtech.jts:jts-core:1.19.0 链接: github 代码 package pers.stu.index;import org.locationtech.jts.geom.*; import org.locationtech.jts.index.hprtree.HPRtree; import org.locationtech.jts.index.quadtree.Quadtree; import org.lo…

【MySQL8】1130 - Host ‘***‘ is not allowed to connect to this MySOL server

问题描述 使用 Navicat 连接 MySQL8 报错&#xff1a; 1130 - Host *** is not allowed to connect to this MySOL server解决方案 use mysql;select host ,user from user; -- 将 root 用户的主机&#xff08;host&#xff09;值修改为 %&#xff0c;即允许从任何主机连接 …

电脑集中管理软件有哪些?电脑集中管控怎么做

电脑集中管理软件有哪些&#xff1f;电脑集中管控怎么做 电脑集中管理软件是指通过一种软件或系统&#xff0c;对多台电脑进行集中管理和控制的工具。这种软件通常具备远程控制、软硬件监控、自动部署等功能。可以方便企业或组织统一管理电脑资源&#xff0c;提高工作效率。今…

新版软考高项试题分析精选(四)

请点击↑关注、收藏&#xff0c;本博客免费为你获取精彩知识分享&#xff01;有惊喜哟&#xff01;&#xff01; 1、一般而言&#xff0c;大型软件系统中实现数据压缩功能&#xff0c;工作在OSI参考模型的&#xff08; &#xff09;。 A.应用层 B.表示层 C.会话层 D.网络层…

Odoo 15开发手册第七章 记录集 - 使用模型数据

在前面的章节中&#xff0c;我们概览了模型创建以及如何向模型加载数据。现在我们已有数据模型和相关数据&#xff0c;是时候学习如何编程与其进行交互了。 业务应用需要业务逻辑来计算数据、执行验证或自动化操作。Odoo框架API为开发者提供了工具用于实现这种业务逻辑。大多数…

有Mac或无Mac电脑通用的获取安卓公钥的方案

从2023年9月开始&#xff0c;所有上架应用市场的app都需要进行APP备案。 其中后端服务器在阿里云的可以在阿里云备案&#xff0c;后端服务器在腾讯云的可以在腾讯云备案。但无论你是在什么云厂商里做备案&#xff0c;无一例外的是&#xff0c;无论是上架安卓应用还是上架IOS应…

如何将图片转为excel或word?(客户端)

演示软件&#xff1a;金鸣表格文字识别大师3.6.1&#xff08;新版本界面可能会略有不同&#xff09; 第一部分 将图片转为excel或文表混合的word 一般的软件要将图片转为可编辑的excel&#xff0c;都需要待识别的图片要有明显清晰的表格线&#xff0c;但我们程序现已克服了这…

多线程概述及创建

什么是线程&#xff1f; 线程(thread)是一个程序内部的一条执行路径。 我们之前启动程序执行后&#xff0c;main方法的执行其实就是一条单独的执行路径。 程序中如果只有一条执行路径&#xff0c;那么这个程序就是单线程的程序。 多线程是什么&#xff1f; 多线程是指从软…

把GPT知识库当成记事本,非常有趣的玩法,很欢乐!

1. 笔者创建了一个“每天碎碎念”知识库&#xff0c;把重要的事情保存成文件记录&#xff0c;并进行训练。 2. 这样每当我记不清楚的时候 就开始灵魂发问~ 3. GPT最擅长胡编乱造&#xff0c;万一他忽悠我怎么办&#xff0c;别着急。查看“知识原文”就知道他是否忽悠你了。 这…

C# 实时监控双门双向门禁控制板源码

本示例使用设备&#xff1a;实时网络双门双向门禁控制板可二次编程控制网络继电器远程开关-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.…

通信原理板块——脉冲编码调制(PCM)

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、脉冲编码调制PCM原理 将模拟信号…

[Jenkins] Docker 安装Jenkins及迁移流程

系统要求 最低推荐配置: 256MB可用内存1GB可用磁盘空间(作为一个Docker容器运行jenkins的话推荐10GB) 为小团队推荐的硬件配置: 1GB可用内存50 GB 可用磁盘空间 软件配置: Java 8—无论是Java运行时环境&#xff08;JRE&#xff09;还是Java开发工具包&#xff08;JDK&#xff…

算法-链表-简单-相交、反转、回文、环形、合并

记录一下算法题的学习5 在写关于链表的题目之前&#xff0c;我们应该熟悉回忆一下链表的具体内容 什么是链表&#xff1a; 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按线性的顺序存储数据&#xff0c…

力扣第695题 岛屿的最大面积 C++ DFS BFS 附Java代码

题目 695. 岛屿的最大面积 中等 相关标签 深度优先搜索 广度优先搜索 并查集 数组 矩阵 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你…

系列四、JVM的内存结构【本地接口(Native Interface)】

一、组成 本地接口由本地方法栈&#xff08;Native Method Stack&#xff09;、本地方法接口&#xff08;Native Interface&#xff09;、本地方法库组成。 二、本地接口的作用 本地接口的作用是融合不同的编程语言为Java所用&#xff0c;它的初衷是融合C/C程序&#xff0c;Jav…

CTFhub-RCE-过滤运算符

检查网页源代码发现如果用户输入 || &#xff08;逻辑或操作符&#xff09; 或者是 & &#xff08;按位与操作符&#xff09;&#xff0c;就不会执行。直接进行测试&#xff0c;在 URL 后输入本地 IP 进行 ping 命令测试&#xff0c;发现可以正常回显。检查网页源代码发现如…

C++基础从0到1入门编程(一)

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 参考视频&#xff1a;黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难 1 第一个C程序-HelloWorld 编写一个C程序分为四个步骤&#xff1a; &#xff08;1&#xff09;创建项目 &#xff08;2&#xff…

Android开发APP显示头部Bar

Android开发显示头部Bar 需求&#xff1a; 显示如下图&#xff1a; 显示头部Bar&#xff0c;颜色也能自定义。 解决方案 这个修改是在如下三个文件里进行修改&#xff1a; 按顺序修改&#xff1a; themes.xml(night): <resources xmlns:tools"http://schemas.andr…

LeetCode(22)N 字形变换【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; N 字形变换 1.题目 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时&#xff0c;排列如下&#xff1a; P A …