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

模拟实现string

目录

1、基本成员变量

2、默认成员函数

构造函数

析构函数

拷贝构造函数(深拷贝)

赋值运算符重载 

3、容量与大小相关的函数

size

capacity 

4、字符串访问相关函数

operator [ ]重载

迭代器 

5、增加的相关函数 

reserve扩容

resize 

push_back追加字符

append追加字符串

operator+= 

insert 

6、删除的相关函数

erase

clear清除数据

7、查找的相关函数

find

8、c_str获取字符串

9、swap交换函数

10、非成员函数

关系运算符函数重载

<<流插入运算符重载

>>流提取运算符重载

getline函数

整型和字符串之间的转换函数


1、基本成员变量

namespace Fan
{//使用命名空间防止我们实现的string与库里的string冲突class string{public://......private:char* _str;      //存储字符串size_t _size;    //有效字符的个数size_t _capacity; //实际存储的有效字符的个数,不包含'\0'const static size_t npos;};const size_t string::npos = -1;
}

2、默认成员函数

构造函数

这里的构造函数我们最好写成全缺省函数,与库里面的构造函数相一致。

//全缺省的默认构造函数
string(const char* str = "") //标准库里string定义对象的默认值为空串""//按声明的顺序进行初始化:_size(strlen(str)), _capacity(_size)
{_str = new char[_capacity + 1]; //在堆上为_str开辟空间,+1是给'\0'预留的strcpy(_str, str); //把常量字符串的内容拷贝过去
}

析构函数

string类里面的_str成员指向的空间是在堆区,堆区的空间不能自动销毁,因此需要我们手动去进行销毁。

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

拷贝构造函数(深拷贝)

首先我们不写时编译器会默认生成一个拷贝构造函数,不过是值拷贝或者浅拷贝,是按照字节拷贝的。

编译器默认生成的拷贝构造函数是浅拷贝,浅拷贝针对日期类是非常合适的,但是像string类这样_str是动态开辟在堆上的。如果我们使用值拷贝会导致:1、同一块空间会析构两次。2、一个对象修改会影响另外一个。因此我们需要进行深拷贝。深拷贝的核心要点在于我和你有一样的值,但是使用的不是同一块空间。

深拷贝有两种写法:传统写法和现代写法。

  • 1、传统写法:

 传统写法就是先开辟一块能够容纳原字符串大小的空间,最后把拷贝的对象的字符串数据拷贝到新开的空间里面。

//拷贝构造函数
//不能用浅拷贝,原因如下:1、析构两次 2、一个对象修改会影响另外一个
// 传统写法
string(const string& s):_size(strlen(s._str)), _capacity(_size)
{_str = new char[_capacity + 1];strcpy(_str, s._str);
}
  • 2、现代写法:

传统写法是自己开空间然后拷贝数据,而现代写法就是创建中间变量。比如我们拿s1作为参数去拷贝构造s2,我们创建一个对象tmp,然后拿s1._str的字符串作为参数给tmp对象完成构造。然后再利用swap函数把tmp对象的_str、_size、_capacity全部与s2的交换即可完成现代方法的深拷贝。不过需要注意的是我们需要把s2的数据置空,避免交换后tmp调用析构函数会出现析构随机值的错误现象。

//现代方法
string(const string& s):_str(nullptr), _size(0), _capacity(0)
{string tmp(s._str); swap(tmp);
}

赋值运算符重载 

  • 思路:

如果我们把s3赋值给s1,这里并不能直接进行赋值,我们需要考虑两个问题。

  1. 如果我s1的空间小于s3,那么直接拷贝过去会导致越界
  2. 如果我s1的空间远大于s3的空间,直接拷贝过去就会导致空间的过度浪费。只有在我s1和s3的空间差不多大的时候,才可以直接进行拷贝。

综上:我们先把s1原先指向的内容delete掉,然后再重新开辟一个和s3一样大的空间,需要注意的是记得多开一个字节,因为还有'\0'字符。我们再利用strcpy把s3的内容拷贝给s1即可。

⭐优化点:

  1. 要避免自己给自己赋值,如若自己给自己赋值,我们直接返回。所以要加上if条件判断
  2. 如果我new失败那么就会导致抛异常,而先前我依旧释放了s1,此时就把s1给破坏了。为了避免这一点,我们可以先开空间再拷贝数据最后释放从而进行优化。
  • 1、传统写法:
//赋值运算符重载--> 深拷贝
//s1=s3 s1.operator=(&s1,s3);
string& operator=(const string& s)
{//防止自己给自己赋值if (this != &s){/*//法一//先删除原先s1的所有空间,防止赋值后s1过大导致空间浪费,s1过小导致空间不够delete[] _str;//给s1开辟与s3等价空间大小,要多开一字节给'\0'_str = new char[strlen(s._str) + 1];strcpy(_str, s._str);*///法二优化//先开辟空间char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;
}
  • 2、现代写法:

这里的现代写法与上文拷贝构造的现代方法没有什么区别。

//现代写法
string& operator=(const string& s)
{if (this != &s){string tmp(s._str);swap(tmp);}return *this;
}

还有一种更加简洁的现代方法,上述写法是引用传参,这里我们可以直接传值传参,让编译器自动调用拷贝构造函数,再把拷贝出来的对象作为右值与左值进行交换。

//法二:简洁版string& operator=(string s) //传值传参调用拷贝构造,s就是s3的深拷贝{swap(s); //交换这两个对象return *this;}

简洁版本的弊端就在于无法避免自己给自己赋值,但很少会出现自己给自己赋值的行为。


3、容量与大小相关的函数

size

直接返回隐含this指针指向的_size即为字符串长度

//返回字符串的长度
size_t size() const //不改变内部成员,最好加上const
{return _size;
}

capacity 

 直接返回隐含this指针指向的_capacity即可

//返回字符串容量
size_t capacity() const //不改变内部成员,最好加上const
{return _capacity;
}

4、字符串访问相关函数

operator [ ]重载

有了operator[ ]运算符重载,我们就可以直接用下标+[ ]进行元素访问,不过这里我们还需要提供一个const版本的operator[ ]运算符重载以便于普通对象和const对象均可以调用而不会出现权限方法的问题。

//版本1
char& operator[](size_t pos) //引用返回,便于后续修改返回的字符
{assert(pos < _size); //确保pos位置的合法性,不能超过字符串return _str[pos]; //返回pos位置字符的引用
}
//版本2
const char& operator[](size_t pos)const
{assert(pos < _size);return _str[pos];
}

迭代器 

string类的迭代器我们可以将其理解为字符指针

  • begin函数的作用就是返回字符串中的第一个字符的地址
  • end函数的作用就是返回字符串最后一个字符的后一个位置地址,即'\0'的地址
//版本1
typedef char* iterator;
iterator begin()
{return _str; //返回第一个有效字符的指针
}
iterator end()
{return _str + _size; //返回最后一个字符后一个位置的地址,即'\0'的地址
}

和上文的operator[ ]重载一样,我们也需要写一个const版本的迭代器,以便于后续的const对象也能够调用。

//版本2:只读,const对象可调用
typedef const char* const_iterator;
const_iterator begin()const
{return _str; //返回第一个有效字符的指针
}
const_iterator end()const
{return _str + _size; //返回最后一个字符后一个位置的地址,即'\0'的地址
}

还有一种基于迭代器的遍历方式:范围for

范围for的底层实现原理和迭代器没两样,只不过看着比较高级。

void test_string()
{Fan::string s1("hello world");//迭代器Fan::string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " "; //h e l l o  w o r l dit++;}cout << endl;//范围forfor (auto& ch : s1) //加上引用,相当于是每个字符的别名,便于修改{ch -= 1;}for (auto& ch : s1){cout << ch << " "; }
}

为了验证范围for是基于迭代器的,我们把模拟迭代器的代码注释掉我们再看下结果:

我们可以看到当我们把模拟迭代器的代码注释掉以后范围for也不起作用了。 


5、增加的相关函数 

reserve扩容

reserve扩容只影响_capacity空间,不影响_size,其有以下两点规则

  1. 当n大于对象当前的capacity时,将capacity扩大到n或者大于n。
  2. 当n小于对象当前的capacity时,无需操作。
//reserve扩容
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1]; //每次开空间一定要多给一个字节给'\0'strcpy(tmp, _str);//释放旧空间delete[] _str;//把新空间赋给_str_str = tmp;//更新容量_capacity_capacity = n;}
}

resize 

resize是将字符串调整为n个字符的长度,不仅会改变_size大小,还会改变_capacity的大小。规则如下:

  1. 如果n小于当前的_size长度,将_size缩小到n
  2. 如果n大于当前的_size长度,将_size扩大到n,扩大的字符默认为'\0'

//resize调整大小
void resize(size_t n, char ch = '\0')
{//如果n<_size,就保留前n个字符即可,把下标n置为'\0'if (n < _size){_size = n;_str[_size] = '\0';}else{//如果n>_capacity,就要扩容if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; i++){//把剩余的字符置为ch_str[i] = ch;}_size = n;_str[_size] = '\0';}
}

push_back追加字符

 首先我们要考虑需不需要扩容,如果需要,我们直接复用reserve函数进行增容,追加字符以后我们要把最后一个下标_size对应的值置为'\0'。

//push_back
void push_back(char ch)
{//法一//先检查是否需要扩容if (_size == _capacity){//复用reserve进行扩容,如果一开始容量为0,记得处理,因为容量*2依旧为0reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0'; //注意最后一个值恒为'\0'以确保字符串的完整性
}

这里我们还可以使用后文写好的insert尾插字符,因为当insert函数中的pos为_size时即为尾插:

//push_back尾插字符
void push_back(char ch)
{//法二:复用insert尾插入字符insert(_size, ch);
}

append追加字符串

使用append追加字符串首先要做的就是判断是否需要扩容,扩容后利用strcpy函数把追加的字符串拷贝到原字符串末尾即可,这里不需要额外处理'\0',因为strcpy默认把'\0'拷贝过去。

//append
void append(const char* str)
{//统计追加字符串后的长度size_t len = _size + strlen(str);//判断是否需要扩容if (len > _capacity){reserve(len);}//把字符串追加到末尾strcpy(_str + _size, str);_size = len;
}

我们这里也可以使用后文的insert追加字符串来完成,因为当pos为_size时,就是在尾部追加字符串。

void append(const char* str)
{//法二:复用insert函数insert(_size, str);
}

operator+= 

operator+=可以追加字符、字符串、对象等。

  • 追加字符:直接复用push_back
//operator+=字符
string& operator+=(char ch)
{//复用push_backpush_back(ch);return *this;
}
  • 追加字符串:直接复用append
//operator+=字符串
string& operator+=(const char* str)
{//复用appendappend(str);return *this;
}

insert 

insert的作用是在指定pos位置往后插入字符或者字符串

  • insert在pos位置插入字符

这里我们首先要判断pos的合法性,然后再进行挪动数据。我们从'\0'位置的下一个位置(_size+1)开始往前挪动。因此定义end指向'\0'后一个位置,当end挪到与pos位置重合时停止,最后把插入的字符ch挪到下标pos处。最后更新_size++。

//insert插入字符
string& insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){//复用reserve进行扩容,如果一开始容量为0,记得处理。否则容量*2依旧为0reserve(_capacity == 0 ? 4 : _capacity * 2);}//依次挪动size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}//当end挪动到pos的位置时停止挪动,并把ch赋值到pos的下标处_str[pos] = ch;_size += 1;return *this;
}

 测试如下:

void test_string()
{Fan::string s("hello world");s.insert(6, '#');s += '#';cout << s.c_str() << endl; //hello #world#for (auto& ch : s){cout << ch << " ";}cout << "$" << endl; //h e l l o  # w o r l d # $s += '\0';for (auto& ch : s){cout << ch << " ";}cout << "$" << endl; //h e l l o  # w o r l d #  $s.insert(0, '#');cout << s.c_str() << endl;//#hello #world#
}
  • insert在pos位置插入字符串

我们首先判断是否需要扩容,接下来挪动数据。定义变量end为_size+len的位置,把pos处往后的字符串整体往后挪动直至空出插入字符串的空间。然后再利用strncpy函数把插入的字符串拷贝过去。

//insert插入字符串
string& insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);if (len == 0){//如果传进来的字符串为空,直接返回即可return *this;}if (_size + len > _capacity){//判断是否扩容reserve(_size + len);}size_t end = _size + len;//当end>=pos+len时都不结束循环while (end >= pos + len){_str[end] = _str[end - len];end--;}//不能使用strcpy,因为会把\0也拷进去,就会出错strncpy(_str + pos, str, len);_size += len;return *this;
}

 测试如下:

void test_string()
{Fan::string s("hello world");s.insert(0, "xxx");cout << s.c_str() << endl; //xxxhello world
}

6、删除的相关函数

erase

如果给定删除的长度len为npos无符号值,或者说len+pos的长度>=_size,那么我们可以直接把pos位置的值设定为'\0'即可,因为此时就是把pos后的数据全部删除。除了此种特殊情况,其余的就是从pos+len处开始往前挪动直到_size+1为止。pos后的数据往前覆盖即可。

//erase删除
void erase(size_t pos, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len >= _size){//此种情况是直接删除pos后的所有数据,直接把pos处设定为'\0'即可_str[pos] = '\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];begin++;}_size -= len;}
}

测试如下:

void test_string()
{Fan::string s("hello world");s.insert(0, "xxx");cout << s.c_str() << endl; //xxxhello worlds.erase(0, 3);cout << s.c_str() << endl; //hello world
}

clear清除数据

clear函数是用来清除原字符串的所有数据,但其空间仍保留。所以我们只需要把下标0的位置置为'\0',并把有效字符个数_size置为0即可。

//clear清除数据
void clear()
{_str[0] = '\0';_size = 0;
}

7、查找的相关函数

find

find函数分为查找字符字符串

  • find查找字符:

我们直接进行遍历即可:

//find查找字符:
size_t find(char ch, size_t pos = 0)
{for (; pos < _size; pos++){if (_str[pos] == ch)return pos;}//没有找到就返回npos,-1return npos; //-1
}
  • find查找字符串:

这里我们可以直接复用C语言的strstr函数进行查找,不过该函数返回的是地址,我们想要获得下标直接利用地址相减即可。

//find查找字符串:
size_t find(const char* str, size_t pos = 0)
{//直接复用C语言函数strstr即可,strstr函数返回的是地址const char* p = strstr(_str + pos, str); if (p == nullptr){return npos;}else{//返回下标直接用p-str即可return p - _str;}
}

8、c_str获取字符串

 c_str用于获取C类型的字符串,直接返回字符串首地址。

//c_str 获取C形式的字符串
const char* c_str()const //最好加上const,便于普通对象和const对象均可调用
{return _str;
}

9、swap交换函数

swap函数用于交换两个对象的数据,我们可以通过复用库里面的swap函数来完成,但是要在前面加上作用域限定符"::"。让编译器在全局域的库里调用swap函数。

//swap交换函数
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}

10、非成员函数

关系运算符函数重载

关系运算符有==、!=、<、<=、>、>=这6类,我们在先前的日期类中已经讲过类似的。这里的关系运算符重载我们不把它放到成员函数里面。

我们直接复用库里面的strcmp函数进行字符串大小比较即可。此外,和日期类一样,在写好<和==的重载后,剩下的4个关系运算符我们直接进行复用即可。

//1.operator<
bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}
//2.operator==
bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}

剩下4个关系运算符我们直接复用上面两个:

//3、operator<=
bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}
//4、operator>
bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}
//5、operator>=
bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}
//6、operator!=
bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}

<<流插入运算符重载

 这里我们可以通过范围for来完成<<运算符的重载

//流插入运算符重载
ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}

>>流提取运算符重载

这里实现的过程中我们要注意的是当遇到空格或者换行符就要停止读取。此外,在一开始要记得调用clear函数把原字符串的所有数据进行清空。然后再正常往后输入数据,否则新数据累加到原数据后面,就达不到预期效果。

//流提取运算符重载
istream& operator>>(istream& in, string& s)
{//法一//先把原字符串里的数据清空才可以输入新的数据s.clear();char ch;ch = in.get(); //使用get()函数才能获取空格或者换行字符while (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;
}

这里有一个缺陷,如果我们频繁输入大量字符就会导致多次扩容,扩容会在效率上有所损耗,因此我们可以提前开辟一个128字节大小的数组,我们把每次输入的字符放到数组里面,最后遇到停止的符号时我们就把字符+=到字符串s上。如若下标加到127,我们就把数组的字符+=到字符串s上,然后重置数组为'\0',更新下标为0即可。

//流提取运算符重载
istream& operator>>(istream& in, string& s)
{//法二//先把原字符串里的数据清空才可以输入新的数据s.clear();char ch;ch = in.get(); //使用get()函数才能获取空格或者换行字符char buff[128] = { '\0' };size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){s += buff;memset(buff, '\0', 128);i = 0;}ch = in.get();}s += buff;return in;
}

getline函数

getline函数与上述写的<<流提取运算符重载非常的相似,唯一不同的地方在于getline只有在遇到换行符才停止读取,而<<在遇到换行符停止外,遇到空格也会停止读取。

//getline函数
istream& getline(istream& in, string& s)
{s.clear();char ch;ch = in.get();//getline函数只有在遇到换行符才会停止while (ch != '\n'){s += ch;ch = in.get();}return in;
}

整型和字符串之间的转换函数

  • stoi和to string

 代码演示:

void test_string()
{int i;cin >> i;string s = to_string(i);int val = stoi(s);
}

相关文章:

模拟实现string

目录 1、基本成员变量 2、默认成员函数 构造函数 析构函数 拷贝构造函数(深拷贝) 赋值运算符重载 3、容量与大小相关的函数 size capacity 4、字符串访问相关函数 operator [ ]重载 迭代器 5、增加的相关函数 reserve扩容 resize push_back追加字符 appe…...

自监督表征预训练之掩码图像建模

自监督表征预训练之掩码图像建模 前言 目前&#xff0c;在计算机视觉领域&#xff0c;自监督表征预训练有两个主流方向&#xff0c;分别是对比学习&#xff08;contrastive learning&#xff09;和掩码图像建模&#xff08;masked image modeling&#xff09;。两个方向在近几…...

华为OD机试题 - 磁盘容量(JavaScript)| 代码+思路+重要知识点

最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 使用说明 参加华为od机试,一定要注意不要完全背…...

ChatGPT:“抢走你工作的不会是 AI ,而是先掌握 AI 能力的人”

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; ChatGPT&#xff1a;“抢走你工作的不会是 AI &#xff0c;而是先掌握 AI 能力的人” ChatGPT&#xff1a;美国OpenAI 研发的聊天机器人程序&#xff0c;人工智能技术…...

数据结构与算法(Java版) | 线性结构和非线性结构

之前&#xff0c;我们说过&#xff0c;数据结构是算法的基础&#xff0c;因此接下来在这一讲我就要来给大家重点介绍一下数据结构了。 首先&#xff0c;大家需要知道的是&#xff0c;数据结构包括两部分&#xff0c;即线性结构和非线性结构。知道这点之后&#xff0c;接下来我…...

电商数据查询平台:母婴行业妈妈用品全网热销,头部品牌格局初现

以往&#xff0c;奶粉、纸尿裤这类产品基本就代表了整体母婴市场中的消费品。而如今&#xff0c;随着母婴行业的高速发展和消费升级&#xff0c;母婴商品的种类日益丰富&#xff0c;需求也不断深入。 在京东平台&#xff0c;母婴大品类中除了包含婴童相关的食品&#xff08;奶粉…...

STM32模拟SPI协议获取24位模数转换(24bit ADC)芯片AD7791电压采样数据

STM32模拟SPI协议获取24位模数转换&#xff08;24bit ADC&#xff09;芯片AD7791电压采样数据 STM32大部分芯片只有12位的ADC采样性能&#xff0c;如果要实现更高精度的模数转换如24位ADC采样&#xff0c;则需要连接外部ADC实现。AD7791是亚德诺(ADI)半导体一款用于低功耗、24…...

华为OD机试题 - 交换字符(JavaScript)| 代码+思路+重要知识点

最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 使用说明 参加华为od机试,一定要注意不要完全背…...

最好的工程师像投资者一样思考,而不是建设者

我在大学期间住在图书馆。“我学习的教科书理论越多&#xff0c;我就会成为一名更好的工程师&#xff0c;”我想。然而&#xff0c;当我开始工作时&#xff0c;我注意到业内最优秀的工程师并不一定比应届毕业生了解更多的理论。他们只是带来了不同的心态&#xff0c;即投资者的…...

Mysql里的ibtmp1文件太大,导致磁盘空间被占满

目录 一、查看磁盘的时候发现磁盘空间100% 二、 排查的时候&#xff1a;查看是什么文件占用的时候&#xff0c;发现是数据库临时表空间增长的 三、为了避免以后再次出现ibtmp1文件暴涨&#xff0c;限制其大小&#xff0c;需在配置文件加入 四、重启Mysql实例&#xff08;重启后…...

android kotlin 协程(四) 协程间的通信

android kotlin 协程(四) 协程间的通信 学完本篇你将会了解到: channelproduceactorselect 先来通过上一篇的简单案例回顾一下挂起于恢复: fun main() {val waitTime measureTimeMillis {runBlocking<Unit> {println("main start") // 1 // …...

苹果手机通讯录突然没了怎么恢复?

手机成为生活中的必需品&#xff0c;都会存储着各种数据文件&#xff0c;比如我们使用过的APP、音乐、照片、通讯录等通常都是存在这里面的。但我们的操作难免会有意外&#xff0c;有的是手动不小心删的&#xff0c;有的是误删的&#xff0c;有的是自己孩子删的等&#xff0c;却…...

BI知识全解,值得收藏

2021年度&#xff0c;中国商业软件市场的增长趋势是快速增长的&#xff0c;达到7.8亿美元&#xff0c;同比增长34.9%。商业智能BI在企业应用中具有巨大的价值&#xff0c;并逐渐成为现代企业信息化和数字化转型的基础。所以&#xff0c;全面了解BI&#xff0c;对于企业管理是非…...

【机器学习】GBDT

1.什么是GBDT GBDT(Gradient Boosting Decision Tree)&#xff0c;梯度提升树。它是一种基于决策树的集成算法。其中Gradient Boosting 是集成方法boosting中的一种算法&#xff0c;通过梯度下降来对新的学习器进行迭代。它是利用损失函数的负梯度方向在当前模型的值作为残差的…...

C#开发的OpenRA游戏高性能内存访问的方法

C#开发的OpenRA游戏高性能内存访问的方法 一个游戏性能往往是比较关键的, 因为游戏很多时候是比拼的是人的速度和技巧。 比如王者荣耀里,一个大招是否及时地放得出来,就会影响到一场比赛的关键。 而这个大招的释放,又取决于游戏运行在手机上的性能。 如果游戏太耗性能,导致…...

【elasticsearch】elasticsearch es读写原理

一、前言&#xff1a; 今天来学习下 es 的写入原理。 Elasticsearch底层使用Lucene来实现doc的读写操作&#xff1a; Luence 存在的问题&#xff1a; 没有并发设计 lucene只是一个搜索引擎库&#xff0c;并没有涉及到分布式相关的设计&#xff0c;因此要想使用Lucene来处理海量…...

数据在内存中的存储【上篇】

文章目录⚙️1.数据类型的详细介绍&#x1f529;1.1.类型的基本归类⚙️2.整型在内存中的存储&#x1f529;2.1.原码、反码、补码&#x1f529;2.2.大小端的介绍⚙️1.数据类型的详细介绍 &#x1f973;基本的内置类型 &#xff1a; &#x1f4a1;char ---------- 字符数据类型…...

慕了没?3年经验,3轮技术面+1轮HR面,拿下字节30k*16薪offer

前段时间有个朋友出去面试&#xff0c;这次他面试目标比较清晰&#xff0c;面的都是业务量大、业务比较核心的部门。前前后后去了不少公司&#xff0c;几家大厂里&#xff0c;他说给他印象最深的是字节3轮技术面1轮HR面&#xff0c;他最终拿到了30k*16薪的offer。第一轮主要考察…...

「可信计算」与软件行为学

可信计算组织&#xff08;Ttrusted Computing Group,TCG&#xff09;是一个非盈利的工业标准组织&#xff0c;它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立&#xff0c;并采纳了由可信计算平台联盟&#xff08;the Trusted Computing Platform Alli…...

华为OD机试题 - 找字符(JavaScript)| 代码+思路+重要知识点

最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 使用说明 参加华为od机试,一定要注意不要完全背…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...