当前位置: 首页 > news >正文

C++ STL:string类的模拟实现

目录

前置说明

一. 构造函数和析构函数的模拟实现

1.1 构造函数

1.2 析构函数

二. string类对象容量及成员相关的函数

2.1 获取字符串有效字符数、容量及_str成员变量获取相关函数

2.2 扩容及变长相关函数

2.3 字符串清空和判空函数

三. 运算符重载函数

3.1 赋值运算符重载函数

3.2 成员访问操作符[]重载函数

3.3 +=运算符重载函数 —— 尾插字符串或字符

四. 增删查改相关函数

4.1 插入字符串(字符)相关函数

4.2 子字符串删除函数

4.3 子串查找函数

五. 迭代器相关函数

六. 关于string的全局函数

6.1 字符串比较相关函数

6.2 字符串输出和输入相关函数

6.3 字符串与数值之间的相互转换

附录:string类模拟实现完整版


前置说明

模拟实现的string类定义了四个成员函数,分别为(1)char* str -- 指向字符串的指针、(2)size_t size -- 字符串中的字符数 、(3)size_t capacity -- 字符串最大可容纳的字符数量、(4)size_t _npos -- size_t类型的-1

一. 构造函数和析构函数的模拟实现

1.1 构造函数

这里实现两种形式的构造函数,一种是通过字符串来构造,第二种是通过一个string类进行拷贝构造。注意:定义拷贝构造函数要进行深拷贝而不是浅拷贝(值拷贝),否则,会使两个string类对象中的_str指向同一块内存区域,在调用析构函数时对同一块空间连续两次释放,从而导致程序崩溃。

图1.1 采用浅拷贝(左)和深拷贝(右)的内存指向情况示意图
        string(const char* str = "")   //通过字符串构造和默认构造: _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//explicit string(const string& str)  //拷贝构造//一般写法 -- 正常完成深拷贝//string(const string& str)  //拷贝构造//	: _size(str._size)//	, _capacity(str._capacity)//{//	_str = new char[_capacity + 1];//	strcpy(_str, str._str);//}//拷贝构造复用写法 -- 临时对象string(const string& str): _str(nullptr), _size(str._size), _capacity(str._capacity){if (this != &str)  //排除自己给自己拷贝的情况{string tmp(str._str);std::swap(_str, tmp._str);}}

1.2 析构函数

析构函数要完成的工作为:释放string类对象的成员函数_str指向的内存空间。

        ~string()  //析构函数{delete _str;_str = nullptr;_size = _capacity = 0;}

二. string类对象容量及成员相关的函数

2.1 获取字符串有效字符数、容量及_str成员变量获取相关函数

  • size函数和length函数:获取字符串长度(末尾'\0'不包含在内)。
  • capacity函数:获取当前string类对象的容量。
  • c_str函数:获取string对象的成员变量_str。
        size_t size()  const //有效字符个数{return _size;}size_t length() const{return _size;}size_t capacity()  //获取容量{return _capacity;}const char* c_str() const  //获取字符串类对象变量_str{return _str;}

2.2 扩容及变长相关函数

这里主要涉及两个函数:

  • reserve:将string类对象的字符容量扩大到n,如果给定的n小于原来的_capacity,则容量不发生改变。
  • resize:将字符串的长度变为n,并将新增的空间的内容初始化为ch。

reserve函数进行的工作:

  1. 检查n是否大于_capacity,如果否,函数不进行任何工作。
  2. 如果n大于_capacity,那么开辟一块能容纳n+1个字符的内存空间,将类对象原来的字符串复制到新开辟的这块空间。
  3. 释放原来的字符串内存空间,_str指向新开辟的这块内存空间。
  4. 更新容量_capacity。

resize函数进行的工作:

  1. 如果n小于或等于_size,则将下标为n位置处的元素改为'\0'。
  2. 如果n大于_size且小于等于_capacity,那么则将下标从_size开始到n-1的元素初始化为ch,然后末尾补'\0'。
  3. 如果n大于_capacity,那么则要先扩容到n,然后将下标位置>=_size的元素全部初始化为ch,然后末尾补'\0'。
        void reserve(size_t n)  //容量指定函数{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;std::swap(_str, tmp);_capacity = n;}}void resize(size_t n, char ch = '\0')  //字符串长度变长或缩短函数{if (n <= _size)  //容量变更后比原有效字符数小{_size = n;_str[n] = '\0';}else  //容量变更后比原有效字符数大{if (n > _capacity)  //容量不足,扩容{reserve(n);}//将_size开始向后的内容全部变为chmemset(_str + _size, ch, n - _size);_size = n;_str[n] = '\0';}}

2.3 字符串清空和判空函数

  • clear:将字符串中的有效字符数清零。
  • empty:判断当前字符串是否为空。
        void clear()  //清空字符串{_str[0] = '\0';_size = 0;}bool empty()  //判断当前字符串是否为空{return _size == 0;}

三. 运算符重载函数

3.1 赋值运算符重载函数

赋值运算符重载函数完成的功能是将一个string类对象的值赋给另一个类对象,要完成string对象间的赋值有两种方法:

  1. 一般方法:规规矩矩进行深拷贝
  2. 通过临时对象赋值:创建一个与赋值对象的字符串内容相同的临时类对象,交换被赋值对象和临时对象的_str成员值,这样在临时对象生命周期结束自动调用析构函数时,会释放被赋值对象_str原来指向的空间。
		//一般写法 -- 正常进行深拷贝string& operator=(const string& str)  //赋值运算符重载{//开辟一块新的内存空间用于存储被复制的字符串char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);//释放原内存空间,同时更新字符串内容delete[] _str;_str = tmp;//更新数据量和容量_size = str._size;_capacity = str._capacity;return *this;}//复用写法 -- 创建临时对象,调用构造函数string& operator=(const string& str){if (this != &str)  //排除自赋值情况{string tmp(str);  //创建临时对象swap(_str, tmp._str);  //交换指针指向_size = str._size;_capacity = str._capacity;}return *this;}

3.2 成员访问操作符[]重载函数

函数功能为获取下标位置为pos处的字符串,如果pos越界,则断言报错。

		char& operator[](size_t pos)  //下标访问操作符{assert(pos < _size);return this->_str[pos];}const char& operator[](size_t pos) const  //针对const对象的下标访问操作符{assert(pos < _size);return this->_str[pos];}

3.3 +=运算符重载函数 —— 尾插字符串或字符

函数所进行的工作包括:

  1. 检查当前对象容量是否充足,如果容量不足,就扩容。
  2. 将字符或字符串尾插到原字符串的后面。
		string& operator+=(const char* str)  //重载+=操作符:字符串尾插{size_t len = strlen(str);   //尾插字符串长度if (_size + len > _capacity){//如果空间不足就扩容reserve(_size + len);}strcpy(_str + _size, str);_size += len;return *this;}string& operator+=(const string& str){(*this) += str._str;  //获取str的成员_str,复用字符串尾插return *this;}string& operator+=(char ch)  //重载+=操作符:尾插字符{if (_size + 1 > _capacity)  //容量不足就开空间{reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';return *this;}

四. 增删查改相关函数

4.1 插入字符串(字符)相关函数

  • push_back:尾插字符函数。
  • append:尾插字符串函数。
  • insert:在pos位置插入字符串函数。

push_back函数和append函数与+=完成一样的操作,直接复用+=即可。insert函数则需要将从pos位置开始往后的元素向后移动len个单位,len为插入字符串的长度,然后在pos位置插入新字符串。

图4.1 insert函数实现的功能图解
		void push_back(char ch)  //尾插字符{(*this) += ch;}void append(const char* str)  //尾插字符串{(*this) += str;}size_t find(char ch, size_t pos = 0)  //查找一个字符{assert(pos < _size);for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;  //找到ch就返回下标}}//找不到就返回nposreturn _npos;}string& insert(size_t pos, const char* str)  //特定位置插入字符串{assert(pos <= _size);size_t len = strlen(str);  //获取插入字符串长度if (_size + len > _capacity)  {//容量不足时扩容reserve(_size + len);}size_t end = _size + len;//按从后往前的顺序,让pos位置开始往后的字符后移len个单位while (end >= pos + len){_str[end] = _str[end - len];--end;}memcpy(_str + pos, str, len);  //pos位置插入字符串_size += len;  //更新有效字符数return *this;}string& insert(size_t pos, const string& str){assert(pos <= _size);return insert(pos, str._str);}

4.2 子字符串删除函数

erase函数:从pos位置开始删除len个函数。

  1. 检查pos是否越界,如果越界,则断言报错。
  2. 将从下标位置pos+len开始的元素向前移动len个单位(包含末尾的'\0'),如果len>_size-pos成立,那么说明要将pos往后的元素全部删除,只需将pos位置处的元素改为'\0'即可。
  3. 更新_size的值。
		string& insert(size_t pos, const char* str)  //特定位置插入字符串{assert(pos <= _size);size_t len = strlen(str);  //获取插入字符串长度if (_size + len > _capacity)  {//容量不足时扩容reserve(_size + len);}size_t end = _size + len;//按从后往前的顺序,让pos位置开始往后的字符后移len个单位while (end >= pos + len){_str[end] = _str[end - len];--end;}memcpy(_str + pos, str, len);  //pos位置插入字符串_size += len;  //更新有效字符数return *this;}string& insert(size_t pos, const string& str){assert(pos <= _size);return insert(pos, str._str);}

4.3 子串查找函数

find函数:从pos位置处开始查找子串,如果找到,就返回子串第一个元素的下标,如果找不到,就返回_npos。

		size_t find(const char* str, size_t pos = 0)  //查找一个字符串{assert(pos < _size);char* ptr = strstr(_str + pos, str);  //获取子字符串首字符地址if (ptr != nullptr){return ptr - _str;}else{return _npos;}}size_t find(const string& str, size_t pos = 0)  //在string类对象中查找子字符串{assert(pos < _size);return find(str._str, pos);  //复用}

五. 迭代器相关函数

普通对象迭代器和const属性对象迭代器本质上都为char*/const char*:

  • typedef char* iteratior
  • typedef const char* const_iterator
  • begin:获取首字符元素地址。
  • end:获取字符串末尾'\0'的地址。
		typedef char* iterator;typedef const char* const_iterator;iterator begin()  //头指针{return _str;}iterator end()  //尾指针{return _str + _size;}const_iterator begin() const  //const对象头指针{return _str;}const_iterator end() const  //const对象尾指针{return _str + _size;}

六. 关于string的全局函数

6.1 字符串比较相关函数

关于比较,总共有六种形式:>、==、>=、<、<=、!=,我们只需要模拟实现其中的两个,然后其余4个复用已经模拟实现完成的两个即可。

	bool operator<(const string& s1, const string& s2)  //比较s1是否小于s2{size_t i1 = 0, i2 = 0;size_t len1 = s1.size();  //字符串s1长度size_t len2 = s2.size();  //字符串s2长度while (i1 < len1 && i2 < len2){if (s1[i1] < s2[i2]){return true;}else if (s1[i1] == s2[i2]){++i1;++i2;}else{return false;}}//如果s1长度小于s2,就成立,否则不成立return i2 < len2;}bool operator==(const string& s1, const string& s2)  //两字符串是否相等{size_t i1 = 0, i2 = 0;while (i1 < s1.size() && i2 < s2.size()){if (s1[i1] != s2[i2]){return false;}else{++i1;++i2;}}return i1 == s1.size() && i2 == s2.size();}bool operator<=(const string& s1, const string& s2){return (s1 == s2) || (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 !(s1 == s2);}

6.2 字符串输出和输入相关函数

  • ostream& operator<<(ostream& out, const string& s) -- 流插入操作符重载函数
  • istream& operator>>(istream& in, string& s) -- 流输出操作符重载函数

注意重载流插入操作符时,不能使用out << s.c_str,因为这样遇到'\0'就会截止输出,从而不一定输出s.size()个字符。

	ostream& operator<<(ostream& out, const string& s){//写法1:范围for循环/*for (auto ch : s)   {out << ch;}*///写法2:普通for循环for (size_t i = 0; i < s.size(); ++i){out << s[i];}//out << s.c_str();  //不能这么写,因为此时遇到\0停止读取,而不是读s.size()个字符return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}

6.3 字符串与数值之间的相互转换

  • stoi:字符串转换为int型数据。
  • stod:字符串转换为double型数据。
	int stoi(const string& s){size_t len = s.size();  //字符串长度long long flag = 1;  //正负号标识符size_t i = 0;long long retVal = 0;//排除空格while (i < len && s[i] == ' '){++i;}//检查正负号if (i < len && s[i] == '+'){++i;}else if (i < len && s[i] == '-'){++i;flag = -flag;}//排除0while (i < len && s[i] == '0'){++i;}//计算结果while (i < len && (s[i] <= '9' && s[i] >= '0')){retVal = retVal * 10 + (s[i] - '0') * flag;//检查是否越界if (retVal >= INT_MAX){return INT_MAX;}else if (retVal <= INT_MIN){return INT_MIN;}++i;}return (int)retVal;}double stod(const string& s){size_t i = 0;size_t len = s.size();  //字符串长度//最终返回值 = 整数部分 + 小数部分double integerRet = 0;  //返回值的整数部分double decimalRet = 0;  //返回值的小数部分int flag = 1;   //正负号标识符while (i < len && s[i] == ' ')  //排除空格{++i;}if (i < len && s[i] == '+')  //确定返回值是正数还是负数{++i;}else if (i < len && s[i] == '-'){flag = -flag;++i;}//计算整数部分while (i < len && (s[i] >= '0' && s[i] <= '9')){integerRet = integerRet * 10 + (s[i] - '0') * flag;++i;}//计算小数部分if (i < len && s[i] == '.'){double factorial = 0.1;  //系数++i;  //跳过小数点while (i < len && (s[i] >= '0' && s[i] <= '9')){decimalRet += (s[i] - '0') * factorial * flag;factorial *= 0.1;++i;}}return integerRet + decimalRet;}

附录:string类模拟实现完整版

#include<iostream>
#include<assert.h>
#include<string.h>
#include<stdbool.h>
#include<limits>using namespace std;namespace zhang
{class string{public://1.创建和销毁相关string(const char* str = "")   //通过字符串构造和默认构造: _size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}//explicit string(const string& str)  //拷贝构造//一般写法 -- 正常完成深拷贝//string(const string& str)  //拷贝构造//	: _size(str._size)//	, _capacity(_size)//{//	_str = new char[_capacity + 1];//	strcpy(_str, str._str);//}//拷贝构造复用写法 -- 临时对象string(const string& str): _str(nullptr), _size(str._size), _capacity(str._capacity){if (this != &str)  //排除自己给自己拷贝的情况{string tmp(str._str);std::swap(_str, tmp._str);}}~string()  //析构函数{delete _str;_str = nullptr;_size = _capacity = 0;}//2.类对象成员和容量相关size_t size()  const //有效字符个数{return _size;}size_t length() const{return _size;}size_t capacity()  //获取容量{return _capacity;}const char* c_str() const  //获取字符串类对象变量_str{return _str;}void reserve(size_t n)  //容量指定函数{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;std::swap(_str, tmp);_capacity = n;}}void resize(size_t n, char ch = '\0')  //容量变更函数{if (n <= _size)  //容量变更后比原有效字符数小{_size = n;_str[n] = '\0';}else  //容量变更后比原有效字符数大{if (n > _capacity)  //容量不足,扩容{reserve(n);}//将_size开始向后的内容全部变为chmemset(_str + _size, ch, n - _size);_size = n;_str[n] = '\0';}}void clear()  //清空字符串{_str[0] = '\0';_size = 0;}bool empty()  //判断当前字符串是否为空{return _size == 0;}//3.运算符重载相关//一般写法 -- 正常进行深拷贝//string& operator=(const string& str)  //赋值运算符重载//{//	//开辟一块新的内存空间用于存储被复制的字符串//	char* tmp = new char[str._capacity + 1];//	strcpy(tmp, str._str);//	//释放原内存空间,同时更新字符串内容//	delete[] _str;//	_str = tmp;//	//更新数据量和容量//	_size = str._size;//	_capacity = str._capacity;//	return *this;//}//复用写法 -- 调用构造函数string& operator=(const string& str){if (this != &str)  //排除自赋值情况{string tmp(str);  //创建临时对象swap(_str, tmp._str);  //交换指针指向_size = str._size;_capacity = str._capacity;}return *this;}char& operator[](size_t pos)  //下标访问操作符{assert(pos < _size);return this->_str[pos];}const char& operator[](size_t pos) const  //针对const对象的下标访问操作符{assert(pos < _size);return this->_str[pos];}string& operator+=(const char* str)  //重载+=操作符:字符串尾插{size_t len = strlen(str);   //尾插字符串长度if (_size + len > _capacity){//如果空间不足就扩容reserve(_size + len);_capacity = _size + len;}strcpy(_str + _size, str);_size += len;return *this;}string& operator+=(const string& str){(*this) += str._str;return *this;}string& operator+=(char ch)  //重载+=操作符:尾插字符{if (_size + 1 > _capacity)  //容量不足就开空间{reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = ch;_str[_size] = '\0';return *this;}//4.迭代器typedef char* iterator;typedef const char* const_iterator;iterator begin()  //头指针{return _str;}iterator end()  //尾指针{return _str + _size;}const_iterator begin() const  //const对象头指针{return _str;}const_iterator end() const  //const对象尾指针{return _str + _size;}//5.增删查改void push_back(char ch)  //尾插字符{(*this) += ch;}void append(const char* str)  //尾插字符串{(*this) += str;}size_t find(char ch, size_t pos = 0)  //查找一个字符{assert(pos < _size);for (size_t i = pos; i < _size; ++i){if (_str[i] == ch){return i;  //找到ch就返回下标}}//找不到就返回nposreturn _npos;}size_t find(const char* str, size_t pos = 0)  //查找一个字符串{assert(pos < _size);char* ptr = strstr(_str + pos, str);  //获取子字符串首字符地址if (ptr != nullptr){return ptr - _str;}else{return _npos;}}size_t find(const string& str, size_t pos = 0)  //在string类对象中查找子字符串{assert(pos < _size);return find(str._str, pos);  //复用}string& insert(size_t pos, const char* str)  //特定位置插入字符串{assert(pos <= _size);size_t len = strlen(str);  //获取插入字符串长度if (_size + len > _capacity)  {//容量不足时扩容reserve(_size + len);}size_t end = _size + len;//按从后往前的顺序,让pos位置开始往后的字符后移len个单位while (end >= pos + len){_str[end] = _str[end - len];--end;}memcpy(_str + pos, str, len);  //pos位置插入字符串_size += len;  //更新有效字符数return *this;}string& insert(size_t pos, const string& str){assert(pos <= _size);return insert(pos, str._str);}string& erase(size_t pos = 0, size_t len = _npos)  //子串删除函数{assert(pos < _size);if (pos + len > _size)  //pos后面的所有字符都会被删除{_str[pos] = '\0';_size = pos;}else  //pos向后的字符没有被删完{for (size_t i = pos; i <= _size; ++i){_str[i] = _str[i + len];}_size -= len;}return *this;}private:char* _str;  //指向存储字符串内存的指针size_t _size;  //有效字符数size_t _capacity;  //当前类对象容量(最多几个有效字符)static size_t _npos;};size_t string::_npos = -1;bool operator<(const string& s1, const string& s2)  //比较s1是否小于s2{size_t i1 = 0, i2 = 0;size_t len1 = s1.size();  //字符串s1长度size_t len2 = s2.size();  //字符串s2长度while (i1 < len1 && i2 < len2){if (s1[i1] < s2[i2]){return true;}else if (s1[i1] == s2[i2]){++i1;++i2;}else{return false;}}//如果s1长度小于s2,就成立,否则不成立return i2 < len2;}bool operator==(const string& s1, const string& s2)  //两字符串是否相等{size_t i1 = 0, i2 = 0;while (i1 < s1.size() && i2 < s2.size()){if (s1[i1] != s2[i2]){return false;}else{++i1;++i2;}}return i1 == s1.size() && i2 == s2.size();}bool operator<=(const string& s1, const string& s2){return (s1 == s2) || (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 !(s1 == s2);}ostream& operator<<(ostream& out, const string& s){//写法1:范围for循环/*for (auto ch : s)   {out << ch;}*///写法2:普通for循环for (size_t i = 0; i < s.size(); ++i){out << s[i];}//out << s.c_str();  //不能这么写,因为此时遇到\0停止读取,而不是读s.size()个字符return out;}istream& operator>>(istream& in, string& s){s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}int stoi(const string& s){size_t len = s.size();  //字符串长度long long flag = 1;  //正负号标识符size_t i = 0;long long retVal = 0;//排除空格while (i < len && s[i] == ' '){++i;}//检查正负号if (i < len && s[i] == '+'){++i;}else if (i < len && s[i] == '-'){++i;flag = -flag;}//排除0while (i < len && s[i] == '0'){++i;}//计算结果while (i < len && (s[i] <= '9' && s[i] >= '0')){retVal = retVal * 10 + (s[i] - '0') * flag;//检查是否越界if (retVal >= INT_MAX){return INT_MAX;}else if (retVal <= INT_MIN){return INT_MIN;}++i;}return (int)retVal;}double stod(const string& s){size_t i = 0;size_t len = s.size();  //字符串长度//最终返回值 = 整数部分 + 小数部分double integerRet = 0;  //返回值的整数部分double decimalRet = 0;  //返回值的小数部分int flag = 1;   //正负号标识符while (i < len && s[i] == ' ')  //排除空格{++i;}if (i < len && s[i] == '+')  //确定返回值是正数还是负数{++i;}else if (i < len && s[i] == '-'){flag = -flag;++i;}//计算整数部分while (i < len && (s[i] >= '0' && s[i] <= '9')){integerRet = integerRet * 10 + (s[i] - '0') * flag;++i;}//计算小数部分if (i < len && s[i] == '.'){double factorial = 0.1;  //系数++i;  //跳过小数点while (i < len && (s[i] >= '0' && s[i] <= '9')){decimalRet += (s[i] - '0') * factorial * flag;factorial *= 0.1;++i;}}return integerRet + decimalRet;}
};

相关文章:

C++ STL:string类的模拟实现

目录 前置说明 一. 构造函数和析构函数的模拟实现 1.1 构造函数 1.2 析构函数 二. string类对象容量及成员相关的函数 2.1 获取字符串有效字符数、容量及_str成员变量获取相关函数 2.2 扩容及变长相关函数 2.3 字符串清空和判空函数 三. 运算符重载函数 3.1 赋值运算…...

并发编程---线程池(六)

阻塞队列的应⽤——线程池一 线程池基本概念二 线程池三种常⽤创建⽅式2.1.newFixedThreadPool线程池&#xff1a;2.2.newSingleThreadExecutor线程池&#xff1a;2.3.newCachedThreadPool线程池&#xff1a;2.4. 线程池代码演示三 线程池创建的七个参数四 线程池底层原理理解&…...

【Java实战】不会还有人用if else进行参数校验吧

当请求参数很多&#xff0c;几乎每一个参数都需要后端去兜底校验时&#xff0c;你还在写if else去判断参数是否为空吗&#xff1f;&#xff1f;要校验为空的参数三四个还好&#xff0c;要是十几个&#xff0c;业务逻辑还没开始就写二三十行代码开始堆山了嘛&#xff0c;教给大家…...

深度学习部署(十六): CUDA RunTime API _vector-add 使用cuda核函数实现向量加法

1. 知识点 nthreads的取值&#xff0c;不能大于block能取值的最大值。一般可以直接给512、256&#xff0c;性能就是比较不错的 (input_size block_size - 1) / block_size;是向上取整 对于一维数组时&#xff0c;采用只定义layout的x维度&#xff0c;若处理的是二维&#xff…...

堆结构的两个应用

堆排序 堆结构很大的一个用处&#xff0c;就是用于堆排序了&#xff0c;堆排序的时间复杂度是O(n∗log2n)O(n*log_2n)O(n∗log2​n)量级的&#xff0c;在众多排序算法中所处的地位也是高手级别的了。 但很多人在使用堆排序的时候&#xff0c;首先认为我必须得有一个堆数据结构…...

Java中的 static

1 static 静态变量 1.1 静态变量的使用 static变量也称作静态变量&#xff0c;也叫做类变量 静态变量被所有的对象所共享&#xff0c;在内存中只有一个副本 当且仅当在类初次加载时会被初始化 静态变量属于类 通过类名就可以直接调用静态变量 也可以通过对象名.静态变量…...

基于Vision Transformer的图像去雾算法研究与实现(附源码)

基于Vision Transformer的图像去雾算法研究与实现 0. 服务器性能简单监控 \LOG_USE_CPU_MEMORY\文件夹下的use_memory.py文件可以实时输出CPU使用率以及内存使用率&#xff0c;配合nvidia-smi监控GPU使用率 可以了解服务器性能是否足够&#xff1b;运行时在哪一步使用率突然…...

服务器相关常用的命令

cshell语法 https://www.doc88.com/p-4985161471426.html domainname命令 1&#xff09;查看当前系统域名 domainname2&#xff09;设置并查看当前系统域名 domainname example.com3&#xff09;显示主机ip地址 domainname -Iwhich命令 which 系统命令在 PATH 变量指定的…...

今天是国际数学日,既是爱因斯坦的生日又是霍金的忌日

目录 一、库函数计算 π 二、近似值计算 π 三、无穷级数计算 π 四、割圆术计算 π 五、蒙特卡罗法计算 π 六、计算800位精确值 从2020年开始&#xff0c;每年的3月14日又被定​为国际数学日​&#xff0c;是2019年11月26日​联合国教科文组织​第四十届大会上正式宣布…...

Qt Quick - StackLayout 堆布局

StackLayout 堆布局一、概述二、attached 属性三、例子1. 按钮切换 StackLayout 页面一、概述 StackLayout 其实就是说&#xff0c;在同一个时刻里面&#xff0c;只有一个页面是展示出来的&#xff0c;类似QStackWidget 的功能&#xff0c;主要就是切换界面的功能。这个类型我…...

C/C++网络编程笔记Socket

https://www.bilibili.com/video/BV11Z4y157RY/?vd_sourced0030c72c95e04a14c5614c1c0e6159b上面链接是B站的博主教程&#xff0c;源代码来自上面视频&#xff0c;侵删&#xff0c;这里只是做笔记&#xff0c;以供复习和分享。上一篇博客我记录了配置环境并且跑通了&#xff0…...

RK3568平台开发系列讲解(网络篇)什么是Socket套接字

🚀返回专栏总目录 文章目录 一、什么是socket ?二、socket 理解为电话机三、socket 的发展历史四、套接字地址格式4.1、通用套接字地址格式4.2、IPv4 套接字格式地址4.3、IPv6 套接字地址格式4.4、几种套接字地址格式比较沉淀、分享、成长,让自己和他人都能有所收获!😄 …...

2022年全国职业院校技能大赛(中职组)网络安全竞赛试题——渗透测试解析(详细)

渗透测试 任务环境说明: 服务器场景:Server9服务器场景操作系统:未知(关闭连接)系统用户名:administrator密码:123456通过本地PC中渗透测试平台Kali对靶机场景进行系统服务及版本扫描渗透测试,以xml格式向指定文件输出信息(使用工具Nmap),将以xml格式向指定文件输出…...

尚融宝03-mybatis-plus基本CRUD和常用注解

目录 一、通用Mapper 1、Create 2、Retrieve 3、Update 4、Delete 二、通用Service 1、创建Service接口 2、创建Service实现类 3、创建测试类 4、测试记录数 5、测试批量插入 三、自定义Mapper 1、接口方法定义 2、创建xml文件 3、测试条件查询 四、自定义Serv…...

vue多行显示文字展开

这几天项目里面有一个需求&#xff0c;多行需要进行展开文字&#xff0c;类似实现这种效果 难点就在于页面布局 一开始就跟无头苍蝇似的&#xff0c;到处百度 &#xff0c;后面发现网上的都不适合自己&#xff0c;最终想到了解决方案 下面是思路&#xff1a; 需求是超过3行&a…...

SpringBoot:SpringBoot 的底层运行原理解析

声明原文出处&#xff1a;狂神说 文章目录1. pom.xml1 . 父依赖2 . 启动器 spring-boot-starter2. 主启动类的注解1. 默认的主启动类2. SpringBootApplication3. ComponentScan4. SpringBootConfiguration5. SpringBootApplication 注解6. spring.factories7. 结论8. 简单图解3…...

哪些场景会产生OOM?怎么解决?

文章目录 堆内存溢出方法区(运行时常量池)和元空间溢出直接内存溢出栈内存溢出什么时候会抛出OutOfMemery异常呢?初看好像挺简单的,其实深究起来考察的是对整个JVM的了解,而这个问题从网上可以翻到一些乱七八糟的答案,其实在总结下来基本上4个场景可以概括下来。 堆内存溢出…...

金三银四、金九银十 面试宝典 Spring、MyBatis、SpringMVC面试题 超级无敌全的面试题汇总(超万字的面试题,让你的SSM框架无可挑剔)

Spring、MyBatis、SpringMVC 框架 - 面试宝典 又到了 金三银四、金九银十 的时候了&#xff0c;是时候收藏一波面试题了&#xff0c;面试题可以不学&#xff0c;但不能没有&#xff01;&#x1f941;&#x1f941;&#x1f941; 一个合格的 计算机打工人 &#xff0c;收藏夹里…...

JAVA开发(Spring框架详解)

javaweb项目几乎已经离不开spring框架了&#xff0c;spring 是一个典型的分层架构框架&#xff0c;它包含一系列的功能并被分为多个功能模块&#xff0c;springboot对spring框架又做了一层封装&#xff0c;以至于很多人对原来的spring框架越来越不了解。 要谈Spring的历史&…...

自学大数据第八天~HDFS命令(二)

嗨喽,好久不见,最近抽空复习了一下hadoop,书读百遍,其意自现这句话还真是; 继续学习HDFS常用命令 改变文件 拥有者~chown hdfs dfs -chown -R hadoop /user/hadoop使用 -R 将使改变在目录结构下递归进行。命令的使用者必须是超级用户。 改变文件所属组-chgrp hdfs dfs -chgr…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...