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

【C++】string类模拟实现

目录

💕1.模拟string类构造函数

💕2.模拟构造函数实现

💕3.拷贝构造函数模拟实现 

💕4.析构函数模拟实现 

💕5.size函数,capacity函数模拟实现 

💕6.begin函数,end函数,模拟实现 

💕7.=赋值运算符重载模拟实现

💕8.访问运算符重载 模拟实现

💕9.reserve函数模拟实现 

💕10.push_back函数模拟实现

💕11.append函数模拟实现

💕 12.+=运算符重载

💕13.find函数模拟实现 

💕14.insert函数指定位置插入字符模拟实现

💕15.insert函数指定位置插入字符串模拟实现

💕16.erase函数模拟实习 

💕17.判断运算符重载

💕18.输出/输入运算符重载

💕19.clear函数 

💕20.总体代码

💕21.完结 


(最新更新时间——2025.1.23——各位小年快乐) 

不过是卑贱的下人 杀了就杀了 

💕1.模拟string类构造函数

我们知道string在C++中是一个单独的名,如果我们想要模拟string类的实现,我们就需要使用命名空间域将string重命名

那么在.h文件中我们可以这样写


blogstring.h的初始模板

#pragma once
#include<iostream>
include<assert.h>
using namespace std;
namespace blog
{class string{private:char* _str;//字符串首地址size_t _size;//有效个数size_t _capacity;//容量};
}

将string的初始成员变量安置后,我们先进行构造函数的实现


💕2.模拟构造函数实现

 构造函数分为有参构造函数,无参构造函数,以及拷贝构造函数,因此,有参构造函数和无参构造函数可以使用全缺省,合并为一个构造函数,拷贝构造函数单独进行实现


首先思考,我们有参构造函数与无参构造函数的形参是字符串,因此,我们的形参应当也是const char* str,const char* str的意义就是字符串类型

再次思考,既然要实现构造函数,那就要对成员变量进行更改,有效个数可以使用strlen函数,容量的话可以先使用size的数值,_str可以使用size的数值进行开辟空间

但需要注意的是capacity开辟出来的容量应该要多一个,多出来的是'\0'的位置


因此,我们可以这样写->:

string.cpp的构造函数

#define _CRT_SECURE_NO_WARNINGS 
#include"blogstring.h"
namespace blog
{string::string(const char* str):_size(strlen(str)){_capacity = _size;//更新容量_str = new char[_capacity+1];//开辟capacity+1个char类型的空间,因为有一个是'\0'的strcpy(_str, str);//strcpy(char* 目标字符串,char* 源字符串)函数,传入两个地址,将源字符串的内容复制到目标字符串中}
}

注意我们的string.c文件中,使用的是命名空间域,这样是因为我们的成员变量是私有的,而不同文件的命名空间域会合并在一起,因此,我们在实现string.c文件时,可以直接让形参访问成员变量


同时我们的全缺省需要写到头文件中,如果我们什么都没传该怎么写,传空字符串,size应该是0。如何让size是0?

我们只需要在头文件处更新一下缺省值即可,如下->:

blogstring.h更新

namespace blog
{class string{public:string(const char* str = "");//有参&&无参构造函数private:char* _str;//字符串首地址size_t _size;//有效个数size_t _capacity;//容量};
}

💕3.拷贝构造函数模拟实现 

如何实现拷贝构造函数,我们回想一下如何使用,string s2(s1),我们的形参应该是一个字符串类型的,那么,我们的头文件可以这样写->:


blogstring.h

#pragma once
#include<iostream>
using namespace std;
namespace blog
{class string{string(const char* str = "");//有参&&无参构造函数string(const string& str);//拷贝构造函数private:char* _str;//字符串首地址size_t _size;//有效个数size_t _capacity;//容量};
}

思考一下,拷贝构造函数的实现应该实现深拷贝,也就是需要开辟新的空间,那么既然要开辟新的空间,我们就需要知道形参字符串的有效之,因此我们可以这样写->:

这里只写拷贝构造函数的实现了,往后也是如此

	string::string(const string& str){_str = new char[str._capacity+1];//开辟新的空间_size = str._size;_capacity = str._capacity;strcpy(_str, str._str);}

如此,就显示出我们之前使用命名空间域合并的优势了

💕4.析构函数模拟实现 

想要实现析构函数,我们就要释放内存并进行空指针化,这点很简单,代码如下->:


string.h

~string();//析构函数

string.c

	string:: ~string(){_size = _capacity = 0;delete[] _str;_str = nullptr;}

💕5.size函数,capacity函数模拟实现 

实现size函数与capacity函数的实现,就是实现返回字符串的size和capacity是多少,这一点也简单容易实现,如下->:

string.h

size_t size();//返回_size
size_t capacity();//返回_capacity

string.c

	//返回_sizesize_t string::size(){return _size;}//返回_capacitysize_t string::capacity(){return _capacity;}

💕6.begin函数,end函数,模拟实现 

在我们模拟实现时,迭代器的类型iterator不可以直接使用,我们需要使用typedef将其重命名一下才可以使用,那么我们的头文件需要先整体更新一下->:


string.h

#pragma once
#include<iostream>
using namespace std;
namespace blog
{class string{typedef char* iterator ;typedef const char* const_iterator;public:string(const char* str = "");//有参&&无参构造函数string(const string& str);//拷贝构造函数~string();//析构函数size_t size();//返回_sizesize_t capacity();//返回_capacityiterator begin();//begin函数iterator end();//end函数const_iterator begin() const;//const begin函数const_iterator end() const;//const end函数private:char* _str;//字符串首地址size_t _size;//有效个数size_t _capacity;//容量};
}

因为原本的begin与end函数有const与非const类型,所以我们这里也模仿上


begin函数返回的是字符串首地址,end函数返回的是'\0'的位置,那么就很好实现了


string.cpp

//begin函数
string::iterator string::begin()
{return _str;
}//end函数
string::iterator string::end()
{return _str+_size;
}//const begin函数
string::const_iterator string::begin()const
{return _str;
}//const end函数
string::const_iterator string::end()const
{return _str + _size;
}

💕7.=赋值运算符重载模拟实现

想要实现赋值运算符,首先想想它可以怎样用,它可以,string s1 = s2这样用,也可以s2 = s3

这样用,这就说明我们传过去的形参,可能已经被初始化了,也可能没有被初始化,这就会导致capacity可能不够的问题,因此,需要先判断一下,再进行深拷贝


如果像string s1 = s2还容易弄,毕竟就像拷贝构造函数一样,但如果是s2 = s3呢?我们需要更改s2的capacity,那么,可以这样写->:


string.h

string& operator=(const string& s);//赋值运算符重载

string.cpp

	//赋值运算符重载string& string::operator=(const string& s){_capacity = s._capacity;//更改capacity_size = s._size;//更改_sizechar* tep = new char[_capacity+1];//开辟一样的空间strcpy(tep, s._str);//将内容复制过去delete[] _str;_str = tep;return *this;}

我们对于.cpp文件也可以进行更一步的优化,如果两个对象不相等的话,进行更改,否则直接返回,如下->:

	//赋值运算符重载string& string::operator=(const string& s){//这里是取地址操作符if (this != &s) {_capacity = s._capacity;//更改capacity_size = s._size;//更改_sizechar* tep = new char[_capacity+1];//开辟一样的空间strcpy(tep, s._str);//将内容复制过去delete[] _str;_str = tep;}return *this;}

💕8.访问运算符重载 模拟实现

想要实现访问运算符,我们想想arr[1],返回的是一个确切的值,那么这里返回的就应该是char类型的,避免内存重复开辟我们可以使用char&来作为返回值(堆区开辟的内存,不会主动释放)


string.h

string类中有const与非const,我们也这样实现

char& operator[](size_t pos);//访问运算符重载
const char& operator[](size_t pos) const;//const 访问运算符重载

string.cpp

	//访问运算符重载char& string::operator[](size_t pos){assert(pos >= 0 && pos < _size);return *(_str + pos);}//const 访问运算符重载const char& string::operator[](size_t pos) const{assert(pos >= 0 && pos < _size);return *(_str + pos);}

💕9.reserve函数模拟实现 

 reserve函数是用来预留空间的,而且只可以扩不可以缩,这一点我们之前有讲,所以只需要重新开一段空间并将值拷贝过去即可


string.h

void reserve(size_t n);//预留空间

string.cpp

	//预留空间模拟实现void string::reserve(size_t n){if (n > _capacity){_capacity = n;char* tep = new char[_capacity + 1];strcpy(tep, _str);delete[] _str;_str = tep;}}

💕10.push_back函数模拟实现

想要实现push_bakc进行尾插,首先需要判断的就是是否需要扩容,以及是否是空容量的问题,之后就是插入的问题,不要忘记'\0'的处理


string.h

void push_back(char ch);//尾插一个字符

string.cpp

	//尾插字符void string::push_back(char ch){//判断是否需要扩容if (_size == _capacity) {size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;_str[_size + 1] = '\0';++_size; }

💕11.append函数模拟实现

attend函数是尾插字符串,依旧需要先判断是否扩容,接着我们可以使用一个c语言中学习的string类型的函数进行尾插


string.h

void append(const char* str);//尾插一个字符串

string.cpp

	//尾插字符串void string::append(const char* str){	size_t len = strlen(str);//计算字符串多长,方便修改size//判断是否需要扩容if (_size == _capacity) {size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}strcpy(_str + _size, str);_size += len;}

💕 12.+=运算符重载

 +=运算符重载可以既尾插一个字符,也可以尾插一个字符串,是两种类型,这里非常容易实现,直接套现push_back和append函数即可,代码如下->:


string.h

string& operator+=(char ch);//+=运算符重载,尾插字符
string& operator+=(const char* str);//+=运算符重载,尾插字符串

string.cpp

	//+=运算符重载,尾插字符string& string::operator+=(char ch){push_back(ch);return *this;}//+=运算符重载,尾插字符串string& string::operator+=(const char* str){append(str);return *this;}

💕13.find函数模拟实现 

find函数的实现也很简单,代码如下->:


string.h

size_t find(char ch, size_t pos = npos);//查找某个字符
size_t find(const char* str, size_t pos = npos);//查找某个字符串
static const size_t npos;//我们需要定义静态npos设为找不到的情况

查找某个字符串时可以直接用函数来实现,并通过指针-指针返回中间元素的个数,进而直接反馈出下标是多少

string.cpp

const size_t string::npos = -1;//查找某个字符size_t string::find(char ch, size_t pos){
//方法一:/*while (_str + pos < _str + _size){if (*(_str + pos) == ch){return (_str + pos) - _str;}pos++;}return npos;*///方法二:for (size_t i = pos; i < _size; i++){if (*(_str + i) == ch){return i;}}return npos;}//查找某个字符串size_t string::find(const char* str, size_t pos){if (p == nullptr)return npos;char* p = strstr(_str + pos, str);return  p - _str;}

💕14.insert函数指定位置插入字符模拟实现

insert函数的模拟实现,其实就像数据结构实现顺序表的样子

 先讲插入位置后面的数据全部后移,再进行擦插入。需要先判断是否需要扩容,因为有可能直接使用insert插入,导致capacity是0


string.h

void insert(size_t pos, char ch);//指定位置插入字符

string.cpp

//指定位置插入字符
void string::insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;
}

💕15.insert函数指定位置插入字符串模拟实现

插入字符串是类似的,只不过用到了memcpy函数进行了将字符串复制上去


string.h

void insert(size_t pos, const char* str);//指定位置插入字符串

string.cpp

//指定插入字符串void 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;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}memcpy(_str + pos, str, len);_size += len;}

💕16.erase函数模拟实习 

指定位置删除字符需要注意的是删去的长度,大于字符串就全删。

删去后使用strcpy进行覆盖,并进行size的减去


string.h

void erase(size_t pos = 0, size_t len = npos);//指定位置删除字符串

string.cpp

	void string::erase(size_t pos , size_t len ) {assert(pos < _size);// len大于前面字符个数时,有多少删多少if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);//用strcpy进行覆盖_size -= len;}}

 

💕17.判断运算符重载

这里很简单,不进行解释了


string.h
 

bool operator<(const string& s) const;
bool operator>(const string& s) const;
bool operator<=(const string& s) const;
bool operator>=(const string& s) const;
bool operator==(const string& s) const;
bool operator!=(const string& s) const;

string.cpp

bool string::operator<(const string& s) const
{return strcmp(_str, s._str) < 0;
}bool string::operator>(const string& s) const
{return !(*this <= s);
}bool string::operator<=(const string& s) const
{return *this < s || *this == s;
}bool string::operator>=(const string& s) const
{return !(*this < s);
}bool string::operator==(const string& s) const
{return strcmp(_str, s._str) == 0;
}bool string::operator!=(const string& s) const
{return !(*this == s);
}

💕18.输出/输入运算符重载

输出输入运算符的重载是要在类外写的,但是不要出了命名空间域,否则会无法访问成员变量(不使用友元)


string.h

istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);

string.cpp

istream& operator>> (istream& is, string& str)
{str.clear();//下面讲char ch = is.get();while (ch != ' ' && ch != '\n'){str += ch;ch = is.get();}return is;
}ostream& operator<< (ostream& os, string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}

💕19.clear函数 

我们在cout重载时,如果不将对象清理,那就会变成尾插,因此,我们需要先进行初始化清理一下


clear函数把字符串首字符设为字符串结束符 '\0',这意味着字符串内容为空。同时将字符串的大小 _size 置为 0


string.h

void clear();//清理函数

string.cpp

	void string::clear(){_str[0] = '\0';_size = 0;}

💕20.总体代码

 string.h

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace blog
{class string{typedef char* iterator ;typedef const char* const_iterator;public://test1string(const char* str = "");//有参&&无参构造函数string(const string& str);//拷贝构造函数~string();//析构函数size_t size() const;//返回_sizesize_t capacity();//返回_capacity//test2iterator begin();//begin函数iterator end();//end函数const_iterator begin() const;//const begin函数const_iterator end() const;//const end函数//test3string& operator=(const string& s);//赋值运算符重载char& operator[](size_t pos);//访问运算符重载const char& operator[](size_t pos) const;//const 访问运算符重载//test4void reserve(size_t n);//预留空间void push_back(char ch);//尾插一个字符void append(const char* str) ;//尾插一个字符串string& operator+=(char ch);//+=运算符重载,尾插字符string& operator+=(const char* str);//+=运算符重载,尾插字符串//test5size_t find(char ch, size_t pos = 0);//查找某个字符size_t find(const char* str, size_t pos = 0);//查找某个字符串//test6void insert(size_t pos, char ch);//指定位置插入字符void insert(size_t pos, const char* str);//指定位置插入字符串void erase(size_t pos = 0, size_t len = npos);//指定位置删除字符串bool operator<(const string& s) const;bool operator>(const string& s) const;bool operator<=(const string& s) const;bool operator>=(const string& s) const;bool operator==(const string& s) const;bool operator!=(const string& s) const;void clear();//清理函数private:char* _str;//字符串首地址size_t _size;//有效个数size_t _capacity;//容量static const size_t npos;};istream& operator>> (istream& is, string& str);//重载输入ostream& operator<< (ostream& os, string& str);//重载输出
}

string.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include"blogstring.h"
namespace blog
{const size_t string::npos = -1;//构造函数string::string(const char* str):_size(strlen(str)){_capacity = _size;//更新容量_str = new char[_capacity + 1];//开辟size个char类型的空间strcpy(_str, str);//strcpy(char* 目标字符串,char* 源字符串)函数,传入两个地址,将源字符串的内容复制到目标字符串中}//拷贝构造函数string::string(const string& str){_str = new char[str._capacity + 1];//开辟新的空间_size = str._size;_capacity = str._capacity;strcpy(_str, str._str);}//析构函数string:: ~string(){_size = _capacity = 0;delete[] _str;_str = nullptr;}//返回_sizesize_t string::size() const{return _size;}//返回_capacitysize_t string::capacity(){return _capacity;}//begin函数string::iterator string::begin(){return _str;}//end函数string::iterator string::end(){return _str + _size;}//const begin函数string::const_iterator string::begin()const{return _str;}//const end函数string::const_iterator string::end()const{return _str + _size;}//赋值运算符重载string& string::operator=(const string& s){//这里是取地址操作符if (this != &s) {_capacity = s._capacity;//更改capacity_size = s._size;//更改_sizechar* tep = new char[_capacity + 1];//开辟一样的空间strcpy(tep, s._str);//将内容复制过去delete[] _str;_str = tep;}return *this;}//访问运算符重载char& string::operator[](size_t pos){assert(pos >= 0 && pos < _size);return *(_str + pos);}//const 访问运算符重载const char& string::operator[](size_t pos) const{assert(pos >= 0 && pos < _size);return *(_str + pos);}//预留空间模拟实现void string::reserve(size_t n){if (n > _capacity){_capacity = n;char* tep = new char[_capacity + 1];strcpy(tep, _str);delete[] _str;_str = tep;}}//尾插字符void string::push_back(char ch){//判断是否需要扩容if (_size == _capacity) {size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;_str[_size + 1] = '\0';++_size;}//尾插字符串void string::append(const char* str){size_t len = strlen(str);//计算字符串多长,方便修改size//判断是否需要扩容if (_size == _capacity) {size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}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;}//查找某个字符size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; i++){if (*(_str + i) == ch){return i;}}return npos;}//查找某个字符串size_t string::find(const char* str, size_t pos){char* p = strstr(_str + pos, str);if (p == nullptr)return npos;return  p - _str;}//指定位置插入字符void string::insert(size_t pos, char ch){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;}//指定插入字符串void 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;while (end > pos){_str[end + len] = _str[end];--end;}memcpy(_str + pos, str, len);_size += len;}//指定位置删除void string::erase(size_t pos, size_t len){assert(pos < _size);// len 大于剩余字符个数时,有多少删多少if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{// 使用 memmove 避免内存重叠问题memmove(_str + pos, _str + pos + len, _size - pos - len + 1);_size -= len;}}bool string::operator<(const string& s) const{return strcmp(_str, s._str) < 0;}bool string::operator>(const string& s) const{return !(*this <= s);}bool string::operator<=(const string& s) const{return *this < s || *this == s;}bool string::operator>=(const string& s) const{return !(*this < s);}bool string::operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s) const{return !(*this == s);}//清理函数void string::clear(){_str[0] = '\0';_size = 0;}//输入重载istream& operator>> (istream& is, string& str){str.clear();char ch = is.get();while (ch != ' ' && ch != '\n'){str += ch;ch = is.get();}return is;}//输出重载ostream& operator<< (ostream& os,string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}
}

test.cpp


这里包含了测试用例

#define _CRT_SECURE_NO_WARNINGS 
#include"blogstring.h"
void test1()
{blog::string s1("hello world");//测试构造函数cout << s1<<endl;blog::string s2(s1);//测试拷贝构造函数cout << s2 << endl;blog::string s3(s2);s1.~string();//测试析构函数cout << s1.capacity()<<' ' << s1.size() << endl;//测试size与capacity函数
}void test2()
{blog::string s1("hello world");printf("%p\n", s1.begin());//测试beginprintf("%p\n", s1.end());//测试endconst blog::string s2(s1);printf("%p\n", s2.begin());//测试beginprintf("%p\n", s2.end());//测试end
}void test3()
{blog::string s1("hello world");s1 = "ming ri fang zhou";//测试赋值运算符重载cout << s1 << endl;for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << ' ';//测试[]运算符重载}cout << endl;const blog::string s2("nihao");for (size_t i = 0; i < s2.size(); i++){cout << s2[i] << ' ';//测试[]运算符重载}
}void test4()
{blog::string s1("hello world");cout << s1.capacity() << endl;s1.reserve(200);//测试reservecout << s1.capacity() << endl;s1.push_back('x');//测试push_backs1.append("oppo");//测试appends1 += 'p';//测试+=运算符重载s1 += "lll";//测试+=运算符重载cout << s1 << endl;}void test5()
{blog::string s1("hello world");int tep1 = s1.find('l');int tep2 = s1.find("or");cout << tep1<<' '<<tep2<<endl;
}void test6()
{blog::string s1;s1.insert(0, 'x');cout << s1 << endl;s1.insert(1, "xxxx");cout << s1 << endl;s1.erase(1);cout << s1 << endl;
}
int main()
{//test1();//test2();//test3();//test4();//test5();//test6();
}

💕21.完结 

相关文章:

【C++】string类模拟实现

目录 &#x1f495;1.模拟string类构造函数 &#x1f495;2.模拟构造函数实现 &#x1f495;3.拷贝构造函数模拟实现 &#x1f495;4.析构函数模拟实现 &#x1f495;5.size函数&#xff0c;capacity函数模拟实现 &#x1f495;6.begin函数,end函数&#xff0c;模拟实…...

2025发文新方向:AI+量化 人工智能与金融完美融合!

2025深度学习发论文&模型涨点之——AI量化 人工智能的融入&#xff0c;使量化交易实现了质的突破。借助机器学习、深度学习等先进技术&#xff0c;人工智能可高效处理并剖析海量市场数据&#xff0c;挖掘出数据背后错综复杂的模式与趋势&#xff0c;从而不仅提升了数据分析…...

eniops库中reduce函数使用方法

reduce 是 eniops 中的一个常用函数&#xff0c;用于对张量进行降维操作。它允许你通过指定维度名称和操作类型&#xff08;如求和、均值等&#xff09;来简化张量的形状。 import eniops import torch# 创建一个示例张量 x torch.randn(2, 3, 4)# 使用 reduce 进行降维操作 …...

第03章 02 VTK中的智能指针

在VTK&#xff08;Visualization Toolkit&#xff09;中&#xff0c;智能指针用于管理对象的生命周期&#xff0c;避免内存泄漏和悬空指针等问题。VTK提供了几种不同类型的智能指针&#xff0c;包括vtkNew、vtkSmartPointer和vtkWeakPtr。以下是它们的区别和作用&#xff1a; …...

03垃圾回收篇(D4_彻底理解GC)

目录 一、浅析大促备战过程中出现的 fullGc&#xff0c;我们能做什么&#xff1f; 1. 什么是 JVM 的 GC? 2. 写代码的时候能做什么&#xff1f; 3. 测试能做啥 4. 知识小结 二、MinorGC、MajorGC、FullGC垃圾回收介绍 1. MinorGC &#xff08;新生代垃圾回收&#xff09…...

C语言小项目——通讯录

功能介绍&#xff1a; 1.联系人信息&#xff1a;姓名年龄性别地址电话 2.通讯录中可以存放100个人的信息 3.功能&#xff1a; 1>增加联系人 2>删除指定联系人 3>查找指定联系人的信息 4>修改指定联系人的信息 5显示所有联系人的信息 6>排序&#xff08;名字&…...

MyBatis和JPA区别详解

文章目录 MyBatis和JPA区别详解一、引言二、设计理念与使用方式1、MyBatis&#xff1a;半自动化的ORM框架1.1、代码示例 2、JPA&#xff1a;全自动的ORM框架2.1、代码示例 三、性能优化与适用场景1、MyBatis&#xff1a;灵活的SQL控制1.1、适用场景 2、JPA&#xff1a;开发效率…...

【理论】测试开发工程师进阶路线

一、腾讯与阿里的质量保证服务参考 阿里云效测试能力与架构 腾讯 WeTest 测试能力全景图 二、测试开发技术体系 1.用户端测试&#xff1a; Web/App 测试 Web/App 自动化测试 用户端专项测试 用户端安全测试 2.服务端测试&#xff1a; 接口协议与 Mock 接口自动化测试 服务端…...

能源新动向:智慧能源平台助力推动新型电力负荷管理系统建设

背景 国家能源局近日发布《关于支持电力领域新型经营主体创新发展的指导意见》&#xff0c;鼓励支持具备条件的工业企业、工业园区等开展智能微电网建设&#xff0c;通过聚合分布式光伏、分散式风电、新型储能、可调节负荷等资源&#xff0c;为电力系统提供灵活调节能力&#x…...

WPF基础 | 深入 WPF 事件机制:路由事件与自定义事件处理

WPF基础 | 深入 WPF 事件机制&#xff1a;路由事件与自定义事件处理 一、前言二、WPF 事件基础概念2.1 事件的定义与本质2.2 常见的 WPF 事件类型 三、路由事件3.1 路由事件的概念与原理3.2 路由事件的三个阶段3.3 路由事件的标识与注册3.4 常见的路由事件示例 四、自定义事件处…...

CAP原则中的P:分区容错性(Partition tolerance)

CAP 理论概述 在分布式系统中&#xff0c;系统面临三大核心问题&#xff1a; 一致性&#xff08;Consistency&#xff09;&#xff1a;所有节点的数据始终保持一致。可用性&#xff08;Availability&#xff09;&#xff1a;系统始终能够响应请求&#xff0c;无论请求是否成功…...

校验收货地址是否超出配送范围实战3(day09)

优化用户下单功能&#xff0c;加入校验逻辑&#xff0c;如果用户的收货地址距离商家门店超出配送范围&#xff08;配送范围为5公里内&#xff09;&#xff0c;则下单失败。 提示&#xff1a; ​ 1. 基于百度地图开放平台实现&#xff08;https://lbsyun.baidu.com/&#xff09…...

【二叉树的深搜】二叉树剪枝

文章目录 814. 二叉树剪枝解题思路&#xff1a;深度优先遍历 后序遍历另一种写法 814. 二叉树剪枝 814. 二叉树剪枝 ​ 给你二叉树的根结点 root &#xff0c;此外树的每个结点的值要么是 0 &#xff0c;要么是 1 。 ​ 返回移除了所有不包含 1 的子树的原二叉树。 ​ 节点…...

Java 大视界 -- Java 大数据中的数据脱敏技术与合规实践(60)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…...

k8s优雅重启

理论上处于terminating状态的pod&#xff0c;k8s 就会把它从service中移除了&#xff0c;只用配置一个优雅停机时长就行了。kubectl get endpoints 验证 简介 使用kubernetes启动容器时&#xff0c;一般都会配置一些探针来保证pod的健康&#xff0c;并通过terminationGracePe…...

RK3568上电启动流程详解.2 [十五]

这里我们再深入分析一下 U-Boot 启动 Kernel 的过程。 我们首先需要从 eMMC 的 boot partition 中加载 kernel Image&#xff0c;kernel dtb 以及 Ramdisk&#xff1a; > mmc partPartition Map for MMC device 0 -- Partition Type: EFIPart Start LBA End L…...

仅仅4M!windows系统适用,免费无限制使用!

软件介绍 在日常生活里&#xff0c;我们经常会碰到电脑运行迟缓、网速卡顿的现象&#xff0c;却又不清楚是哪个程序在占用过多资源。这种时候&#xff0c;一款能实时监测网络和系统状态的工具就变得非常关键了。今天呢&#xff0c;就给大家介绍一个小巧又实用的监控工具——「T…...

SOME/IP服务接口

本系列文章将分享我在学习 SOME/IP 过程中积累的一些感悟&#xff0c;并结合 SOME/IP 的理论知识进行讲解。主要内容是对相关知识的梳理&#xff0c;并结合实际代码展示 SOME/IP 的使用&#xff0c;旨在自我复习并与大家交流。文中引用了一些例图&#xff0c;但由于未能找到原作…...

计算机网络之网络层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 03 网络层 在网上看到其他人做了相关笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解网络层-网络层概述和编址【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51CTO博客 2 …...

WebAssembly视频检测在社区创作平台的落地与实践 | 得物技术

一、背景&现状 创作者服务平台作为得物为社区创作者提供的PC端视频发布入口&#xff0c;地位非常重要。且随着功能的升级迭代&#xff0c;用户群体也越来越多。但我们偶尔会收到如下反馈&#xff1a; 视频损坏&#xff0c;无法播放视频模糊曝光度问题黑屏&#xff0c;只有…...

服务器内部是如何运行的

服务器内部的运行可以从硬件和软件两个方面来解释。 一、硬件层面 服务器的硬件与普通计算机相似,但它通常具有更高的性能和更强的扩展性。服务器硬件包括: 1.中央处理单元(CPU):负责执行服务器上的计算任务。服务器一般配备多核心的高性能CPU,以支持多个请求并行处理…...

mapbox加载geojson,鼠标移入改变颜色,设置样式以及vue中的使用

全国地图json数据下载地址 目录 html加载全部代码 方式一&#xff1a;使用html方式加载geojson 1. 初始化地图 2. 加载geojson数据 设置geojson图层样式&#xff0c;设置type加载数据类型 设置线条 鼠标移入改变颜色&#xff0c;设置图层属性&#xff0c;此处是fill-extru…...

电子应用设计方案102:智能家庭AI鱼缸系统设计

智能家庭 AI 鱼缸系统设计 一、引言 智能家庭 AI 鱼缸系统旨在为鱼类提供一个健康、舒适的生活环境&#xff0c;同时为用户提供便捷的管理和观赏体验。 二、系统概述 1. 系统目标 - 自动维持水质稳定&#xff0c;包括水温、酸碱度、硬度和溶氧量等关键指标。 - 智能投食&…...

ESP8266 OTA固件启动日志里分区解析【2M flash】

ESP8266 启动日志中分区表的内容及其解释 分区表日志&#xff1a; I (136) boot: Partition Table: I (147) boot: ## Label Usage Type ST Offset Length I (170) boot: 0 nvs WiFi data 01 02 00009000 00004000 I (193) boot:…...

dl学习笔记:(7)完整神经网络流程

完整神经网络流程 反向传播链式求导 代码实现反向传播动量法Momentum开始迭代为什么选择小批量TensorDataset与DataLoader 反向传播 由于本节的公式比较多&#xff0c;所以如果哪里写错了漏写了&#xff0c;还请帮忙指出以便进行改正&#xff0c;谢谢。 在前面的章节已经介绍过…...

三分钟简单了解一些HTML的标签和语法_01

1.图片建议建立一个文件夹如下图所示 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"keywords"><title>魔神羽落</title><style>.testone{background-color: #ff53e…...

C# 中使用Hash用于密码加密

通过一定的哈希算法&#xff08;典型的有MD5&#xff0c;SHA-1等&#xff09;&#xff0c;将一段较长的数据映射为较短小的数据&#xff0c;这段小数据就是大数据的哈希值。他最大的特点就是唯一性&#xff0c;一旦大数据发生了变化&#xff0c;哪怕是一个微小的变化&#xff0…...

【C++高并发服务器WebServer】-2:exec函数簇、进程控制

本文目录 一、exec函数簇介绍二、exec函数簇 一、exec函数簇介绍 exec 函数族的作用是根据指定的文件名找到可执行文件&#xff0c;并用它来取代调用进程的内容&#xff0c;换句话说&#xff0c;就是在调用进程内部执行一个可执行文件。 exec函数族的函数执行成功后不会返回&…...

Linux将目录挂载到另一个目录,类似软硬链接,并通过fstab实现

格式 <源> <目> none bind 0 0例如 /data/var/lib/docker /var/lib/docker none bind 0 0参数使用制表符tab间隔 效果 rootDebian12:~# cat /etc/fstab | grep -v ^# /dev/mapper/Debian12--vg-root / ext4 erro…...

【C++模板】:如何判断自定义类型是否实现某个函数

一、引子 偶尔我们会面对这样的尴尬的场景&#xff0c;我们需要显示的去判断在某个自定义类型中&#xff0c;是否已经提供了我们期待的API接口&#xff0c;以避免产生“莫须有”的错误。阁下该如何破解此问题&#xff01; 这里&#xff0c;直接给出一种通用的方法&#xff0c;…...

关于CAN(FD)转以太网详细介绍

一、功能描述 CANFD 完全向下兼容 CAN &#xff0c;以下统称 CAN(FD) 。 SG-CAN(FD)NET-210 是一款用来把 CANFD 总线数据转为网口数据的设备。 网口支持 TCP Sever 、 TCP Client 、 UDP Sever 、 UDP Client 四种模式。 可以通过软件配置和 Web 网页配置。 两路…...

GPU算力平台|在GPU算力平台部署MedicalGPT医疗大模型的应用教程

文章目录 一、GPU算力服务平台云端GPU算力平台 二、平台账号注册流程MedicalGPT医疗大模型的部署MedicalGPT医疗大模型概述MedicalGPT部署步骤 一、GPU算力服务平台 云端GPU算力平台 云端GPU算力平台专为GPU加速计算设计&#xff0c;是一个高性能计算中心&#xff0c;广泛应用…...

【ChatGPT】意义空间与语义运动定律 —— AI 世界的神秘法则

作者介绍 斯蒂芬•沃尔弗拉姆&#xff08;Stephen Wolfram&#xff09; 计算机科学家、数学家和理论物理学家&#xff0c;当今科学和技术领域重要的革新者之一。他创造了在全世界备受推崇的软件系统——Mathematica、Wolfram|Alpha和Wolfram语言。 在 ChatGPT 内部&#xff0c;…...

Harbor 部署教程

Harbor 部署教程 一.背景二.遇到的问题及解决办法1.服务无法启动,相关的容器不断重启 三.操作步骤1. 创建工作目录并进入2. 配置 Docker 守护进程3. 重启 Docker 服务4. 下载 Harbor 离线安装包和 Docker Compose5. 安装 Docker Compose6. 解压 Harbor 安装包7. 配置 Harbor8. …...

hive 自动检测、自动重启、记录检测日志、自动清理日志

最终效果 定时检测hive运行状态&#xff0c;进程不存在或者进程存在但是不监听端口的hiveserver2&#xff0c;自动重新拉起每次检测脚本执行的日志都会保存在log目录下.check文件&#xff0c;每一个月一个文件每月15日&#xff0c;删除2月前的检测日志开启hive自带日志输出后&…...

C++类和对象下详细指南

C类和对象下详细指南 1. 初始化列表与构造函数 1.1 初始化列表概述 初始化列表在C中用于初始化对象的成员变量&#xff0c;特别是当你需要在对象构造时就明确成员变量的值时。通过初始化列表&#xff0c;成员变量的初始化可以在进入构造函数体之前完成。这不仅可以提升性能&…...

【算法C++】构造回文字符串问题

问题描述 小C手中有一个由小写字母组成的字符串 s。她希望构造另一个字符串 t&#xff0c;并且这个字符串需要满足以下几个条件&#xff1a; t 由小写字母组成&#xff0c;且长度与 s 相同。t 是回文字符串&#xff0c;即从左到右与从右到左读取相同。t 的字典序要小于 s&…...

基于java线程池和EasyExcel实现数据异步导入

基于java线程池和EasyExcel实现数据异步导入 2.代码实现 2.1 controller层 PostMapping("import")public void importExcel(MultipartFile file) throws IOException {importService.importExcelAsync(file);}2.2 service层 Resource private SalariesListener sa…...

使用Layout三行布局(SemiDesign)

tips&#xff1a;Semi Design网址 &#xff1a;Semi Design 1、安装Semi # 使用 npm npm i douyinfe/semi-ui# 使用 yarn yarn add douyinfe/semi-ui# 使用 pnpm pnpm add douyinfe/semi-ui 2、引入Layout布局 import { Layout } from douyinfe/semi-ui;3、开始实现三行布局…...

缓存之美:万文详解 Caffeine 实现原理(下)

上篇文章&#xff1a;缓存之美&#xff1a;万文详解 Caffeine 实现原理&#xff08;上&#xff09; getIfPresent 现在我们对 put 方法有了基本了解&#xff0c;现在我们继续深入 getIfPresent 方法&#xff1a; public class TestReadSourceCode {Testpublic void doRead() …...

PHP防伪溯源一体化管理系统小程序

&#x1f50d; 防伪溯源一体化管理系统&#xff0c;品质之光&#xff0c;根源之锁 &#x1f680; 引领防伪技术革命&#xff0c;重塑品牌信任基石 我们自豪地站在防伪技术的前沿&#xff0c;为您呈现基于ThinkPHP和Uniapp精心锻造的多平台&#xff08;微信小程序、H5网页&…...

STM32——LCD

一、引脚配置 查看引脚 将上述引脚都设置为GPIO_Output 二、导入驱动文件 将 LCD 驱动的 Inc 以及 Src 中的 fonts.h,lcd.h 和 lcd.c 导入到自己工程的驱动文件中。 当然&#xff0c;后面 lcd 的驱动学习可以和 IMX6U 一块学。 三、LCD函数 void LCD_Clear(u16 Color); 功能…...

洛谷刷题1-3

比较巧妙&#xff0c;求最小公倍数&#xff0c;看多少个数一次循环&#xff0c;直接求解就好了&#xff0c;N的数量级比较大&#xff0c;一层循环也会超时&#xff0c;也用了点双指针的想法&#xff08;归并排序&#xff09; 这里很大的问题&#xff0c;主要是cin输入的时候遇到…...

Facebook 元宇宙与全球文化交流的新趋势

随着科技的快速发展&#xff0c;虚拟现实与增强现实技术逐渐成为全球社交平台的重要组成部分。Facebook&#xff08;现改名为 Meta&#xff09;率先将目光投向了元宇宙这一新兴领域&#xff0c;致力于打造一个超越传统社交媒体的虚拟空间&#xff0c;成为全球文化交流的新平台。…...

结构化布线系统分为六个子系统

文章目录 前言系统介绍1. 工作区子系统 (Work Area Subsystem)2. 水平布线子系统 (Horizontal Cabling Subsystem)3. 管理子系统 (Administration Subsystem)4. 干线&#xff08;垂直&#xff09;子系统 (Backbone Cabling Subsystem)5. 设备间子系统 (Equipment Room Subsyste…...

亿坊软件前端命名规范

在前端开发中&#xff0c;文件命名的重要性不言而喻。由于历史原因和个人习惯&#xff0c;不同的开发者在命名DOM结构、图片和CSS文件时&#xff0c;可能会产生不一致的情况。这不仅会导致维护成本增加&#xff0c;还会降低团队协作效率。因此&#xff0c;制定一套统一的命名规…...

单片机-STM32 IIC通信(OLED屏幕)(十一)

一、屏幕的分类 1、LED屏幕&#xff1a; 由无数个发光的LED灯珠按照一定的顺序排列而成&#xff0c;当需要显示内容的时候&#xff0c;点亮相关的LED灯即可&#xff0c;市场占有率很高&#xff0c;主要是用于户外&#xff0c;广告屏幕&#xff0c;成本低。 LED屏是一种用发光…...

【Qt】: QPointer详解

考古&#xff1a; 《Qt智能指针之QScopedPointer》 QPointer是Qt框架中的一个智能指针类&#xff0c;用于安全地管理QObject派生对象的指针。它的主要功能是自动将指针置为nullptr&#xff0c;当它所指向的QObject对象被销毁时。这在Qt应用程序中非常有用&#xff0c;因为QObj…...

15_业务系统基类

创建脚本 SystemRoot.cs 因为 业务系统基类的子类 会涉及资源加载服务层ResSvc.cs 和 音乐播放服务层AudioSvc.cs 所以在业务系统基类 提取引用资源加载服务层ResSvc.cs 和 音乐播放服务层AudioSvc.cs 并调用单例初始化 using UnityEngine; // 功能 : 业务系统基类 public c…...

C++中explicit关键字的介绍和使用详解

某一天突然发现Qt自动生成的类文件的构造函数前边都有explicit关键字&#xff0c;但是explicit关键字什么意思呐&#xff1f; 在C中&#xff0c;explicit是一个关键字&#xff0c;主要用于修饰构造函数或转换运算符&#xff0c;其作用是防止隐式类型转换或隐式构造行为。 下面…...