string类的模拟实现

文章目录

  • string类的模拟实现
    • string基本框架的实现
    • operator+=的实现
    • string常用函数的实现


string类的模拟实现

前文对于string的常用函数做了讲解,由于string是一个面试官常考的点,总喜欢让模拟实现string类,下面来模拟实现一下string,赋予基本的功能,且逐步完善函数实现方式。

string基本框架的实现

string类的基本框架,比如构造函数,拷贝构造,析构函数,成员变量,起码的push_back等一些能正常使得string运行的函数的实现。

namespace String {class string {public://迭代器: string中的迭代器实际上就是指针typedef char* iterator;typedef const char* const_iterator;iterator begin(){//begin 表示的是string的首元素地址return _str;}iterator end(){//end 返回string最后一个元素的下一个位置,也就是'\0'return _str + _size;}const_iterator begin() const{//begin 表示的是string的首元素地址return _str;}const_iterator end() const{//end 返回string最后一个元素的下一个位置,也就是'\0'return _str + _size;}//默认构造和带参构造合并,使用缺省参数string(const char* str = "")  //""字符串自带'\0':_str(new char[strlen(str) + 1]), _size(strlen(str)), _capacity(strlen(str)){//存储字符串strcpy(_str, str);}//拷贝构造//string(const string& s)//{//	//深拷贝,就是创建一个大小一样的空间//	_str = new char[s._capacity + 1];//	strcpy(_str, s._str);//	_size = s._size;//	_capacity = s._capacity;//}string(const string& s){_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}void Print() {cout << _str << "\t" << _size << "\t" << _capacity << endl;}//reserve  保留容量 可以扩容void reserve(size_t n)  //只是改变capacity 不改变size{if (n > _capacity){//新建一个字符数组cout << "reserve->" << n << endl;char* new_str = new char[n + 1];//更改容量//strcpy(new_str, _str);memcpy(new_str, _str, _size + 1);delete[] _str;_str = new_str;_capacity = n;}}//push_back	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){if (_size + strlen(str) > _capacity){reserve(_size + strlen(str));//至少保留_size + strlen(str)}//加入字符串memcpy(_str + _size, str, strlen(str) + 1);//在'\0'位置(就是_str末尾)+str_size += strlen(str);}//实现+=  也是使用push_back 和append函数string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}//返回sizesize_t size() const  //const表示修饰this指针,也就是说只读,如果是const对象,也可以访问,普通用户相当于权限的缩小也可也访问{return _size;}size_t capacity(){return _capacity;}private:char* _str;int _size;int _capacity;};
}

构造函数和拷贝构造

默认构造函数和带参构造函数合并,使用缺省参数

拷贝构造函数,我们要使用深拷贝,因为如果是浅拷贝,仅仅是将数值传给新的string对象,但是两者对应一个地址一个空间,当析构一个string对象后,另一个对象再次析构就会报错。

string(const char* str = "")  //""字符串自带'\0':_str(new char[strlen(str) + 1])   //因为我们底层用的数组,所以一定要多开一位空间存放'\0', _size(strlen(str)), _capacity(strlen(str))
{//存储字符串strcpy(_str, str);  //传入字符串的时候,一般都是结尾为'\0',中间有'\0'的都是我们为string对象增加的。所以这个地方还是使用strcpy即可
}//拷贝构造
string(const string& s)
{//深拷贝,就是创建一个大小一样的空间_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

push_back和append的实现

想要实现push_back和append函数,都要在底层中考虑是否需要扩容,那么我们就顺势要写出reserve函数,让其来判断是否需要扩容。

//reserve  保留容量 可以扩容
void reserve(size_t n)  //只是改变capacity 不改变size
{if (n > _capacity){//新建一个字符数组cout << "reserve->" << n << endl;char* new_str = new char[n + 1];//更改容量//strcpy(new_str, _str);memcpy(new_str, _str, _size + 1);delete[] _str;_str = new_str;_capacity = n;}
}		
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)
{if (_size + strlen(str) > _capacity){reserve(_size + strlen(str));//至少保留_size + strlen(str)}//加入字符串memcpy(_str + _size, str, strlen(str) + 1);//在'\0'位置(就是_str末尾)+str_size += strlen(str);
}

在这里插入图片描述

注意:为什么拷贝字符串内容的时候用memcpy而不是strcpy,这是因为,string中可能中间会有'\0',memcpy是根据第三个参数来定要拷贝的字符长度,而strcmpy,是根据要拷贝的字符串的'\0'出现的位置,所以使用memcpy更加合适。

strcpy和memcpy的对比

  • char * strcpy ( char * destination, const char * source );
  • void * memcpy ( void * destination, const void * source, size_t num );

operator+=的实现

我们实现了push_back和append之后就可以直接复用这两个函数,实现operator+=

string& operator+=(char ch)
{push_back(ch);  //插入一个字符的时候return *this;
}
string& operator+=(const char* str)
{append(str); //插入字符串的时候return *this;
}//因为string对象在该函数之后不会释放空间,所以传引用返回,提高效率

string常用函数的实现

下面主要是对于insert、find、erase、substr、resize、一系列重载运算符等的实现

insert的实现

我们主要实现两种insert函数 1.在pos位置上插入n个字符c 2.在pos位置上插入字符串str

string& insert(size_t pos, size_t n, char c)
{//1.先判定pos是否正确assert(pos <= _size);//2.扩容reserve(_size + n);//3.在pos位置上开始挪动n个字符size_t end = _size;//因为如果pos为0的时候,无符号整型0减去1,end >= pos比较 为一个巨大值,使得该循环无法停止//npos是static变量,定义为-1;while (end >= pos && end != npos){_str[end + n] = _str[end];--end;}//4.添加n个字符for (int i = 0; i < n; i++){_str[pos + i] = c;}_size += n;return *this;
}
string& insert(size_t pos, const char*str)
{//1.pos的判定assert(pos <= _size);//2.扩容int len = strlen(str);reserve(_size + len);//3.在pos位置上移动len个字符size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}//4.将str字符串的字符依次输入for (int i = 0; i < len; i++){_str[pos + i] = str[i];}//5.最后_size增加_size += len;return *this;
}

在这里插入图片描述

注意:npos的使用,是为了防止size_t无符号整型在于整型pos比较时候的强制转换,整型提升,得到一个巨大值,造成无限循环。

erase的实现

主要就是判断len是否等于npos,或者pos+len>=_size,分两种情况,是否从pos删除完,实际上就是在pos位置上加上'\0'即可,反之就是间隔len个距离向前移动字符,直到pos+len<=_size,最后_size-=len

string& erase(size_t pos = 0, size_t len = npos)
{assert(pos <= _size);if (len == npos || pos + len >= _size){//表示从pos位置删完_str[pos] = '\0';_size = pos;}else {//从pos位置删除len个字符//向前挪动size_t end = pos;while (end+len <= _size)  {_str[end] = _str[end + len];end++;}//此时end==_size//_str[end] = '\0';_size -= len;}return *this;
}

在这里插入图片描述

find的实现

find的实现,就是遍历找到符合条件的下标,并返回

size_t find(char ch, size_t pos = 0) const
{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;  //没有找到返回-1;
}
size_t find(const char* s, size_t pos = 0) const
{//使用strstrassert(pos < _size);const char* str = strstr(_str+pos, s);  //使用str函数,进行比较是否有对应的字符串if (str){return str-_str;//两个指针相减,得到的是地址的偏移量}else {return npos;}
}

substr的实现

substr的实现,就是判断要解决的n的数值,然后新建一个string字符串,将从pos位置开始的n个字符依次添加到这个新字符串中,最后返回新字符串

//substr的实现
string substr(size_t pos = 0, size_t len = npos)
{assert(pos < _size);size_t n = len;//如果缺省len=npos 或者是截取的范围大于_sizeif (len == npos || pos + len >= _size){n = _size - pos;  }//创建一个新的字符数组string new_str;new_str.reserve(n);for (size_t i = pos; i < n + pos; i++){new_str += _str[i];}return new_str;
}

在这里插入图片描述

resize的实现

resize底层是有reserve的,即需要判断是否需要扩容,满足_size<=_capacity

//实现resize
void resize(size_t n, char ch = '\0')
{//两种情况,1.n<_size 直接赋值'\0' 2.判断是否扩容 if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);//让reserve来判断是否是需要扩容for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}
}

opeartor重载运算符

重载运算符,只要实现一两个就能实现其他,下面我们实现的是operator< 和operator== 然后通过调用这两个函数,来实现其他operator

		//字符串比较按照ascii比较//bool operator<(const string& s)//{//	int num=memcmp(_str, s._str, _size > s._size?s._size : _size);//	//如果在最小长度下,前面数值小于后者 num返回的是负数//	//	return num == 0 ? _size < s._size : num < 0;//	//如果如果num为0,说明等于,且前者长度小于后者,返回真值,反之返回//}
bool operator<(const string& s)
{size_t i1 = 0;size_t i2 = 0;int num = _size > s._size ? s._size : _size;  //得到两者最小的长度while (i1 < num && i2 < num){if (_str[i1] < s._str[i2])		//只要不相等就返回{return true;}else if(_str[i1] > s._str[i2]) {return false;}else{++i1;		//该字符相当,那么继续++++i2;}}return _size < s._size;  //现在退出循环,说明前num个字符都相等,如果此时_size<s._size 那么返回真,反之返回假
}
bool operator==(const string& s)
{return _size == s._size && memcmp(_str, s._str, _size > s._size ? s._size : _size) == 0;  //两者字符长度相等,且通过memcpy返回值是否为0来判断函数返回值
}
//我们把 _size == s._size 放在前面,那么后面只需要memcpy(_str,s._str,_size)==0即可
bool operator<=(const string& s)
{return *this < s || *this == s;
}
bool operator>(const string& s)
{return !(*this <= s);
}
bool operator>=(const string& s) 
{return !(*this < s);
}
//访问指定下标的字符
//operator[]的实现
// 
char& operator[](size_t pos)
{//可读写   pos表示下标assert(pos < _size);return _str[pos];  //返回的是单个字符所以用char  且_str变量离开该函数依旧存在,可以使用&返回
}
string& operator=(const string& s)
{if (this != &s){//调用拷贝构造函数 将s的数据给tmpstring tmp(s);std::swap(_str, tmp._str);std::swap(_size, tmp._size);std::swap(_capacity, tmp._capacity);//进行交换,交换之后tmp在函数结束之后就会释放空间,但是其通过拷贝构造函数生成的新的string对象中的数值留给了*this对象}return *this;
}

流插入>>和流提取<<

//流提取
ostream& operator<<(ostream& out, const String::string& s)
{//就是将s字符串中的每一个字符都加载到out中for (auto ch : s){out << ch;}return out;
}
//流插入
istream& operator>>(istream& in, String::string& s)
{//判断一个字符是否结束 按照空格或者\0来判断s.clear();char buff [128];char ch = in.get();//get 字符int i = 0;while (ch == ' '|| ch == '\n'){ch = in.get();  //处理缓冲区前面的空格和换行}while (ch != ' ' && ch != '\n'){buff[i++] = ch;//如果输入的数值在127之外if (i == 127)  //先i++  相当于从1到127{//留出来一个空间给\0 所以只能这样buff[i] = '\0';s += buff;i = 0;//要重置i}ch = in.get();}if (i != 0){//如果i不0的话,那就是说数值长度在127之内 直接扩容buff[i] = '\0';s += buff;}return in;
}

总结:

  • 有关于插入以及添加字符、字符串的函数,都需要考虑容量的问题所以底层会有reserve,比如push_back、append、resize、insert、+=等函数
  • find的实现就是遍历,以及使用strstr函数,快速方便的得到下标
  • erase、insert等函数都会涉及到移动数组元素,erase向前移动,insert向后移动
  • 重载运算符的实现,可以方便我们使用string类

最后附上完整模拟实现string类的代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
#include<math.h>
//模拟实现string
namespace String {class string {public://迭代器: string中的迭代器实际上就是指针typedef char* iterator;typedef const char* const_iterator;iterator begin(){//begin 表示的是string的首元素地址return _str;}iterator end(){//end 返回string最后一个元素的下一个位置,也就是'\0'return _str + _size;}const_iterator begin() const{//begin 表示的是string的首元素地址return _str;}const_iterator end() const{//end 返回string最后一个元素的下一个位置,也就是'\0'return _str + _size;}//默认构造函数//string() //	:_str(new char[1])//	,size(0)//	,capacity(0)//{//	_str[0] = '\0';//}//带参构造函数  字符串//string(const char* str)//	:_str(new char[strlen(str)])//	,size(strlen(str))//	,capacity(strlen(str))//{//	//存储字符串//	strcpy(_str, str);//}//默认构造和带参构造合并,使用缺省参数string(const char* str="")  //""字符串自带'\0':_str(new char[strlen(str)+1]), _size(strlen(str)), _capacity(strlen(str)){//存储字符串strcpy(_str, str);}//拷贝构造//string(const string& s)//{//	//深拷贝,就是创建一个大小一样的空间//	_str = new char[s._capacity + 1];//	strcpy(_str, s._str);//	_size = s._size;//	_capacity = s._capacity;//}string(const string& s){_str = new char[s._capacity + 1];memcpy(_str, s._str,s._size + 1);_size = s._size;_capacity = s._capacity;}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}void Print() {cout << _str <<"\t" << _size <<"\t"<< _capacity << endl;}//size_t  无符号整型const char* c_str(){return _str;}//返回sizesize_t size() const  //const表示修饰this指针,也就是说只读,如果是const对象,也可以访问,普通用户相当于权限的缩小也可也访问{return _size;}//operator[]的实现// char& operator[](size_t pos){//可读写   pos表示下标assert(pos < _size);return _str[pos];  //返回的是单个字符所以用char  且_str变量离开该函数依旧存在,可以使用&返回}//对于const对象const char& operator[](size_t pos) const   //修饰const对象,前面const修饰的话,表示不可以被修改,后面const对象就修饰this指针  const String::string* this{//只读assert(pos < _size);return _str[pos];}//reserve  保留容量 可以扩容void reserve(size_t n)  //只是改变capacity 不改变size{if (n > _capacity){//新建一个字符数组cout << "reserve->" << n << endl;char* new_str = new char[n + 1];//更改容量//strcpy(new_str, _str);memcpy(new_str, _str, _size + 1);delete[] _str;_str = new_str;_capacity = n;}}//push_back	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){if (_size + strlen(str) > _capacity){reserve(_size + strlen(str));//至少保留_size + strlen(str)}//加入字符串memcpy(_str + _size, str, strlen(str)+1);//在'\0'位置(就是_str末尾)+str_size += strlen(str);}//实现+=  也是使用push_back 和append函数string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}string& insert(size_t pos, size_t n, char c){//1.先判定pos是否正确assert(pos <= _size);//2.扩容reserve(_size + n);//3.在pos位置上开始挪动n个字符size_t end = _size;//因为如果pos为0的时候,无符号整型0减去1,end >= pos比较 为一个巨大值,使得该循环无法停止//npos是static变量,定义为-1;while (end >= pos && end != npos){_str[end + n] = _str[end];--end;}//4.添加n个字符for (int i = 0; i < n; i++){_str[pos + i] = c;}_size += n;return *this;}string& insert(size_t pos, const char*str){//1.pos的判定assert(pos <= _size);//2.扩容int len = strlen(str);reserve(_size + len);//3.在pos位置上移动len个字符size_t end = _size;while (end >= pos && end != npos){_str[end + len] = _str[end];--end;}//4.将str字符串的字符依次输入for (int i = 0; i < len; i++){_str[pos + i] = str[i];}//5.最后_size增加_size += len;return *this;}//从pos位置开始,删除len长度字符string& erase(size_t pos = 0, size_t len = npos){assert(pos <= _size);if (len == npos || pos + len >= _size){//表示从pos位置删完_str[pos] = '\0';_size = pos;}else {//从pos位置删除len个字符//向前挪动size_t end = pos;while (end+len <= _size)  {_str[end] = _str[end + len];end++;}//此时end+len==_size//_str[end] = '\0';_size -= len;}return *this;}//findsize_t find(char ch, size_t pos = 0) const{assert(pos < _size);for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;  //没有找到返回-1;}size_t find(const char* s, size_t pos = 0) const{//使用strstrassert(pos < _size);const char* str = strstr(_str+pos, s);if (str){return str-_str;//两个指针相减,得到的是地址的偏移量}else {return npos;}}//substr的实现string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t n = len;//如果缺省len=npos 或者是截取的范围大于_sizeif (len == npos || pos + len >= _size){n = _size - pos;  }//创建一个新的字符数组string new_str;new_str.reserve(n);for (size_t i = pos; i < n + pos; i++){new_str += _str[i];}return new_str;}void clear(){_str[0] = '\0';_size = 0;}//实现resizevoid resize(size_t n, char ch = '\0'){//两种情况,1.n<_size 直接赋值'\0' 2.判断是否扩容 if (n < _size){_size = n;_str[_size] = '\0';}else{reserve(n);//让reserve来判断是否是需要扩容for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;_str[_size] = '\0';}}//字符串比较按照ascii比较//bool operator<(const string& s)//{//	int num=memcmp(_str, s._str, _size > s._size?s._size : _size);//	//如果在最小长度下,前面数值小于后者 num返回的是负数//	//	return num == 0 ? _size < s._size : num < 0;//	//如果如果num为0,说明等于,且前者长度小于后者,返回真值,反之返回//}bool operator<(const string& s){size_t i1 = 0;size_t i2 = 0;int num = _size > s._size ? s._size : _size;while (i1 < num && i2 < num){if (_str[i1] < s._str[i2]){return true;}else if(_str[i1] > s._str[i2]) {return false;}else{++i1;++i2;}}return _size < s._size;}bool operator==(const string& s){return _size == s._size && memcmp(_str, s._str, _size) == 0;}bool operator<=(const string& s){return *this < s || *this == s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s) {return !(*this < s);}//string& operator=(const string& s)//{//	if (this != &s)//	{//		//如果不是同一个string//		//深拷贝//		char* new_str = new char[s._capacity + 1];//		memcpy(new_str, s._str, s._size);//		//删除原来地址//		delete[] _str;//		//新指向一个new_str//		_str = new_str;//		//更改容量和size//		_capacity = s._capacity;//		_size = s._size;//	}//	return *this;//}string& operator=(const string& s){if (this != &s){//调用拷贝构造函数 将s的数据给tmpstring tmp(s);std::swap(_str, tmp._str);std::swap(_size, tmp._size);std::swap(_capacity, tmp._capacity);//进行交换,交换之后tmp在函数结束之后就会释放空间,但是其通过拷贝构造函数生成的新的string对象中的数值留给了*this对象}return *this;}size_t capacity(){return _capacity;}size_t size(){return _size;}//无穷递归的问题:反复调用堆栈// std::swap(*this,tmp)//定义属性private:char* _str;int _size;int _capacity;public:const static size_t npos;};const size_t string::npos = -1;
}
//流提取
ostream& operator<<(ostream& out, const String::string& s)
{//就是将s字符串中的每一个字符都加载到out中for (auto ch : s){out << ch;}return out;
}
//流插入
istream& operator>>(istream& in, String::string& s)
{//判断一个字符是否结束 按照空格或者\0来判断s.clear();char buff [128];char ch = in.get();//get 字符int i = 0;while (ch == ' '|| ch == '\n'){ch = in.get();  //处理缓冲区前面的空格和换行}while (ch != ' ' && ch != '\n'){buff[i++] = ch;//如果输入的数值在127之外if (i == 127)  //先i++  相当于从1到127{//留出来一个空间给\0 所以只能这样buff[i] = '\0';s += buff;i = 0;//要重置i}ch = in.get();}if (i != 0){//如果i不0的话,那就是说数值长度在127之内 直接扩容buff[i] = '\0';s += buff;}return in;
}

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

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

相关文章

优化企业集成架构:iPaaS集成平台助力数字化转型

前言 在数字化时代全面来临之际&#xff0c;企业正面临着前所未有的挑战与机遇。技术的迅猛发展与数字化转型正在彻底颠覆各行各业的格局&#xff0c;不断推动着企业迈向新的前程。然而&#xff0c;这一数字化时代亦衍生出一系列复杂而深奥的难题&#xff1a;各异系统之间数据…

Vite创建Vue+TS项目引入文件路径报错

使用vite搭建vue3脚手架的时候&#xff0c;发现main.ts中引入App.vue编辑器会报错&#xff0c;但是不影响代码运行。 报错信息&#xff1a;TS2307: Cannot find module ‘./App.vue’ or its corresponding type declarations. 翻译过来是找不到模块或者相关的声明类型&#…

如何快速模拟一个后端 API

第一步&#xff1a;创建一个文件夹&#xff0c;用来存储你的数据 数据&#xff1a; {"todos": [{ "id": 1, "text": "学习html44", "done": false },{ "id": 2, "text": "学习css", "…

Redis7学习笔记01

一、redis7实战教程简洁 1、大纲&#xff1a; ①、适合对象&#xff0c;从小白到熟手&#xff0c;一套全包圆 ②、Redis专题-大厂面试题&#xff0c;含100道 ③、Redis专题-真实需求生产真实案例 ④、Redis7新特性 2、小白篇高阶篇&#xff1a; 3、大厂面试题&#xff1a…

自动化测试——APP测试

一、环境配置 1、安装jdk 配置环境变量 2、Android SDK 环境安装 3、Appium Server安装 4、模拟器安装 5、安装appium-python-client Python第三方库 二、APP自动化测试原理 三、Desired Capabilites——APPium自动化配置项 1、设置参数 2、操作系统 3、选择版本 4、设备名称…

TCP KeepAlive与HTTP Keep-Alive

TCP KeepAlive与HTTP Keep-Alive TCP KeepAliveHTTP Keep-AliveTCP服务器怎么检测客户端断开连接 TCP KeepAlive TCP连接建立之后&#xff0c;如果应用程序或者上层协议一直不发送数据&#xff0c;或者隔很长时间才发送一次数据&#xff0c;那么TCP需要判断是应用程序掉线了还…

Android Studio 启用设备远程调试配置完整步聚

启用手机设置->开发者选项-无线调试,然后选择允许 已启用后无线调试变成绿色 ,点击无线调试进入详情页面 点击Android Studio的Device Manager 下的WIFI图标 会弹出下图窗口 打开手机的开发者选项中的WIFI调试(无线调试)下的使用二维码配对设备进行扫描. 设备配对成功后手机…

Unity Shader - if 和 keyword 的指令比较

文章目录 环境TestingIf4Sampleunity shaderlab 中的 TestingIf4Sample.shadergraphics analyzer 中的 TestingIf4Sample.glsl TestingKW4Sampleunity shaderlab 中的 TestingKW4Sample.shadergraphics analyzer 中的 TestingKW4Sample.glsl 比较 环境 Unity : 2020.3.37f1 Pi…

在Linux中用strsignal函数输出对各种信号的描述

2023年7月29日&#xff0c;周六上午 目录 函数原型Linux有多少种信号使用示例 函数原型 #include <string.h>char* strsignal(int signum);strsignal函数接受一个整数参数signum&#xff0c;表示信号的编号。 用于把信号编号转换成一个简短的对这个信号编号的描述。 L…

JAVA SE -- 第十一天

&#xff08;全部来自“韩顺平教育”&#xff09; 异常-Exception 一、异常介绍 1、基本介绍 Java语言中&#xff0c;将程序执行中发生的不正常情况为“异常”&#xff08;开发过程中的语法错误和逻辑错误不是异常&#xff09; 2、执行过程中发生的异常事件可分为两大类 …

FPGA2-采集OV5640乒乓缓存后经USB3.0发送到上位机显示

1.场景 基于特权A7系列开发板&#xff0c;采用OV5640摄像头实时采集图像数据&#xff0c;并将其经过USB3.0传输到上位机显示。这是验证数据流能力的很好的项目。其中&#xff0c;用到的软件版本&#xff0c;如下表所示&#xff0c;基本的硬件情况如下。该项目对应FPGA工程源码…

图注意力网络论文详解和PyTorch实现

图神经网络(gnn)是一类功能强大的神经网络&#xff0c;它对图结构数据进行操作。它们通过从节点的局部邻域聚合信息来学习节点表示(嵌入)。这个概念在图表示学习文献中被称为“消息传递”。 消息(嵌入)通过多个GNN层在图中的节点之间传递。每个节点聚合来自其邻居的消息以更新其…

MyBatis(二)

文章目录 一.MyBatis的模式开发1.1 定义数据表和实体类1.2 配置数据源和MyBatis1.3 编写Mapper接口和增加xxxMapper.xml1.4 测试我们功能的是否实现. 二. Mybatis的增删查改操作2.1 单表查询2.2 多表查询三.动态SQL的实现3.1 什么是动态SQL3.2 动态SQL的使用if标签的使用trim标…

M1/M2 通过VM Fusion安装Win11 ARM,解决联网和文件传输

前言 最近新入了Macmini M2&#xff0c;但是以前的老电脑的虚拟机运行不起来了。&#x1f605;&#xff0c;实际上用过K8S的时候&#xff0c;会发现部分镜像也跑不起来&#xff0c;X86的架构和ARM实际上还是有很多隐形兼容问题。所以只能重新安装ARM Win11&#xff0c;幸好微软…

【MySQL】表的操作

今天我们来谈谈MySQL下对表的操作 目录 一、创建表 二、查看表 2.1 查看库中存有的表 2.2 查看表结构 2.3 查看表的创建语句 三、修改表 3.1 重命名表名 3.2 新增列 3.3 修改列的数据类型 3.4 删除列 3.5 重命名列 四、删除表 一、创建表 我们要想在数据库中创建…

SpringMvc+阿贾克斯

0目录 1.SpringMVC 加阿贾克斯 2.分页版 1.实战 创建数据库 创建工程和pom依赖 配置web.xml和applicationContext.xml 实体类 Mapper接口方法 Mapper.xml BookService BookSeriviceImpl 控制层 测试 加入findAll.html 测试 2.分页版 控制层 PostMan测…

华为OD机试真题 Java 实现【AI面板识别】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明4、控制台输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08…

【C++】STL中stack,queue容器适配器的模拟实现(使用deque容器)

文章目录 前言一、deque的一些基本知识相比vector&#xff1a;相比list&#xff1a;3为什么选择deque作为stack和queue的底层默认容器 二、stack模拟实现三、queue的模拟实现 前言 STL标准库中stack和queue的底层结构 &#xff1a;虽然stack和queue中也可以存放元素&#xff0c…

java文件相关操作工具,包括读取服务器路径下文件,删除文件及子文件,删除文件夹等方法

文章目录 一、记录文件相关操作方法二、代码1.读取路径返回List\<File>2.读取路径返回List\<String>3.删除文件夹4.删除文件 一、记录文件相关操作方法 二、代码 1.读取路径返回List<File> import org.slf4j.LoggerFactory; import org.slf4j.Logger;impo…

【计算机视觉 | 图像分割】arxiv 计算机视觉关于图像分割的学术速递(7 月 26 日论文合集)

文章目录 一、分割|语义相关(7篇)1.1 Learning Transferable Object-Centric Diffeomorphic Transformations for Data Augmentation in Medical Image Segmentation1.2 Optical Flow boosts Unsupervised Localization and Segmentation1.3 Spectrum-guided Multi-granularity…