【C++】String类的使用(含模拟实现)

目录

一,了解String类

二,String类的构造函数和赋值重载函数

1,构造函数

2,赋值重载函数

三,String类功能的使用

1,容量相关

2,迭代器和范围for遍历字符串

3.类型检查函数

4,const迭代器

5,String的扩容

6,String的大小sizeof

7,容量改变测试

8,String类的插入操作

9,string类的删除操作

10,string中字符的替换

11,find系列

12,非成员函数重载


一,了解String类

string是C++标准库提供的动态字符串类型,定义在头文件<string>当中,完整的类型为:

std::string_basic<char>,日常使用的为别名std::string

string就是一个字符顺序表

引入String类的好处是:用自动化的内存管理 取代了手动危险的内存管理操作

其他的好处例如:

1,自动内存管理(告别缓冲区溢出)

2,操作简单直观(可以直接使用操作符)

3,功能丰富,可以直接使用(不用手动实现)

4,极好的调试和安全性

例:

std::string 始终知道自己有多长(.size() 方法 O(1) 复杂度),而 C 字符串需要用 strlen() 从头遍历计数(O(n) 复杂度)。

通过 .at() 访问字符时会做边界检查(越界抛异常),而 C 数组越界访问通常直接导致未定义行为(难以排查的崩溃)

.at()std::string 提供的一个带边界检查的字符访问方法 。它的作用和 [] 运算符类似,都是用来访问字符串中某个位置的字符,但关键区别在于安全性

使用\[\]不会检查边界,抛出异常,一旦越界就是未定义行为,而.at()会对字符串进行检查,一旦越界就会抛出异常:std::out_of_range

头文件:#include <string>

(了解即可)这张图片表明:这三种类型都是std::basic_string模版类的别名,区别在于底层存储的字符类型不同:

std::string :基于 char 类型(通常占 1 字节)。用于处理 ASCII/UTF-8 编码的文本,是最常用、兼容性最好的字符串类型。

std::u16string :基于 char16_t 类型(占 2 字节)。用于处理 UTF-16 编码,常见于 Windows 系统或某些需要处理非西欧字符(如中文、日文)的跨平台场景。

std::wstring :基于 wchar_t 类型(Windows 占 2 字节,Linux/macOS 占 4 字节)。用于处理"宽字符",在不同平台下编码和大小不固定,现代 C++ 推荐优先使用 stringu16string,以减少跨平台问题。

二,String类的构造函数和赋值重载函数

1,构造函数

常用的六种构造函数:

六种函数的运用实例:

cpp 复制代码
#include <string>
int main()
{
	//1,直接创建一个string类型的变量
	string s1;
	//2,创建并初始化为括号内的字符串
	string s2("AAAABBBB");
	//将s2字符拷贝构造给s3
	string s3(s2);
	//3,从给定字符串下标为4的位置开始,到15的位置结束进行拷贝
	//超出就到给定的字符串的结尾停下,不写15,就到末尾停下
	string s4(s3, 4, 15);
	const char* data = "wwwc\0d1111";
	//4,遇到/0就停止
	string s5(data);
	//5,遇到/0不会停止,即使原来的不够也会拷贝15个
	string s6(data, 15);
	//6,以4个相同的某字符进行构造
	string s7(4, 'x');
	cout << s1 << endl;//什么都没有
	cout << s2 << endl;//AAAABBBB
	cout << s3 << endl;//AAAABBBB
	cout << s4 << endl;//BBBB
	cout << s5 << endl;//wwwc
	cout << s6 << endl;//wwwcd1111
	cout << s7 << endl;//xxxx
	return 0;
}

2,赋值重载函数

展示:

cpp 复制代码
int main()
{
	//1,用另一个字符串对象进行拷贝
	string st1 = "wwwwaaaa";
	string st2 = st1;
	//2,以"\0结尾的字符串"进行赋值
	string st3;
	st3 = "dcccccc";
	//以单个字符进行赋值
	string st4 = "hello";
	st4 = 'A';
	return 0;
}

三,String类功能的使用

1,容量相关

cpp 复制代码
//容量相关 
void test01()
{
	string s1("abcdef");
	cout << s1.length() << endl;//6 //计算元素个数,不支持所有类型,例如树
	cout << s1.size() << endl;//6 //计算元素的个数,支持所有类型
	cout << s1.max_size() << endl;//计算string的size最大大小
	cout << s1.capacity() << endl;//15 //计算容量
}

2,迭代器和范围for遍历字符串

迭代器是STL六大组件之一,他的本质:行为想指针的对象

下面是使用迭代器遍历字符串的例子:

其中还有auto和范围for遍历字符串

auto的作用在于当类型很长时,可以一键代替,减少代码量

end会指向字符串的后一个元素,rend则是第一个元素的前一个位置

迭代器共有4种:正向,反向,const正向,const反向

访问下标:

cpp 复制代码
//迭代器和范围for-auto
int main()
{
	string a1 = "aaabbb";
	//使用下标进行循环打印
	for (size_t i = 0;i < a1.size();i++)
	{
		a1[i] += 2;  //ASCII码值增加2
		cout << a1[i] << " ";
	}
	cout << endl;
	cout << a1 << endl;
	//使用迭代器进行打印
    //迭代器是STL的六大组件之一
	string::iterator g = a1.begin();
	while (g != a1.end())
	{
		*g -= 2;
		cout << *g << " ";
		++g;
	}
	cout << endl;
	cout << a1 << endl;
	//使用auto和范围for auto可以自动识别数据的类型,自动迭代,自动判断结束 
	//auto的底层就是范围迭代器
	for (auto s1 : a1) //auto就相当于一个拷贝,不会修改原来的字符串
	{
		s1 += 2;//但是迭代器可以修改
		cout << s1 << " ";
	}
	cout << endl;
	cout << a1 << endl;
	//要想改变原来的字符串需要使用引用&
	for (auto& s2 : a1)
	{
		s2 += 2;
		cout << s2 << "";
	}
	cout << endl;
	cout << a1 << endl;
	return 0;
}

运行的结果:

反向迭代器:

cpp 复制代码
void test2()
{
	string s1("ab cdef");
	string::iterator c1 = s1.begin();
	while (c1 != s1.end())
	{
		cout << *c1 << " ";
		c1++;
	}
	cout << endl;
	//反向遍历--反向迭代器
	string::reverse_iterator c2 = s1.rbegin();
	while (c2 != s1.rend())
	{
		cout << *c2 << " ";
		c2++; //依旧写++
	}
	cout << endl;
}

运行结果:

补充:auto可以做返回值,但是不建议使用,可能会出现一下问题:

cpp 复制代码
//auto做返回值的缺点
//当多个函数嵌套--难以找到返回值的类型
auto k1()
{
	return 3;
}
auto k2()
{
	return k1();
}
auto k3()
{
	return k2();
}
int main()
{
	//要一路从k3->k2->k1才能找到a的类型是什么
	auto a = k3();
	return 0;
}

3.类型检查函数

语法:typeid(变量).name()

cpp 复制代码
//类型检查函数
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	//如果将''强行塞进两个字符
	//就会变成多字符字面量 类型是int
	//a的ASCII码值是97转化为16进制就是0x61 两个就是0x6161转化为十进制就是24929
	auto d = 'aa';
	auto e = "a";
	auto f = "abc";
	//auto d;
	cout << typeid(a).name() << endl;//int
	cout << typeid(b).name() << endl;//int 
	cout << typeid(c).name() << endl;//char
	cout << typeid(d).name() << endl;//int
	cout << d << endl;//24929
	cout << typeid(e).name() << endl;//char const* __ptr64
	cout << typeid(f).name() << endl;//char const* __prr64
	//f作为一个指针打印的结果却是实际的字符串,这是因为C++对char*进行了重载 
	cout << f << endl;//abc
	return 0;
}

4,const迭代器

cpp 复制代码
//const迭代器  只可以进行读取不可以进行修改
void test3()
{
	string a1("ab cdef");
	//const正向迭代器
	string::const_iterator s1 = a1.begin();
	while (s1 != a1.end())
	{
		//*s1++; 会产生报错
		cout << *s1 << " ";
		s1++;
	}
	cout << endl;
	//const反向迭代器
	string::const_reverse_iterator s2 = a1.rbegin();
	while (s2 != a1.rend())
	{
		cout << *s2 << " ";
		s2++;
	}
	cout << endl;
}

运行结果:

5,String的扩容

cpp 复制代码
//string的扩容
void StrPushBack()
{
	string s1;
	size_t sz = s1.capacity	();
	cout << "first capacity:" << sz << endl;
	for (int i = 0;i < 100;i++)
	{
		s1.push_back('c');
		if (sz != s1.capacity())
		{
			 sz = s1.capacity();
			cout << "capacity:" << sz << endl;
		}
	}
}

运行结果:

解读:刚开始15是因为一个字符给了'\0',进行一次二倍扩,之后进行1.5倍扩。

使用reserve提前扩充:

cpp 复制代码
void StrPushBack2()
{
	string s1;
	s1.reserve(100);//提前预留至少100个空间
	size_t sz = s1.capacity();
	cout << "first capacity:" << sz << endl;
	for (int i = 0;i < 100;i++)
	{
		s1.push_back('c');
		if (sz != s1.capacity())
		{
			sz = s1.capacity();
			cout << "capacity:" << sz << endl;
		}
	}
}

运行结果:

6,String的大小sizeof

cpp 复制代码
//string的结构类似是这样的
//class string
//{
//private:
//	char* buff[16];//最开始开辟的空间不是在堆上开辟的,而是在buff上进行开辟的
//	char* str; //当字符的个数<=15时,存在buff当中,否则就存在堆上,str指向堆中的内存
//	size_t size;
//	size_t capacity;
//	
//};
void test4()
{
	string s1("abc def");
	string s2("aaaaaaaaaaaaaaaaa");
	//按照内存对齐方法计算,类似结构体
	cout << sizeof(s1) << endl;//32位下:28 64位下:40
	cout << sizeof(s2) << endl;//32位下:28 64位下:40
 }

7,容量改变测试

reserve和shrink_to_fit和resize和clear对容量的改变

以及判空的使用:

cpp 复制代码
//reserve和shrink_to_fit和resize和clear对容量的改变
//以及判空的使用
void test5()
{
	string s1("aggdfgdfdsfdffdasfdsd");
	cout << s1.size() << endl;//21
	cout << s1.capacity() << endl;//31 
	//小于size--不变
	s1.reserve(15);
	cout << s1.size() << endl;//21
	cout << s1.capacity() << endl;//31
	//介于size和 capacity之间--依旧不变
	s1.reserve(25);
	cout << s1.size() << endl;//21
	cout << s1.capacity() << endl;//31
	//大于capacity--进行扩容
	s1.reserve(40);
	cout << s1.size() << endl;//21
	cout << s1.capacity() << endl;//47
	cout << endl;

	//进行缩小--capacity缩小到能够容纳size的大小
	s1.shrink_to_fit();
	cout << s1.size() << endl;//21
	cout << s1.capacity() << endl;//31
	cout << endl;

	//resize进行缩小
	//当resize小于size时会减少数据,数据丢失
	s1.resize(15);
	cout << s1.size() << endl;//15
	cout << s1.capacity() << endl;//31
	//介于size与capacity之间,用'\0'代替
	s1.resize(25);
	cout << s1.size() << endl;//25
	cout << s1.capacity() << endl;//31
	cout << endl;

	s1.clear();//清理掉内容,使得size为0,capacity不变
	cout << s1.size() << endl;//0
	cout << s1.capacity() << endl;//31
	cout << endl;

	//判空
	if (s1.empty() == true)
	{
		cout << "为空" << endl;
	}
}

结果:

8,String类的插入操作

尾插单个字符:

尾插字符串:

9,string类的删除操作

10,string中字符的替换

11,find系列

cpp 复制代码
void test3()
{
	string str("fdwsfd.aaa.cc");
	size_t pos = str.find(".");
	//substr取子串
	string d1 = str.substr(pos);
	cout << d1 << endl;
	//从后面开始查找
	pos = str.rfind(".");
	string d2 = str.substr(pos);
	cout << d2 << endl;
	
	string st1("fdsdfgdfdsa_aaakkkk56");
	//从前向后找到字符集合中字符的下标
	size_t p1 = st1.find_first_of("asg");
	cout << st1 << endl;
	while (p1 != st1.npos)
	{
		st1[p1] = '*';
		p1 = st1.find_first_of("asg", p1 + 1);
	}
	cout << st1 << endl;
	//从前向后找到不在字符集合中的下标
	string str2("dfsafadfdffs56");
	size_t p2 = str2.find_first_not_of("ad");
	cout << str2 << endl;
	while (p2 != str2.npos)
	{
		str2[p2] = '*';
		p2 = str2.find_first_not_of("ad", p2 + 1);
	}
	cout << str2 << endl;
	//从后向前找到字符集合中字符的下标
	string str3("/uer/df/dd/a");
	string str4("2026\\August\\test8.20\\Project1\\Project1");
	replacestr(str3);
	replacestr(str4);
	//find_last_not_of:从后向前找不在字符集合范围之内的字符
}

运行结果:

12,非成员函数重载

四,String的模拟实现

代码链接:https://gitee.com/codelsj-w/test8.29.git

头文件:String.h 定义string类以及函数声明

cpp 复制代码
#pragma once
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
namespace stt
{
	class string
	{
	public:
		//iterator的本质就是一个重命名
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}
		//初始化
		//C风格字符串初始化
		string(const char* str = "")//默认值设置一个空,包含斜杠零,防止使用strlen直接报错
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		//string类初始化--深拷贝
		string(const string& st1)
		{
			_str = new char[st1._capacity + 1];
			strcpy(_str, st1._str);
			_capacity = st1._capacity;
			_size = st1._size;
		}

		//s1 = s2
		string& operator=(const string& st2)
		{
			if (this != &st2) //为了避免s1 = s1这种情况
			{
				delete[] _str;
				_str = new char[st2._capacity + 1];
				strcpy(_str, st2._str);
				_capacity = st2._capacity;
				_size = st2._size;
			}
			return *this;
		}
		//函数进行析构
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		//短小频繁调用的函数直接定义在类当中,默认为内联inline
		const char* c_str()const
		{
			return _str;
		}
		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}
		//清除空间 
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
		//下标访问
		char& operator[](size_t n)
		{
			assert(n < _size);
			return _str[n];
		}
		const char& operator[](size_t n)const
		{
			assert(n < _size);
			return _str[n];
		}
		//函数的声明
		void reserve(size_t n);//提前预留空间
		//在结尾插入字符或者字符串
		void push_back(char s);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		//插入字符或字符串
		void insert(size_t pos, char ch);
		void insert(size_t pos, const char* str);
		//从某位置开始删除len个字符
		void erase(size_t pos, size_t len = npos);
		//查找字符或者字符串
		size_t find(char ch, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		//获取子串
		string substr(size_t pos = 0, size_t len = npos);
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
		//静态成员一定在类外面进行初始化
		static const size_t npos;
	};
	//全局变量--类的比较函数
	bool operator>(const string& s1, const string& s2);
	bool operator<(const string& s1, const string& s2);
	bool operator>=(const string& s1, const string& s2);
	bool operator<=(const string& s1, const string& s2);
	bool operator==(const string& s1, const string& s2);
	bool operator!=(const string& s1, const string& s2);
	//重载流插入和流提取
	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& sin, string& s);
}

源文件:String.cpp 函数的实现

cpp 复制代码
#include "String.h"

namespace stt
{
	const size_t string::npos = -1;
	//函数的声明
	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			strcpy(tmp, _str);
			delete[] _str;
			_str = tmp;
			_capacity = n;
		}
	}
	//在结尾插入字符或者字符串
	void string::push_back(char s)
	{
		//满容--扩容
		if (_size == _capacity)
		{
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		_str[_size++] = s;
		_str[_size] = '\0';
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		//开辟空间
		if (len + _size > _capacity)
		{
			reserve(len + _size > 2 * _capacity ? len + _size : 2 * _capacity);
		}
		strcpy(_str + _size, str);
		_size += len;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	//插入字符或字符串
	void string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size == _capacity)//空间已经满了
		{
			//先开辟空间 
			reserve(_capacity == 0 ? 4 : 2 * _capacity);
		}
		//数据向后挪动
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			end--;
		}
		_str[pos] = ch;
		_size++;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size + len > _capacity)
		{
			reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
		}
		//数据向后挪动
		size_t end = _size + len;
		while (end-len+1 > pos)
		{
			_str[end] = _str[end - len];
			end--;
		}
		//数据插入
		const char* ch = str;
		for (int i = 0;i < len;i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
	}
	//从某位置开始删除len个字符
	void string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);//删除最后一个'\0'没有意义,之后还要加上
		if (len >= _size-pos) //pos以及后面的全部删除
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			//将后面没有删除的元素向前移动
			for (size_t i = pos + len;i <= _size;i++)
			{
				_str[i-len] = _str[i];
			}
			_size = _size - len;
		}

	}
	//查找字符或者字符串
	size_t string::find(char ch, size_t pos)
	{
		assert(pos < _size);
		for (size_t i = 0;i < _size;i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	//查找字符串 
	size_t string::find(const char* str, size_t pos)
	{
		assert(pos < _size);
		const char* ptr = strstr(_str + pos, str);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;//指针相减得到中间差了多少字符
		}
	}
	//获取子串
	string string::substr(size_t pos, size_t len)
	{
		assert(pos < _size);
		//判断len是否大于剩余字符,大于的话就进行更新
		if (len > _size - pos)
		{
			len = _size - pos;
		}
		//取子串
		string s1;
		s1.reserve(len);
		for (size_t i = 0;i < len;i++)
		{
			s1 += _str[pos + i];
		}
		return s1;
	}
	//全局比较函数的实现
	bool operator>(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) > 0;
	}
	bool operator<(const string& s1, const string& s2)
	{
		return !(s1 >= s2);
	}
	bool operator>=(const string& s1, const string& s2)
	{
		return s1 > s2 || s1 == s2;
	}
	bool operator<=(const string& s1, const string& s2)
	{
		return !(s1 > s2);
	}
	bool operator==(const string& s1, const string& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	bool operator!=(const string& s1, const string& s2)
	{
		return !(s1 == s2);
	}
	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch: s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& sin, string& s)
	{
		s.clear();//清理掉原有的内容
		//s.reserve(1024); //空间很容易开辟过大或者过小
		//开辟一个数组进行储存
		//这样不用提前开辟空间,同时以一个str的大小为基准,容易控制
		const size_t N = 256;
		char str[N];
		int i = 0;
		char ch;
		ch = sin.get();
		while (ch != '\0' && ch != '\n')
		{
			str[i++] = ch;
			//当数组满了,就将内容添加s当中
			if (i == N - 1)
			{
				str[i] = '\0';
				s += str;
				i = 0; //重新开始
			}
			ch = sin.get(); //读取下一个字符
		}
		//这种表示最后一次没有充满数组str,将里面的值直接拷贝出来即可
		if (i > 0)
		{
			str[i] = '\0';
			s += str;
		}
		return sin;
	}
}

源文件:test.cpp 功能的测试

cpp 复制代码
#include "String.h"

namespace stt
{
	//遍历,下标访问 
	void test01()
	{
		string s1;
		string s2("abcdefg");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
		for (int i = 0;i < s2.size();i++)
		{
			s2[i] += 2;
		}
		cout << s2.c_str() << endl;
		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl;

		string::iterator a = s2.begin();
		while (a != s2.end())
		{
			cout << *a << " ";
			a++;
		}
		cout << endl;
	}

	//插入和删除
	void test02()
	{
		string s2("asdfgh");
		s2 += '!';
		s2 += "&&&";
		cout << s2.c_str() << endl;
		s2.insert(0, 'G');
		cout << s2.c_str() << endl;
		s2.insert(3, "EEE");
		cout << s2.c_str() << endl;
		s2.insert(7, 'Q');
		cout << s2.c_str() << endl;
		s2.insert(0, "AAA");
		cout << s2.c_str() << endl;
		s2.erase(6);
		cout << s2.c_str() << endl;
		string s3("adcdeeegggg");
		s3.erase(6, 54);
		cout << s3.c_str() << endl;
		s2.erase(2, 2);
		cout << s2.c_str() << endl;
	}

	//下标查找和访问子串
	void test3()
	{
		string s1("app.cgg.www");
		string s2(s1);
		string s3 = s2;
		cout << s3.c_str() << endl;
		size_t pos = s3.find('.');
		string s4 = s3.substr(pos);
		cout << s4.c_str() << endl;
	}
	
	//比较和流插入,流提取
	void test4()
	{
		string s1("abccccccd");
		string s2("agccccc");
		cout << (s1 < s2) << endl;
		cout << (s1 == s2) << endl;
		cout << (s1 != s2) << endl;
		cout << (s1 >= s2) << endl;
		cout << ("agccccc" >= s2) << endl;
		cout << s1 << s2 << endl;

		string s3;
		cin >> s3;
		cout << s3 << endl;
	}
}
int main()
{
	//stt::test01();
	//stt::test02();
	//stt::test3();
	stt::test4();
	return 0;
}

_2,模拟实现补充

借助第三方的实现 :

深拷贝的另一种写法 :

赋值的另一种写法:

五,编码部分

计算机只能存储二进制010101,那么要如何表示各种符号的呢?各个国家的文字呢?

针对这个问题最开始发明的是ASCII码值

ASCII只能表示128个符号

之后针对各个国家的文字发明了万国码(Unicode)

Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发;

在中文中我们主要关注UTF-8这一可变长度字符编码

在1个字节时可以表示上面的ASCII码表中的各种符号

一般2~3个字节就可以表示绝大部分的文字了

两个字节就可以表示一些中文字符

变化展示:

六,Bug解析

相关推荐
想吃火锅10051 小时前
【leetcode】42.接雨水js
开发语言·javascript·ecmascript
竹叶青_七弦2 小时前
从复制粘贴到Harness:Java后端AI编程的工程化之路
java
wuminyu2 小时前
Linux内核线程调度与虚拟线程调度机制系统级深度剖析
java·linux·c语言·jvm·c++
小溪学编程2 小时前
Java InputStream 详解:从基础到实战
java·python·php
血小板要健康2 小时前
队列 + 宽搜(BFS):二叉树层序遍历 算法总结
java·数据结构·笔记·算法·leetcode·宽度优先
雪芽蓝域zzs2 小时前
第十六节:递归组件实现无限层级折叠侧边栏菜单
开发语言·javascript·ecmascript
A黄俊辉A2 小时前
【无标题】
java
BestHeaker3 小时前
跨企业接口对接:IQDS / CPK 数据解析与协作避坑指南(五)
java·服务器·前端
神明不懂浪漫3 小时前
【第二章】库、表、增删改查操作
开发语言·数据库·经验分享·笔记