C++【string类,模拟实现string类】

  🌟个人主页:落叶

  🌟当前专栏:  C++专栏


目录

为什么学习string类

C语言中的字符串

标准库中的string类

auto和范围for

auto关键字

 迭代器

范围for

string类的常用接口说明和使用

1. string类对象的常见构造

2.string类对象的容量操作

3.string类对象的访问及遍历操作  

4. string类对象的修改操作  

【push_back】在字符串后尾插字符c

 【append】在字符串后追加一个字符串

【find + npos(重 点)】从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置

【rfind】从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置。

【erase】从字符串中删除字符

5.string类非成员函数

【operator+】尽量少用,因为传值返回,导致深拷贝效率低

【operator>> (重点)】输入运算符重载

【operator<< (重点)】输出运算符重载

【getline (重点)】获取一行字符串

【relational operators (重点)】大小比较

string类的模拟实现

经典的string类问题

 浅拷贝

 深拷贝

浅拷贝(Shallow Copy)

深拷贝(Deep Copy)

写时拷贝(了解)



为什么学习string类

C语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列 的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户 自己管理,稍不留神可能还会越界访问。

标准库中的string类

在使用string类时,必须包含#include头文件以及using namespace std;

auto和范围for

auto关键字

在这里补充2个C++11的小语法,方便我们后面的学习。

  • 在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个 不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期 推导而得。
  • 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
  • auto不能直接用来声明数组

auto声明的变量编译器会推导而得类型。

这个typeid是获取变量的真实的类型,

我们可以看到类型是对得上的。

不能没有值

// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项auto e;

 auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

 

auto不能用来声明数组

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。


 迭代器

https://legacy.cplusplus.com/reference/string/string/begin/

迭代器类似于指针,它指向容器中的一个元素,并且可以通过递增或递减来移动到下一个或前一个元素。迭代器提供了一组操作,如解引用(*),递增(++),递减(--),比较(==, !=)等。

add.begin()是指向第一个元素,

add.end()是指向最后一个元素,

通过i遍历每个字符

在这个例子中,add.begin()返回一个指向vec第一个元素的迭代器,add.end()返回一个指向add最后一个元素之后位置的迭代器。循环中,i被用来遍历add中的元素,*i用来访问当前元素的值。

迭代器使得算法可以与容器解耦,提高了代码的复用性和灵活性。


范围for

  • 对于一个有范围的集合言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围 内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

范围for底层就是迭代器,范围for可以作用到数组和容器对象上进行遍历。


为什么要在auto后面加引用呢?

通过引用遍历可以避免不必要的拷贝构造函数的调用,从而提高性能。

这样做有几个优点:

性能优化如果容器中的元素很大,或者元素是复杂类型(如类对象),通过引用遍历可以避免不必要的拷贝构造函数的调用,从而提高性能。

修改元素如果需要在循环中修改容器中的元素,必须通过引用来遍历它们。如果不使用引用,循环变量将是容器中元素的一个副本,对

副本的修改不会影响到原始容器中的元素。

举个例子,auto& 告诉编译器,num 是一个引用 add容器中的实际元素,而不是一个副本。因此,如果对 num 的修改会直接 add中的元素。


string类的常用接口说明和使用

在C++中,std::string 类是标准库的一部分,它提供了一个方便的接口来处理字符串。std::string 位于 <string> 头文件中,并且是 std 命名空间的一部分。

#include<string>

1. string类对象的常见构造

https://legacy.cplusplus.com/reference/string/string/string/

(constructor)函数名称功能说明
string() (重点构造空的string类对象,即空字符串
string(const char* s) (重点用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点拷贝构造函数

 

#include<iostream>
using namespace std;
#include<string>int main()
{string s1;         //构造空的string对象string s2("hello");//用字符串构造string对象string s3(10, 'w');//用10个w构造string对象string s4(s2);     //拷贝构造函数return 0;
}

2.string类对象的容量操作

函数名称 功能说明
size(重点)返回字符串有效字符长度
capacity 返回空间总大小
empty (重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间**
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充

size我们可以看到字符串有效字符长度是5

https://legacy.cplusplus.com/reference/string/string/size/


 capacity返回空间总大小

https://legacy.cplusplus.com/reference/string/string/capacity/

明明只有4个字符,空间大小怎么会是15呢?

因为底层有个16大小的数组存放数据,

当我们给满16个字符,会自动扩容到32大小的空间。


empty(重点)检测字符串释放为空串,是返回true,否则返回false

https://legacy.cplusplus.com/reference/string/string/empty/

是空字符串返回true就是1,不是空字符串返回false就是0


clear (重点)清空有效字符

https://legacy.cplusplus.com/reference/string/string/clear/

我们可以看到清空了有效字符,但是空间大小不变。


reserve (重点)为字符串预留空间**

https://legacy.cplusplus.com/reference/string/string/reserve/

reserve 也就是扩容空间的意思。

数据很多的话,我们可以提前扩容空间避免多次2倍扩容。

 


 resize (重点)将有效字符的个数改成n个,多出的空间用\0填充

https://legacy.cplusplus.com/reference/string/string/resize/

字符不变,多出来的空间都用\0填充。

这里是3,abc保留,cdef被清除。


注意: 1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接 口保持一致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不 同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数 增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参 数小于string的底层空间总大小时,reserver不会改变容量大小。


3.string类对象的访问及遍历操作  

函数名称功能说明
operator[] (重 点)返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

 operator[] (重 点)返回pos位置的字符,const string类对象调用】

https://legacy.cplusplus.com/reference/string/string/operator[]/


rbegin + rend     begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器。

https://legacy.cplusplus.com/reference/string/string/rbegin/

正向迭代器是iterator,反向迭代器是reverse_iterator。

reverse_iterator 是标准库提供的一种迭代器适配器,它允许我们以逆序的方式遍历容器。reverse_iterator 的设计是为了简化反向遍历容器的过程,使得我们可以像使用普通迭代器一样使用它,但方向相反。

以下是使用 reverse_iterator 的几个主要原因:

1.简化代码使用 reverse_iterator 可以避免手动计算容器的结束位置和开始位置之间的偏移量,从而简化代码。

2.一致性它提供了一种与普通迭代器使用方式一致的方法来反向遍历容器,使得代码更加直观和易于理解。

3.与算法兼容许多标准库算法都设计为与迭代器一起工作。通过使用 reverse_iterator,我们可以将这些算法应用于容器的反向遍历,而无需修改算法本身。

4.安全性使用 reverse_iterator 可以减少由于手动计算迭代器位置而可能引入的错误。


4. string类对象的修改操作  

函数名称
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重 点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重 点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
erase从字符串中删除字符 

【push_back】在字符串后尾插字符c

https://legacy.cplusplus.com/reference/string/string/push_back/


 【append】在字符串后追加一个字符串

https://legacy.cplusplus.com/reference/string/string/append/

abcdefg插入了1111的后面

 

【operator+= (重 点)】 在字符串后追加字符串str

https://legacy.cplusplus.com/reference/string/string/operator+=/

可以+=字符串和字符

也可以+=一个对象


 【c_str(重点)】返回C格式字符串

https://legacy.cplusplus.com/reference/string/string/c_str/

可以返回字符串


【find + npos(重 点)】从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置

https://legacy.cplusplus.com/reference/string/string/find/

在字符串中查找内容

下面我们可以看到,从0下标位置开始查找9这个字符,找到了返回9的下标,没有找到返回-1

没有找到交换返回-1


【rfind】从字符串pos位置开始往前找字符c,返回该字符在字符串中的 位置。

https://legacy.cplusplus.com/reference/string/string/rfind/

我们不写下标,默认从最后一个下标开始往前查找。

找到了返回下标,没有找到返回-1。


【substr 】 在str中从pos位置开始,截取n个字符,然后将其返回

https://legacy.cplusplus.com/reference/string/string/substr/

从3下标开始截取4个字符,给s2。

我们也可以用find查找a这个下标, 然后通过i下标这个位置截取3个字符。


【erase】从字符串中删除字符

https://legacy.cplusplus.com/reference/string/string/erase/

下面我们可以看到,从下标1删除到下标5,就只剩下a和g了。


5.string类非成员函数

函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>> (重点)输入运算符重载
operator<< (重点)输出运算符重载
getline (重点)获取一行字符串
relational operators (重点)大小比较

【operator+】尽量少用,因为传值返回,导致深拷贝效率低

https://legacy.cplusplus.com/reference/string/string/operator+/

我们可以看到s1是您好,s2是落叶,加起来就是您好落叶。

我们也可以加字符串


【operator>> (重点)】输入运算符重载

https://legacy.cplusplus.com/reference/string/string/operator%3E%3E/

可以往s1对象,输入字符串,

但是遇到空格就会停止


【operator<< (重点)】输出运算符重载

https://legacy.cplusplus.com/reference/string/string/operator%3C%3C/


【getline (重点)】获取一行字符串

https://legacy.cplusplus.com/reference/string/string/getline/

这个它遇到空格不会停止,会继续往后读取字符串,也会把空格给读取了。


【relational operators (重点)】大小比较

https://legacy.cplusplus.com/reference/string/string/operators/

计算比较大小,如果大于为真,返回1,为假返回0。


string类的模拟实现

经典的string类问题

上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让 学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析 构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:/*String():_str(new char[1]){*_str = '\0';}*///String(const char* str = "\0") 错误示范//String(const char* str = nullptr) 错误示范String(const char* str = ""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if (nullptr == str){assert(false);return;}_str = new char[strlen(str) + 1];strcpy(_str, str);}~String(){if (_str){delete[] _str;_str = nullptr;}}
private:char* _str;
};
// 测试
void TestString()
{String s1("hello bit!!!");String s2(s1);
}

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一 不想分享就你争我夺,玩具损坏。

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父 母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给 出。一般情况都是按照深拷贝方式提供。

浅拷贝(Shallow Copy)

浅拷贝通常指的是对象之间的简单位拷贝(bitwise copy),这意味着新对象和原对象将共享相同的资源。在C++中,如果你没有显式地定义拷贝构造函数和赋值运算符,编译器会为你生成默认的版本,这些默认版本执行的是浅拷贝。

对于 std::string 来说,浅拷贝是不存在的,因为 std::string 内部管理着自己的动态内存。当你进行拷贝构造或赋值操作时,std::string 会执行深拷贝。这意味着新创建的字符串对象拥有自己独立的内存空间,与原字符串对象不共享任何资源。

深拷贝(Deep Copy)

深拷贝指的是创建一个新对象,并且递归地复制原对象中的所有元素,使得新对象和原对象完全独立。在 std::string 的情况下,每次你进行拷贝构造或赋值操作时,都会执行深拷贝


 stringl类模拟实现【代码】

string.h

#define _CRT_SECURE_NO_WARNINGS 1#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace bit
{class string{public://迭代器typedef char* iterator;static const int npos;string(const char* str = "");string(const string& s);~string();///扩容void reserve(size_t n);//尾插字符void push_back(char ch);//尾插字符串void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);//传统string& operator=(const string& s);//现代string& operator=(string s);//清除字符void clear(){_str[0] = '\0';_size = 0;}//首字符iterator begin(){return _str;}//最后的iterator end(){return _str + _size;}//返回_strchar* c_str() const{return _str;}//size_t size()const{return _size;}size_t capacity()const{return _capacity;}//指定位置修改字符char& operator[](size_t index){assert(index < _size);return _str[index];}//指定位置修改字符const char& operator[](size_t index)const{assert(index < _size);return _str[index];}////查询字符size_t find(char ch, size_t pos = 0);//查询字符串size_t find(const char* str, size_t pos = 0);//指定位置插入字符string& insert(size_t pos, char c);//指定位置插入字符串string& insert(size_t pos, const char* str);// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len);//调整字符串大小void resize(size_t n, char c = '\0');//判断字符串是不是空bool empty()const;//字符串交换void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}void swap(string& s, string& s1){s.swap(s1);}//流插入friend ostream& operator<<(ostream& _cout, const bit::string& s);//流提取friend istream& operator>>(istream& _cin, bit::string& s);private:char* _str;size_t _size;size_t _capacity;};//判断大于-小于-大于等于 - 小于等于 - 等于 - 不等于bool operator<(const string& lhs, const string& rhs);bool operator>(const string& lhs, const string& rhs);bool operator<=(const string& lhs, const string& rhs);bool operator>=(const string& lhs, const string& rhs);bool operator==(const string& lhs, const string& rhs);bool operator!=(const string& lhs, const string& rhs);
}

string.cpp

#include"string.h"namespace bit
{const int string::npos = -1;//构造string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1];strcpy(_str, str);}深拷贝-(传统写法)//string::string(const string& s)//{//	_str = new char[s._capacity + 1];//	strcpy(_str, s._str);//	_size = s._size;//	_capacity = s._capacity;//}//深拷贝-(现代写法)string::string(const string& s){string tmp(s._str);swap(tmp);}//析构string::~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}/////扩容void string::reserve(size_t n){cout << "reserve:"<< n << endl;if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}//尾插字符void string::push_back(char ch){if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;}//尾插字符串void string::append(const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){size_t tmp = _capacity * 2;if (tmp < (_size + len)){tmp = (_size + len);}reserve(tmp);}strcpy(_str + _size, str);_size += len;}string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return  *this;}赋值-(传统写法)//string& string::operator=(const string& s)//{//	if (this != &s)//	{//		delete[] _str;//		_str = new char[s._capacity + 1];//		strcpy(_str, s._str);//		_size = s._size;//		_capacity = s._capacity;//	}//	return *this;//}//赋值-(现代写法) string& string::operator=(string s){swap(s);return *this;}//查询字符size_t string::find(char ch, size_t pos){assert(pos < _size);for (size_t i = pos; i < _size; i++){if (ch == _str[i]){return i;}}return npos;}//查询字符串size_t string::find(const char* str, size_t pos){assert(pos < _size);const char* tmp = strstr(_str + pos, str);if (tmp == __nullptr){return npos;}return tmp - _str;}//在字符串里插入字符string& string::insert(size_t pos, char ch){assert(pos < _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}//循环移动字符int i = _size;while ((int)pos <= i){_str[i + 1] = _str[i];i--;}//在pos位置插入字符_str[pos] = ch;_size++;return *this;}//指定位置插入字符串string& string::insert(size_t pos, const char* str){size_t len = strlen(str);if ((_size + len) > _capacity){size_t tmp = _capacity * 2;if (tmp < (_size + len)){tmp = (_size + len);}reserve(tmp);}//循环往后移动字符size_t i = _size + len;while (pos < i){_str[i] = _str[i - len];i--;}//循环把str的字符一个一个,赋值给_str[pos+i]的位置for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size + len;return *this;}//指定位置删除字符串string& string::erase(size_t pos, size_t len){if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{size_t i = pos + len;while (i <= _size){_str[i - len] = _str[i];i++;}_size = _size - len;}return *this;}//判断大于-小于-大于等于 - 小于等于 - 等于 - 不等于bool operator<(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) < 0;}bool operator>(const string& lhs, const string& rhs){return !(lhs <= rhs);}bool operator<=(const string& lhs, const string& rhs){return (lhs < rhs) || (lhs == rhs);}bool operator>=(const string& lhs, const string& rhs){return !(lhs < rhs);}bool operator==(const string& lhs, const string& rhs){return strcmp(lhs.c_str(), rhs.c_str()) == 0;}bool operator!=(const string& lhs, const string& rhs){return !(lhs == rhs);}//调整字符串大小void string::resize(size_t n, char c){_str[n] = c;}//删除pos位置上的元素,并返回该元素的下一个位置bool string::empty()const{if (_size != 0){return false;}else{return true;}}//流插入ostream& operator<<(ostream& _cout, const bit::string& s){for (size_t i = 0; i < s._size; i++){_cout << s._str[i];}return _cout;}//流提取istream& operator>>(istream& _cin, bit::string& str){str.clear();int i = 0;char buff[256];char ch;ch = _cin.get();while (ch != ' ' && ch != '\n'){buff[i++] += ch;if (i == 255){buff[i] = '\0';str += buff;i = 0;}ch = _cin.get();}if (i > 0){buff[i] = '\0';str += buff;}return _cin;}}

test.cpp【测试】

#include"string.h"//int main()
//{
//	bit::string n("asdasdf");
//	n += 'w';
//	cout << n.c_str() << endl;
//}//int main()
//{
//	bit::string n("asfsddfgdf");
//	//迭代器
//	bit::string::iterator i = n.begin();
//	while (i != n.end())
//	{
//		cout << *i << endl;
//		i++;
//	}
//}//int main()
//{
//	bit::string n("qweeqqqw");
//	//n.clear();
//	//cout << n.c_str() << endl;
//	n.insert(0, "11111111");
//	cout << n.c_str() << endl;
//}//int main()
//{
//	bit::string n("11223344556677");
//	/*n.erase(4, 99);*/
//	n[2] = 'x';
//	cout << n.c_str() << endl;
//}//int main()
//{
//	bit::string n("11223344556677");
//	bit::string s1(n);
//	cout << (n < s1) << endl;
//	cout << (n > s1) << endl;
//	cout << (n <= s1) << endl;
//	cout << (n >= s1) << endl;
//	cout << (n == s1) << endl;
//	cout << (n != s1) << endl;
//
//}//int main()
//{
//	bit::string n("11223344556677");
//	/*n.resize(8,'y');
//	n.resize(8);
//	cout << n.c_str() << endl;*/
//	
//	cout << n.empty() << endl;
//}//int main()
//{
//	string n("1122");
//	string s1("qqwee");
//	n.swap(s1);
//	cout << n << endl;
//	cout << s1 << endl;
//}int main()
{bit::string n("112233");bit::string n1 = n;//bit::string s1("qweqwer");//n.swap(s1);//cout << n << endl;//cout << s1 << endl;/*cin >> n;cout << n << endl;*///getline(cin, n);//n = n1;//swap(n, n1);//swap(n,n1);//cout << n << endl;cout << n1 << endl;
}

写时拷贝(了解)

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该 资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源, 如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有 其他对象在使用该资源。  

写时拷贝

写时拷贝在读取是的缺陷

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

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

相关文章

android——jetpack startup初始化框架

一、jetpack startup Android Jetpack Startup是一个库&#xff0c;它简化了Android应用启动过程&#xff0c;尤其是对于那些需要处理复杂数据绑定和初始化逻辑的应用。它的核心在于提供了一个StartupComponent&#xff0c;用于声明应用的初始化逻辑&#xff0c;这个逻辑会在首…

A019基于SpringBoot的校园闲置物品交易系统

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

【赵渝强老师】Redis的RDB数据持久化

Redis 是内存数据库&#xff0c;如果不将内存中的数据库状态保存到磁盘&#xff0c;那么一旦服务器进程退出会造成服务器中的数据库状态也会消失。所以 Redis 提供了数据持久化功能。Redis支持两种方式的持久化&#xff0c;一种是RDB方式&#xff1b;另一种是AOF&#xff08;ap…

Excel:vba实现批量插入图片批注

实现的效果&#xff1a;实现的代码如下&#xff1a; Sub InsertImageNamesAndPictures()Dim PicPath As StringDim PicName As StringDim PicFullPath As StringDim RowNum As IntegerDim Name As StringDim Comment As CommentDim folder As FileDialog 定义文件选择对话框 清…

tomcat启动失败和缓存清理办法

tomcat只在学校接触过并且是在window xp和win7的电脑上配置过&#xff08;中途升级过电脑系统&#xff09;&#xff0c;只记得在windows系统上可以将其设置成服务管理。但我已毕业10多年了&#xff0c;学的知识早就不知道丢哪里了。这次为了修改一个07&#xff0c;08年的项目&a…

SpringBoot使用ApplicationContext.getBean启动报空指针处理记录

问题&#xff1a;项目启动报空指针 定位&#xff1a;新增filter中init方法使用getbean控制 解决&#xff1a;在新增filter上加注解 DependsOn({"applicationContextUtils"}) Component DependsOn({"applicationContextUtils"})//此处解决空指针问题 pu…

ReactPress:深入解析技术方案设计与源码

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎提出宝贵的建议&#xff0c;欢迎一起共建&#xff0c;感谢Star。 ReactPress是一个基于React框架开发的开源发布平台&#xff0c;它不仅仅是一个简单的博客系统&#xff0c;更是一个功能全…

华为OD机试真题-用户调度问题-2024年OD统一考试(E卷)

最新华为OD机试考点合集:华为OD机试2024年真题题库(E卷+D卷+C卷)_华为od机试题库-CSDN博客 每一题都含有详细的解题思路和代码注释,精编c++、JAVA、Python三种语言解法。帮助每一位考生轻松、高效刷题。订阅后永久可看,发现新题及时跟新。 题目描述 在通信系统中,一…

Docker 基础命令简介

目录 Docker 基础命令 1. Docker 版本信息 2. 获取 Docker 帮助 3. 列出所有运行中的容器 4. 运行一个新的容器 5. 查看容器日志 6. 停止容器 7. 启动已停止的容器 8. 删除容器 9. 列出所有镜像 10. 拉取镜像 11. 构建镜像 12. 删除镜像 13. 执行命令 14. 查看容…

A20红色革命文物征集管理系统

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

系统安全架构

一个完整的信息安全系统至少包含三类措施: 技术方面的安全措施&#xff0c; 管理方面的安全措施 相应的政策法律。 网络安全威胁 授权侵犯&#xff1a;为某一特权使用一个系统的人却将该系统用作其他未授权的目的。假冒&#xff1a;一个实体(人或系统)假装成另一个实体非法…

先锋精科委身芯片“圈子” 引致交易不公允和信披不透明

不要违背圈子的规则&#xff0c;但也不要盲从圈子的规则。 ——语出马云。 引 言 “圈子”是钥匙&#xff0c;也是一把锁。 走进“圈子”&#xff0c;将获得包括资金、订单、货源、技术等企业发展所需的资源&#xff0c;能够助推一家企业乃至整个行业的跨越式发展&#…

MinerU容器构建教程

一、介绍 MinerU作为一款智能数据提取工具&#xff0c;其核心功能之一是处理PDF文档和网页内容&#xff0c;将其中的文本、图像、表格、公式等信息提取出来&#xff0c;并转换为易于阅读和编辑的格式&#xff08;如Markdown&#xff09;。在这个过程中&#xff0c;MinerU需要利…

【划分型 DP-最优划分】【腾讯笔试压轴】【hard】力扣132. 分割回文串 II

给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文串。 返回符合要求的 最少分割次数 。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;1 解释&#xff1a;只需一次分割就可将 s 分割成 [“aa”,“b”] 这样两个回文子…

数学建模学习(135):使用Python基于WSM、WPM、WASPAS的多准则决策分析

1. 算法介绍 多标准决策分析(Multi-Criteria Decision Analysis, MCDA)是帮助决策者在复杂环境下做出合理选择的重要工具。WSM(加权和法)、WPM(加权乘积法)、WASPAS(加权和乘积评估法)是 MCDA 中的三种常用算法。它们广泛应用于工程、经济、供应链管理等多个领域,用于…

Django前后端分离基本流程

Django前后端分离项目基础流程介绍 前后端分离是一种架构模式&#xff0c;其中前端和后端分别独立开发和部署&#xff0c;它们通过API进行通信。在Django项目中实现前后端分离&#xff0c;可以提高开发效率和项目的可维护性。 以下是实现Django前后端分离项目的基本流程&#…

【论文复现】基于深度学习的手势识别算法

本文所涉及所有资源均在这里可获取。 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐、摄影的一位博主。 &#x1f4d7;本文收录于论文复现系列&#xff0c;大家有兴趣的可以看一看…

使用QtWebEngine的Mac应用如何发布App Store

前言 因为QtWebEngine时第三方包,苹果并不直接支持进行App Store上签名和发布,所以构建和发布一个基于使用QtWebEngine的应用程序并不容易,这里我们对Qt 5.8稍微做一些修改,以便让我们的基于QtWeb引擎的应用程序并让签名能够得到苹果的许可。 QtWebEngine提供了C++和Qml的…

智能新纪元:人工智能技术的社会影响与伦理挑战-亿发

在数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;正以其不可阻挡之势&#xff0c;深刻改变着我们的生产、生活和学习方式。它不仅是一项技术革命&#xff0c;更是推动社会进步的重要力量。本文将探讨人工智能如何重塑未来&#xff0c;以及它所带来的深远影响。 AI…

云平台虚拟机运维笔记整理,使用libvirt创建和管理虚拟机,以及开启虚拟机嵌套,虚拟磁盘扩容,物理磁盘扩容等等

云平台虚拟机运维笔记整理,使用libvirt创建和管理虚拟机,以及开启虚拟机嵌套,虚拟磁盘扩容,物理磁盘扩容等等。 掌握和使用qemu和libvirt,分别使用它们创建一个cirros虚拟机,并配置好网络。 宿主机node0的系统为ubuntu16,IP为192.168.56.200。 qemu和libvirt简介 QEMU…