手搓string类
手搓string类
文章目录
- 手搓string类
- string的成员
- 一.构造,拷贝构造,赋值重载和析构
- 1.构造函数
- 2.拷贝构造
- 3.swap问题
- 4.赋值重载
- 5.析构函数
- 二.简单接口
- 1.c_str
- 2.size(有效字符长度)
- 3.capacity(有效字符容量)
- 4.operator[]
- 5.迭代器和范围for
- 三.容量
- 1.reverse
- 2.resize
- 3.clear
- 四.插入
- 1.push_back
- 2.append
- 3.operator+=
- 4.insert
- 5.erase
- 五.查找
- find
- 六.流插入<<和流提取>>的重载
- 1.流插入<<重载
- 2.流提取>>重载
- 七.整体实现代码
string的成员
class string()
{private:char *str;size_t _size;size_t _capacity;const static size_t npos=-1;
};
在类和对象中提到过,静态成员变量不能给缺省值,必须要在类外定义。
但是其实有一个特例,那就是针对整形开了一个后门,静态的整形成员变量可以直接在类中定义。
一.构造,拷贝构造,赋值重载和析构
1.构造函数
在类和对象时提到过,如果要显示定义构造函数最好是给全缺省
string(const char *str="")//注意:\0和空指针一样,'\0'是字符常量(char类型),“”和“\0"一样,
{_size = strlen(str);//这是成员函数,有this指针_capacity = _size;_str = new char [_capacity + 1];//多开一个字节给'\0'strcpy(_str, str);//可能会传参过来构造
}
这里的_capacity是给有效字符预留的空间,为了给’\0’留位置在开空间的时候要多开一个。
2.拷贝构造
首先是老实人写法,构造一个新的空间,将s._str的值拷贝到新的空间,再将其他的值拷贝
//拷贝构造
string(const string& s)
{//先构造一个新空间给_str_str = new char[s._capacity + 1];//然后将s的其他值赋值给this_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);
}
在下面的写法中,会经常复用C语言中的字符串函数。(不为别的,就是好用)
不是谁都想当老实人,所以又有了一种现代写法(并不是为了提高效率,而是为了让代码更简洁)。
首先构造一个tmp,然后将tmp和s交换
void swap(string&s)
{std::swap(_str, s._str);//库中和string分别提供了一个swap函数,用库中的swap交换内置类型std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}string(const string& s):_str(nullptr) //这里必须要给_str初始化,否则拿到一个非法的地址,析构会报错,_size(0),_capacity(0)
{string tmp(s._str);//这里复用构造函数swap(tmp);//有隐藏的this指针,一个参数就够了
}
如果不给_str初始化成空指针话,交换以后临时变量tmp就指向了 _str的空间,这可能是一个野指针,临时变量tmp在出这个函数就要被销毁,调用析构的时候delete一个野指针就会产生错误。
可以delete一个空指针,无论是free还是delete当接收的参数是一个空指针时就不做任何处理。
3.swap问题
标准库中的swap函数是一个模板,在交换自定义类型时有一次构造和拷贝构造,代价比较大,所以我们提供一个成员函数会比较好。在成员函数中交换内置类型时就可以使用标准库中的swap函数,**要指定域,因为编译器默认是现在局部找,局部找不到再去全局找,再找不到就报错。**如果去局部找的话,找到的swap函数参数不匹配。
4.赋值重载
复制重载也分为传统写法:
string& operator=(const string& s)
{if (this != &s)//要注意不要自己给自己赋值{//深拷贝char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);//为新空间赋值_str = tmp;_capacity = s._capacity;_size = s._size;return *this;}}
现代写法,使用传值传参,然后直接使用临时变量交换,这个写法是我比较推荐的(太简洁了)
void swap(string&s)
{std::swap(_str, s._str);//库中和string分别提供了一个swap函数,用库中的swap交换内置类型std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
string& operator=(string s)
{swap(s);//传值传参,s是一个拷贝,直接使用这个临时变量,反正临时变量出了这个函数就被销毁了return *this;
}
s是一个拷贝的临时变量,在销毁的时候会自动调用析构函数清理,不用我们做额外处理,而且直接使用临时变量调用swap还省去了我们创建临时变量。十分简洁,但是可读性不算太好
5.析构函数
直接使用delete销毁空间,再将_size和 _capacity置0就行
//析构函数
~string()
{//释放空间delete[] _str;_str = nullptr;_size = _capacity= 0;
}
二.简单接口
1.c_str
这个主要是返回一个C类型的数组指针
const char* c_str()const
{return _str;
}
对于只读的函数接口建议加上const,这样不但普通对象可以使用,const类型的对象也可以使用
2.size(有效字符长度)
size_t size()const
{return _size;
}
3.capacity(有效字符容量)
size_t capacity()const
{return _capacity;
}
4.operator[]
char& operator[](size_t pos)
{//虽然string是自定义类型,但_str是内置类型assert(pos < _size);return _str[pos];
}const char& operator[](size_t pos)const//const对象只读
{assert(pos < _size);return _str[pos];
}
因为const对象只能读不可写,所以这里要重载两个,重载了[]string就可以像访问数组那样访问了
5.迭代器和范围for
强调一下,迭代器虽然行为像指针,但不一定是指针
typedef char* iterator;//这里为了简单,就实现成指针iterator begin()
{return _str;
}iterator end()
{return _str + _size;
}
有了迭代器,就可以使用范围for了,但是范围for只认识beging和end,所以如果要使用范围for,在手搓迭代器的时候就不要乱取名哦。
三.容量
1.reverse
这个是string类中用于扩容的成员函数
void reverse(size_t n)
{//只扩容,所以先检查情况if (n > _capacity){//_str中有数据,不能直接改_str的空间,要先建立临时空间char*tmp = new char[n + 1];//将_str中的数据拷贝给tmp,再将_str所指的空间释放strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;//更新容量}
}
reverse是控制容量的成员函数,但是缩容的代价太大了。所以只考虑缩容,其实string类中给的reverse是会缩容的。
2.resize
这个是改变有效字符长度的成员函数
void resize(size_t n,char ch='\0')//改变size可能会改变capacity,默认插入补空间的字符是'\0'
{if (n > _capacity)//这里也可以写n>_size{reverse(n);//扩大空间以后,要用字符初始化后续空间for (size_t i = _size; i < n; i++){_str[i] = ch;}//这里还要改变size_size = n;//可能使用者会传其他字符来初始化,前面的循环没有在size位置补'\0'_str[_size] = '\0';}else {//如果是缩小的话,就直接在n位置补'\0'_str[n] = '\0';_size = n;}}
有看到n>_size就扩容的,但在我看来只有大于容量的时候才有必要改变有效容量。
在缩小的时候同样没必要更改容量,直接在n位置插入一个’\0’,就无法访问到n后面的元素了,间接改变了__size
3.clear
这个成员函数是将string变成一个空串,在重载流提取的时候会用到这个函数
void clear()
{_size = 0;_str[0] = '\0';
}
四.插入
1.push_back
尾插单个字符,插入就要考虑扩容
void push_back(char c)
{if (_size == _capacity)//容量不够,要扩容{//扩容要调用reverse,这里还要检查一下_capacity,第一次可能是0int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);//这里面更新了_capacity,外面不用再更新}//扩容完毕以后,就可以开始插入了_str[_size] = c;_size++;_str[_size] = '\0';
}
这样尾插只能插入单个字符,所以string还提供了一个append(追加字符串)
2.append
这是一个在末尾追加字符串的成员函数
string& append(const char* s)
{//这里可以使用C标准库中的strcpy,不过还是要考虑扩容的问题,要检查剩下的空间是否足够插入int len = strlen(s);if (_size + len > _capacity){//容量不够插入就要扩容reverse(_capacity + len);}//复用C标准库函数strcpy(_str + _size, s);_size += len;return *this;
}
追加一个字符串开原本空间的两倍可能还不够用,最正确的写法是计算一下字符串的长度用于增加空间
3.operator+=
重载+=是string类一个非常正确的选择,在做oj的时候你将发现,+=比尾插和追加好用太多了。
string& operator+=(char c)
{//插入一个字符直接_size位置插入,复用push_backpush_back(c);return *this;
}//还要重载一个字符串类型,可以复用append
string& operator+=(const char* str)
{append(str);return *this;
}
先介绍push_back和append不是没有道理的,复用可以减少代码冗余还省事,还不快复用起来
4.insert
在pos位置插入一个字符或者字符串,需要挪动数据。
插入单个字符:
string& insert(size_t pos, char c)
{assert(pos <= _size);//string没有单独的扩容函数,在每个插入数据的地方都要检查容量if (_size == _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);}//插入字符要挪动字符,这里要小心,在pos位置插入size_t end = _size + 1;//指向'\0'的下一个位置while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = c;_size += 1;return *this;
}
这里有一个问题,要知道end和pos都是size_t类型的数据,如果在写判断条件的时候,写成end>=pos,可能会陷入死循环。
如果pos是0,end永远无法小于零,这就死循环咯。
解决办法有两种,其一是把end写成有符号的int并且在判断的时候强转pos,也就是这样:
int end = _size;while (end >=(int) pos) {_str[end] = _str[end - 1];--end; }
第二种就是我写的这样,把end放在_size+1的位置,这样在判断的时候可以不用取等号,也就完美避免了所有问题。我比较推荐这种写法,因为end作为下标本身取值范围就应该要大于零。
插入字符串:
string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (_size +len > _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);}//扩容完毕,开始挪动数据,这次要挪动len个位置,要考虑一下长度是否越界size_t end = _size + len;while (end > pos + len-1){_str[end] = _str[end - len];end--;}//位置挪出来以后要插入字符串/*for (size_t i = pos; i <= pos + len; i++){_str[i] = *str;3str++;}*///此外,还可以复用C库函数strncpystrncpy(_str + pos, str, len);_size += len;return *this;
}
这里要用strncpy而不能使用strcpy,因为strcpy会将’\0’也拷贝过来,而字符串的结束标志就是以’\0’为准的。
5.erase
从pos位置开始删除len个长度的字符
//实现一下erase
string& erase(size_t pos = 0, size_t len = npos)
{assert(pos < _size);//删除要判断是否会越界if (len == npos || pos + len >= _size){//说明要删除的长度超过了pos后面有的字符串,只要直接在pos位置插入'\0'就行_str[_size] = '\0';_size = pos;}else{//如果不越界,就要覆盖删除,可以复用strcpystrcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}
说到erase就不得不说一下npos,npos是一个无符号整形默认是-1也就是四十二亿九千万,如果长度大于npos就是越界了。
五.查找
find
从pos位置开始查找一个字符或者一段字符串
size_t find(char c, size_t pos)
{assert(pos < _size);//断言检查for (int i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;
}size_t find(const char* s, size_t pos)
{//这里可以考虑复用C的库函数strstrassert(pos < _size);char *ret = strstr(_str + pos, s);//这个C语言库函数返回的是一个指针//检查合法性if (ret == NULL){return npos;}else{return ret - _str;}
}
其实在一般的情况下使用strstr查找子串已经够了,kmp算法其实是一个外强中干的家伙。
六.流插入<<和流提取>>的重载
这个我们在日期类中就已经接触过了,不能写在类中否则会被this指针抢第一个参数位置,还是使用友元然后定义在类外。
1.流插入<<重载
ostream& operator<<(ostream& out, const string& s)
{for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}
2.流提取>>重载
istream& operator>>(istream& in, string& s)
{s.clear();char ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;//我就说+=好用吧ch = in.get();}return in;
}
但这个代码有一个不好的地方在于插入长字符串时可能会频繁的扩容。
为了减少扩容次数,我可以建立一个数组,这个数组满了就往s中插入,数组满一次才扩容一次,有效减少扩容次数
stream& operator>>(istream& in, string& s)//要对s插入数据,s不能为const类型
{s.clear();//清空s//定义一个数组,往这个数组中放数据,当这个数组满了以后+=给s,这样可以避免频繁的扩容char buff[128] = { '\0' };char ch = in.get();//从缓冲区拿字符size_t i = 0;while (ch != ' ' && ch != '\n'){if (i == 127){s += buff;i = 0;//满了以后+=geis再将i重置为0,开始下一轮}buff[i++] = ch;ch = in.get();}//如果输入的数据不能让数组满呢?if (i > 0){//只要有数据就应该导进来,字符数组是以'\0'作为结束标志的,对于只插入几个字符的情况,要手动补'\0'buff[i] = '\0';//后置++,是有效字符的下一个位置s += buff;}return in;
}
getline就是在判断的时候把空格去掉就行,只以换行作为结束标志。但是cout,cin以及scanf和printf都是以空格和换行为结束标志的。
七.整体实现代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;
namespace wbm
{class string{public://重载流插入和流提取,不能写在类中,使用友元friend istream& operator>>(istream& in, string& s);friend ostream& operator<<(ostream& out, const string& s);//首先写构造函数,给一个全缺省的构造string(const char *str="")//注意:\0和空指针一样,'\0'是字符常量(char类型),“”和“\0"一样,{_size = strlen(str);//这是成员函数,有this指针_capacity = _size;_str = new char [_capacity + 1];//多开一个字节给'\0'strcpy(_str, str);//可能会传参过来构造}//析构函数~string(){//释放空间delete[] _str;_str = nullptr;_size = _capacity= 0;}拷贝构造//string(const string& s)//{// //先构造一个新空间给_str// _str = new char[s._capacity + 1];// //然后将s的其他值赋值给this// // _size = s._size;// _capacity = s._capacity;// strcpy(_str, s._str);//}//不当老实人,使用现代写法,主要是调用swap函数void swap(string&s){std::swap(_str, s._str);//库中和string分别提供了一个swap函数,用库中的swap交换内置类型std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string(const string& s):_str(nullptr) //这里必须要给_str初始化,否则拿到一个非法的地址,析构会报错,_size(0),_capacity(0){string tmp(s._str);//这里复用构造函数swap(tmp);//有隐藏的this指针,一个参数就够了}//重载=//string& operator=(const string& s)//{// if (this != &s)// {// //深拷贝// char* tmp = new char[s._capacity + 1];// strcpy(tmp, s._str);//为新空间赋值// _str = tmp;// _capacity = s._capacity;// _size = s._size;// return *this;// }//}//不当老实人,现代写法主要是为了简洁,并不能提高效率string& operator=(string s){swap(s);//传值传参,s是一个拷贝,直接使用这个临时变量,反正临时变量除了这个函数就被销毁了return *this;}//重载+=,两种形式,一种加字符,还有加字符串//string& operator+=(char ch)//{// //在字符串size位置直接插入,然后补一个'\0'// //但是这里要考虑一个问题,就是扩容,所以不如直接写尾插// _str[_size] = ch;// ++_size;// _str[_size] = '\0';//}void reverse(size_t n){//只扩容,所以先检查情况if (n > _capacity){//_str中有数据,不能直接改_str的空间,要先建立临时空间char*tmp = new char[n + 1];//将_str中的数据拷贝给tmp,再将_str所指的空间释放strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}//reverse都有了,这不来个resizevoid resize(size_t n,char ch='\0')//改变size可能会改变capacity,默认插入补空间的字符是'\0'{if (n > _capacity){reverse(n);//扩大空间以后,要用字符初始化后续空间for (size_t i = _size; i < n; i++){_str[i] = ch;}//这里还要改变size_size = n;//可能使用者会传其他字符来初始化,前面的循环没有在size位置补'\0'_str[_size] = '\0';}else {//如果是缩小的话,就直接在n位置补'\0'_str[n] = '\0';_size = n;}}void push_back(char c){if (_size == _capacity)//容量不够,要扩容{//扩容要调用reverse,这里还要检查一下_capacity,第一次可能是0int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);//这里面更新了_capacity,外面不用再更新}//扩容完毕以后,就可以开始插入了_str[_size] = c;_size++;_str[_size] = '\0';}string& operator+=(char c){//插入一个字符直接_size位置插入,复用push_backpush_back(c);return *this;}//还要重载一个字符串类型,可以复用appendstring& operator+=(const char* str){append(str);return *this;}string& append(const char* s){//这里可以使用C标准库中的strcpy,不过还是要考虑扩容的问题,要检查剩下的空间是否足够插入int len = strlen(s);if (_size + len > _capacity){//容量不够插入就要扩容reverse(_capacity + len);}//复用C标准库函数strcpy(_str + _size, s);_size += len;return *this;}//要重载一下[],方便读写char& operator[](size_t pos){//虽然string是自定义类型,但_str是内置类型assert(pos < _size);return _str[pos];}const char& operator[](size_t pos)const//const对象只读{assert(pos < _size);return _str[pos];}//[]都有了,这不来个迭代器(行为像指针,但不一定是指针,这里写成指针)typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}//写一个find函数,搞两个,一个是找字符,一个是找字符串size_t find(char c, size_t pos){assert(pos < _size);//断言检查for (int i = pos; i < _size; i++){if (_str[i] == c){return i;}}return npos;}size_t find(const char* s, size_t pos){//这里可以考虑复用C的库函数strstrassert(pos < _size);char *ret = strstr(_str + pos, s);//这个C库函数返回的是一个指针//检查合法性if (ret == NULL){return npos;}else{return ret - _str;}}//还要insert和erase//实现insert,分为插入字符和插入字符串string& insert(size_t pos, char c){assert(pos <= _size);//string没有单独的扩容函数,在每个插入数据的地方都要检查容量if (_size == _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);}//插入字符要挪动字符,这里要小心,在pos位置插入size_t end = _size + 1;//指向'\0'的下一个位置while (end >=(int) pos){_str[end] = _str[end - 1];--end;}_str[pos] = c;_size += 1;return *this;}string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (_size +len > _capacity){int newCapacity = _capacity == 0 ? 4 : 2 * _capacity;reverse(newCapacity);}//扩容完毕,开始挪动数据,这次要挪动len个位置,要考虑一下长度是否越界size_t end = _size + len;while (end > pos + len-1){_str[end] = _str[end - len];end--;}//位置挪出来以后要插入字符串/*for (size_t i = pos; i <= pos + len; i++){_str[i] = *str;str++;}*///此外,还可以复用C库函数strncpystrncpy(_str + pos, str, len);_size += len;return *this;}//实现一下erasestring& erase(size_t pos = 0, size_t len = npos){assert(pos < _size);//删除要判断是否会越界if (len == npos || pos + len >= _size){//说明要删除的长度超过了pos后面有的字符串,只要直接在pos位置插入'\0'就行_str[_size] = '\0';_size = pos;}else{//如果不越界,就要覆盖删除,可以复用strcpystrcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}//下面写几个简单函数,比如返回_size,_capacity,返回一个C类的指针const char* c_str()const{return _str;}size_t size()const{return _size;}size_t capacity()const{return _capacity;}void clear(){_size = 0;_str[0] = '\0';}private:char* _str;size_t _size;size_t _capacity;const static size_t npos=-1; //静态成员变量只有整形可以给缺省值};ostream& operator<<(ostream& out, const string& s){for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;}istream& operator>>(istream& in, string& s)//要对s插入数据,s不能为const类型{s.clear();//定义一个数组,往这个数组中放数据,当这个数组满了以后+=给s,这样可以避免频繁的扩容char buff[128] = { '\0' };char ch = in.get();//从缓冲区拿字符size_t i = 0;while (ch != ' ' && ch != '\n'){if (i == 127){s += buff;i = 0;//满了以后+=geis再将i重置为0,开始下一轮}buff[i++] = ch;ch = in.get();}//如果输入的数据不能让数组满呢?if (i > 0){//只要有数据就应该导进来,字符数组是以'\0'作为结束标志的,对于只插入几个字符的情况,要手动补'\0'buff[i] = '\0';//后置++,是有效字符的下一个位置s += buff;}return in;}
}
urn _str;
}
size_t size()const{return _size;}size_t capacity()const{return _capacity;}void clear(){_size = 0;_str[0] = '\0';}
private:char* _str;size_t _size;size_t _capacity;const static size_t npos=-1; //静态成员变量只有整形可以给缺省值
};
ostream& operator<<(ostream& out, const string& s)
{for (size_t i = 0; i < s.size(); i++){out << s[i];}return out;
}istream& operator>>(istream& in, string& s)//要对s插入数据,s不能为const类型
{s.clear();//定义一个数组,往这个数组中放数据,当这个数组满了以后+=给s,这样可以避免频繁的扩容char buff[128] = { '\0' };char ch = in.get();//从缓冲区拿字符size_t i = 0;while (ch != ' ' && ch != '\n'){if (i == 127){s += buff;i = 0;//满了以后+=geis再将i重置为0,开始下一轮}buff[i++] = ch;ch = in.get();}//如果输入的数据不能让数组满呢?if (i > 0){//只要有数据就应该导进来,字符数组是以'\0'作为结束标志的,对于只插入几个字符的情况,要手动补'\0'buff[i] = '\0';//后置++,是有效字符的下一个位置s += buff;}return in;
}
}
---文章到此就结束啦,希望对各位有所帮助。
相关文章:

手搓string类
手搓string类 文章目录手搓string类string的成员一.构造,拷贝构造,赋值重载和析构1.构造函数2.拷贝构造3.swap问题4.赋值重载5.析构函数二.简单接口1.c_str2.size(有效字符长度)3.capacity(有效字符容量)4.operator[]5.迭代器和范…...

小白学Pytorch系列--Torch API (7)
小白学Pytorch系列–Torch API (7) Comparison Ops allclose 此函数检查输入和其他是否满足条件: >>> torch.allclose(torch.tensor([10000., 1e-07]), torch.tensor([10000.1, 1e-08])) False >>> torch.allclose(torch.tensor([10000., 1e-…...

函数(上)——“Python”
各位CSDN的uu们你们好呀,今天小雅兰的内容是Python的函数呀,下面,就让我们进入函数的世界吧 首先可以选择性地看一下小雅兰很久之前写的C语言函数章节的知识: 函数——“C”_认真学习的小雅兰.的博客-CSDN博客 函数递归…...

ChatGPT说:如何利用ChatGPT变现?躺着赚钱不是梦。
您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。 💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通 😁 2. 毕业设计专栏&…...

4.网络爬虫—Post请求(实战演示)
网络爬虫—Post请求实战演示POST请求GET请求POST请求和GET请求的区别获取二进制数据爬[百度官网](https://www.baidu.com/)logo实战发送post请求百度翻译实战使用session发送请求模拟登录17k小说网常见问题前言: 📝📝此专栏文章是专门针对…...

【视频文稿】车载Android应用开发与分析 - 开发系统应用
本期视频地址:https://www.bilibili.com/video/BV1NY411z7TK/ 前言 Hello,大家好,我是林栩。 开发车载应用,其实主要都是在Android系统中编写各种系统应用,所以上期视频先介绍了Android系统源码的下载和编译流程&…...

Scala流程控制
目录 单分支 双分支 多分支 for 循环控制 循环守卫 循环步长 循环嵌套 循环返回值 While 和 do..While 循环控制 While循环控制 do..While 循环控制 循环中断 单分支 if (条件表达式) {执行代码块 }var age StdIn.readShort()if (age < 18){println("童年&quo…...

人脸活体检测系统(Python+YOLOv5深度学习模型+清新界面)
摘要:人脸活体检测系统利用视觉方法检测人脸活体对象,区分常见虚假人脸,以便后续人脸识别,提供系统界面记录活体与虚假人脸检测结果。本文详细介绍基于YOLOv5深度学习技术的人脸活体检测系统,在介绍算法原理的同时&…...

prometheus03-如何导出prometheus指标
Prometheus是一个开源的监控系统和时间序列数据库,用于收集和存储服务的指标数据。要导出Prometheus指标,你需要使用或实现一个Prometheus Exporter。以下是一个简单的指南,分为三个主要步骤: 选择或实现Prometheus Exporter Pr…...

Linux驱动开发——串口设备驱动
Linux驱动开发——串口设备驱动 一、串口简介 串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。使用两条线即可实现双向通信,一条用于发送,一条用于接收。串口通信距…...

LeetCode--缺失的第一个正数(41)和 接雨水(42)
目录 缺失的第一个正数 接雨水 0ms,100% 代码 缺失的第一个正数 来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/first-missing-positive 题目:给你一个未排序的整数数组 nums ,请…...

java源码阅读---ReentrantLock源码解析
ReentrantLock源码解读 在讲ReentrantLock之前我们先看一下Lock接口里的方法 Lock接口中的方法 lock()方法 void lock(); //直接加锁,如果加锁失败什么也不返回lockInterruptibly()方法 void lockInterruptibly() throws InterruptedException;lockInterruptibly()方法能够…...

OpenCv + Qt5.12.2 文字识别
OpenCv Qt5.12.2 文字检测与文本识别 前言 好久没有进行一些相关的更新的了,去年一共更新了四篇,最近一直在做音视频相关的直播服务,又是重新学习积攒经验的一个过程。去年疫情也比较严重,等到解封,又一直很忙&a…...

网络作业1【计算机网络】
网络作业1【计算机网络】前言推荐网络作业1一. 单选题(共7题,58.1分)二. 多选题(共1题,8.3分)三. 判断题(共4题,33.6分)最后前言 2023-3-13 20:11:42 以下内容源自《计…...

常见背包问题
一.前言若你想学习或正在学习动态规划,背包问题一定是你需要了解的一种题型,并且大多数人最初都是从背包问题入坑进而打开动态规划这一大门。背包问题分为多种,你可以先掌握最常见的主要是三类:01背包、完全背包、多重背包二.分析…...

【python】python编译器以及安装
✅作者简介:一名在读大二学生,希望大家多多支持 🔥系列专栏:python 💬个人主页:小园园子的CSDN博客 python编译器以及安装一、编译器与解释器详细内容Python解释器种类Python的运行机制二、python环境搭建p…...

Effective C++快速复习
Effective C快速复习 习惯 C 01 视 C 为一个语言联邦:C、Object-Oriented C、Template C、STL 02 尽量以 const, enum, inline 替换 #define:其实是尽量以编译器替换预处理器比较好,因为 #define 只是简单的字符串匹配替换,编译…...

【华为OD机试真题JAVA】绘图机器的绘图问题
标题:绘图机器的绘图问题| 时间限制:1秒 | 内存限制:262144K | 语言限制:不限 绘图机器的绘图笔初始位置在原点(0,0) 机器启动后按照以下规则来进行绘制直线 1. 尝试沿着横线坐标正向绘制直线 直到给定的终点E 2. 期间可以通过指令在纵坐标轴方向进行偏移 off…...

GPT-4最震撼我的一点
昨天我看了一遍OpenAI发的视频和论文,最震撼我的并不是根据手绘草图生成HTML页面代码,因为草图太简单,对于复杂的有交互的界面,还不知道它的能力究竟如何,能不能生成准确的、清晰的代码,我再实验一下再给大…...

LeetCode-复制带随机指针的链表
题目描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的…...

如何在Unity中实现AStar寻路算法及地图编辑器
文章目录AStar算法简介实现Node节点节点间的估价算法核心邻节点的搜索方式地图编辑器简介实现绘制地图网格障碍/可行走区域地图数据存储AStar算法 简介 Unity中提供了NavMesh导航寻路的AI功能,如果项目不涉及服务端它应该能满足大部分需求,但如果涉及服…...

线性代数之矩阵
一、思维导图二、矩阵及其运算1、矩阵的定义注:零矩阵:元素均为0 的矩阵,通常记作0m*n称为矩阵的类型。满足阶梯形矩阵 行简化的阶梯形矩阵即满足如下条件的矩阵: (1)阶梯形; (2)非零首元所在列其余元素均为0 ; (3) 非…...

【个人首测】百度文心一言 VS ChatGPT GPT-4
昨天我写了一篇文章GPT-4牛是牛,但这几天先别急,文中我测试了用GPT-4回答ChatGPT 3.5 和 Notion AI的问题,大家期待的图片输入也没有出现。 昨天下午百度发布了文心一言,对标ChatGPT,录屏无实机演示让百度股价暴跌。但是晚上百度就…...

基于STM32的ADC采样及各式滤波实现(HAL库,含VOFA+教程)
前言:本文为手把手教学ADC采样及各式滤波算法的教程,本教程的MCU采用STM32F103ZET6。以HAL库的ADC采样函数为基础进行教学,通过各式常见滤波的实验结果进行分析对比,搭配VOFA工具直观的展示滤波效果。ADC与滤波算法都是嵌入式较为…...

Redis高级篇
文章目录面试题库redis有哪些用法?redis单线程时代性能依然很快的原因?主线程和IO线程怎么协作完成请求处理的BigKey(重要)什么算是BigKey?怎么发现BigKey?怎么删除bigkey?bigkey生产调优缓存双…...

sess.close()这句话一般是干什么的,在代码中可以不加么?
sess.close()这句话是用于关闭TensorFlow会话对象的方法。 关闭会话对象可以释放资源,避免内存泄漏,以及清除图中的变量和操作。 在代码中是否可以不加这句话,取决于你是如何创建和使用会话对象的。如果你使用了with语句来创建和管理会话对…...

网络舆情监测处置平台,TOOM舆情如何做好舆情风险点及防控措施?
网络舆情监测处置平台是一个综合性的系统,旨在帮助企业、政府或其他组织有效地管理和处置网络舆情。从多个角度来分析该平台,我们可以考虑以下几个方面: 1,技术实现 网络舆情监测处置平台的技术实现是其核心,它通常采…...

百度文心一言对标 ChatGPT,你怎么看?
文心一言 VS ChatGPT接受不完美 期待进步里程碑意义文心一言初体验✔ 文学创作✔ 商业文案创作✔ 数理逻辑推算✔ 中文理解✔ 多模态生成写在最后何为文心?“文”就是我们中华语言文字中的文,“心”是希望该语言模型可以用心的去理解语言,用心…...

阿里笔试2023-3-15
太菜了,记录一下笔试题目,代码有更好解法欢迎分享。 1、满二叉子树的数量。 给定一颗二叉树,试求这课二叉树有多少个节点满足以该节点为根的子树是满二叉树?满二叉树指每一层都达到节点最大值。 第一行输入n表示节点数量ÿ…...

STM32:TIM定时器输出比较(OC)
一、输出比较简介 1、输出比较 OC(Output Comapre)输出比较输出比较可以通过比较CNT(时基单元)和CCR(捕获单元)寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频…...