当前位置: 首页 > 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…...

贪心算法(几种常规样例)

贪心算法&#xff08;几种常规样例&#xff09; 贪心算法&#xff0c;指在对问题进行求解的时候&#xff0c;总是做出当前看来是最好的选择。也就是说不从整体上最优上考虑&#xff0c;算法得到的结果是某种意义上的局部最优解 文章目录贪心算法&#xff08;几种常规样例&…...

【数据结构】基础知识总结

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了数据结构复习用的&#xff0c;由于牛客刷题发现数据结构方面和王道数据结构的题目非常像&#xff0c;甚至很多都是王道中的&#xff0c;所以将基础知识进行了整理&#xff0c;后续会将牛客刷题的错题一…...

宣布推出 .NET 社区工具包 8.1!

我们很高兴地宣布 .NET Community Toolkit 8.1 版正式发布&#xff01;这个新版本包括呼声很高的新功能、bug 修复和对 MVVM 工具包源代码生成器的大量性能改进&#xff0c;使开发人员在使用它们时的用户体验比以往更好&#xff01; 就像在我们之前的版本中一样&#xff0c;我…...

ChatGPT解开了我一直以来对自动化测试的疑惑

目录 前言 与ChatGPT的对话 什么是自动化测试,我该如何做到自动化测试,或者说需要借助什么工具可以做到自动化测试&#xff1f; 自动化测试如何确保数据的准确性 自动化测试是怎么去验证数据的 如何通过断言验证数据 自动化测试有哪些验证工具可以验证数据 总结 前言…...

十大经典排序算法(上)

目录 1.1冒泡排序 1. 算法步骤 3.什么时候最快 4. 什么时候最慢 5.代码实现 1.2选择排序 1. 算法步骤 2. 动图演示 3.代码实现 1.3 插入排序 1. 算法步骤 2. 动图演示 3. 算法实现 1.4 希尔排序 1. 算法步骤 2. 动图演示 3.代码实现 1.5 归并排序 1. 算法步骤 2…...

如何从 MySQL 读取 100w 数据进行处理

文章目录 场景常规查询流式查询MyBatis 流式查询接口非流式查询和流式查询区别游标查询场景 大数据量操作的场景大致如下: 1、 数据迁移; 2、 数据导出; 3、 批量处理数据; 在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理。…...

【数据降维-第2篇】核主成分分析(KPCA)快速理解,及MATLAB实现

一篇介绍了PCA算法的快速理解和应用&#xff0c;本章讲一下KPCA。KPCA方法与PCA方法一样&#xff0c;是有着扎实的理论基础的&#xff0c;相关理论在论文上以及网络上可以找到大量的材料&#xff0c;所以这篇文章还是聚焦在方法的快速理解以及应用上&#xff0c;此外还会对同学…...

Python+ChatGPT实战之进行游戏运营数据分析

文章目录一、数据二、目标三、解决方案1. DAU2. 用户等级分布3. 付费率4. 收入情况5. 付费用户的ARPU最近ChatGPT蛮火的&#xff0c;今天试着让ta写了一篇数据分析实战案例&#xff0c;大家来评价一下&#xff01;一、数据 您的团队已经为您提供了一些游戏数据&#xff0c;包括…...

Java每日一练(20230313)

目录 1. 字符串统计 ★ 2. 单词反转 ★★ 3. 俄罗斯套娃信封问题 ★★★ &#x1f31f; 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 Java 每日一练 专栏 1. 字符串统计 编写一个程序&#xff0c;对于输入的一段英语文本&#xff0c;可以统计&#…...

国内ChatGPT日趋成熟后,可以优先解决的几个日常小问题

现在ChatGPT的发展可谓如日中天&#xff0c;国内很多大的公司例如百度、京东等也开始拥抱新技术&#xff0c;推出自己的应用场景&#xff0c;但可以想象到的是&#xff0c;他们必定利用这个新技术在巩固自己的现有应用场景&#xff0c;比如某些客服&#xff0c;你都不用想&…...