【C++庖丁解牛】模拟实现STL的string容器(最后附源码)
📙 作者简介 :RO-BERRY
📗 学习方向:致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持

目录
- 1.vs和g++下string结构的说明
- 2.经典的string类问题
- 浅拷贝
- 深拷贝
- 3.模拟实现string
- private成员变量
- 构造函数
- 拷贝构造函数
- 赋值拷贝
- 析构函数
- 接口 c_str
- 接口operatror[]
- reserve接口
- push_back接口
- append接口
- operator+=
- insert接口
- erase接口
- swap接口
- find接口
- substr接口
- clear接口
- 流插入输出
- 打印
- 4.测试集
- test1
- test2
- test3
- test4
- test5
- test6
- 5.源码
- string.h
- test.cpp
1.vs和g++下string结构的说明
注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。
- vs下string的结构
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:
- 当字符串长度小于16时,使用内部固定的字符数组来存放
- 当字符串长度大于等于16时,从堆上开辟空间
这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。

- g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指
针将来指向一块堆空间,内部包含了如下字段:
- 空间总大小
- 字符串有效长度
- 引用计数
- 指向堆空间的指针,用来存储字符串。
2.经典的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共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
浅拷贝
浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。

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

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

3.模拟实现string
模拟实现string需要我们定义一个命名空间达到隔绝的效果,为了不和库里名称重复
private成员变量
private:size_t _capacity = 0;size_t _size = 0;char* _str = nullptr;const static size_t npos = -1; //特殊处理,本来是不可以,加上const后可以在这里定义//const static double npos = -1; //这个设计只针对整数,对其他类型都不支持
构造函数
//string() 这是默认构造,但是我们可以直接写全缺省的构造函数就不需要这个了// :_str(new char[1]) //这里不能设置为nullptr,设置了会出现空指针解引用的错误// , _size(0)// , _capacity(0)//{// _str[0] = '\0'; //str初始化为一个\0,与库里面的string相同//}string(const char* str = "") //这里给一个空的字符串即可,达到缺省的目的{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1]; //这里要加1,因为strlen是不算\0,我们要给他加上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){string tmp(s._str);swap(tmp); //swap是我们自定义的函数具体介绍在后面}
赋值拷贝
//传统写法//string& operator=(const string& s)//{// if (this != &s)// {// char* tmp = new char[s._capacity + 1];// strcpy(tmp, s._str);// delete[] _str;// _str = tmp;// _size = s._size;// _capacity = s._capacity;// }// return *this;//}//现代写法string& operator=(string s){swap(s);return *this;}
析构函数
~string() //析构函数{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}
接口 c_str
返回字符串
const char* c_str() const{return _str;}size_t size() const //只读函数加const{return _size;}
接口operatror[]
实现string的下标访问
const char& operator[](size_t pos) const //下标访问数据,返回字符引用,根据需求加const,为了print函数能调用{assert(pos <= _size); //越界断言return _str[pos];}char& operator[](size_t pos) //下标访问数据,返回字符引用1.减少拷贝2.可以进行修改{assert(pos <= _size); //越界断言return _str[pos];}
reserve接口
预存容量
void reserve(size_t n) //扩容--开新空间,将数据拷贝,在进行释放旧空间{if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
push_back接口
尾插
void push_back(char ch){if (_size == _capacity) //扩容{size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}
append接口
void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}
operator+=
实现
string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}
insert接口
//在pos位置插入ch字符void 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; //这里采用size+1,为了阻止临界0出现越界while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;_size++;}//在pos位置插入str字符串void insert(size_t pos, const char* str){assert(pos <= _size); //越界断言size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >=(int) pos){_str[end + len] = _str[end];--end;}strncpy(_str + pos, str, len);_size += len;}
erase接口
void erase(size_t pos, size_t len=npos) //缺省值{assert(pos < _size);if (len == npos || pos + len >= _size) //没有传入len,全删{_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}
swap接口
void swap(string& s){std::swap(_str, s._str); //调用的库里的swap函数std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
find接口
//在pos位置之后查找字符chsize_t find(char ch,size_t pos=0){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}//在pos位置之后查找字符串strsize_t find(const char* str , size_t pos = 0){const char* ptr = strstr(_str, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}
substr接口
//在pos位置之后取len长度的字符串string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size) //有多少取多少{end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}
clear接口
void clear(){_size = 0;_str[0] = '\0';}
流插入输出
实现cout输出以及cin输入string
//流插入重载很特殊在全局重载ostream& operator<<(ostream& out, const string& s){//直接进行输出即可for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char buff[128]; //如果输入的字符串比较长,就需要很多次扩容,使用buff就不需要考虑没空间的问题char ch = in.get(); //这里要用in.get()函数才能取到//in >> ch; //cin默认取不到换行和空格int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get(); //这里要用in.get()函数才能取到}if (i > 0){buff[i] = '\0';s += buff;}return in;}
打印
//设置在全局的打印函数void print_str(const string& s){for (size_t i = 0; i < s.size(); i++){cout << s[i] << endl;}cout << s.c_str() << endl;//迭代器的使用 本身可以修改指向的内容不可修改string::const_iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;}
4.测试集
test1
void test_string1() //测试函数{string s1("hello world"); //构造函数测试cout << s1.c_str() << endl;string s2("hello world");cout << s2.c_str() << endl;//遍历for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1.c_str() << endl;//对于const对象迭代器的使用string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;//傻瓜式的替换成迭代器for (auto ch : s1){cout << ch << " ";}cout << endl;print_str(s1); //print函数的调用cout << endl;}
运行结果:

test2
void test_string2(){string s1("hello world");cout << s1.c_str() << endl;//insert函数测试s1.insert(5, 'y');s1.insert(0, 'y');cout << s1.c_str() << endl;s1.insert(5, "yxxxx");cout << s1.c_str() << endl;}
运行结果:

test3
void test_string3(){string s1("hello world");cout << s1.c_str() << endl;s1.erase(5, 3);cout << s1.c_str() << endl;s1.erase(5, 100);cout << s1.c_str() << endl;}
运行结果:

test4
void test_string4(){string s1("hello world");cout << s1.c_str() << endl;string s2("xxxxxx");//std::swap(s1, s2);s1.swap(s2);cout << s1.c_str() << endl;}
运行结果:

test5
void test_string5(){string s1("hello world");string s2(s1); //需要深拷贝cout << s2.c_str() << endl;string s3 = "xxxxxx"; //需要写赋值构造,因为要考虑空间不同的问题s1 = s3;cout << s1.c_str() << endl;}
运行结果:

test6
void test_string6(){string s1("hello world");cout << s1.c_str() << endl; //传统打印//实现直接打印cout << s1 << endl;cin >> s1;cout << s1 << endl;}
运行结果:

5.源码
string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;namespace A //命名空间
{class string{public://string() 这是默认构造,但是我们可以直接写全缺省的构造函数就不需要这个了// :_str(new char[1]) //这里不能设置为nullptr,设置了会出现空指针解引用的错误// , _size(0)// , _capacity(0)//{// _str[0] = '\0'; //str初始化为一个\0,与库里面的string相同//}//迭代器---这里的迭代器是我们的原生指针,但是不是所有的容器都是原生指针,这是因为string底层是连续的空间typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}string(const char* str = "") //这里给一个空的字符串即可,达到缺省的目的{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1]; //这里要加1,因为strlen是不算\0,我们要给他加上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){string tmp(s._str);swap(tmp);}//传统写法//string& operator=(const string& s)//{// if (this != &s)// {// char* tmp = new char[s._capacity + 1];// strcpy(tmp, s._str);// delete[] _str;// _str = tmp;// _size = s._size;// _capacity = s._capacity;// }// return *this;//}//现代写法string& operator=(string s){swap(s);return *this;}~string() //析构函数{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}const char* c_str() const{return _str;}size_t size() const //只读函数加const{return _size;}const char& operator[](size_t pos) const //下标访问数据,返回字符引用,根据需求加const,为了print函数能调用{assert(pos <= _size); //越界断言return _str[pos];}char& operator[](size_t pos) //下标访问数据,返回字符引用1.减少拷贝2.可以进行修改{assert(pos <= _size); //越界断言return _str[pos];}void reserve(size_t n) //扩容--开新空间,将数据拷贝,在进行释放旧空间{if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size == _capacity) //扩容{size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void 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; //这里采用size+1,为了阻止临界0出现越界while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;_size++;}void insert(size_t pos, const char* str){assert(pos <= _size); //越界断言size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}int end = _size;while (end >=(int) pos){_str[end + len] = _str[end];--end;}strncpy(_str + pos, str, len);_size += len;}void erase(size_t pos, size_t len=npos) //缺省值{assert(pos < _size);if (len == npos || pos + len >= _size) //没有传入len,全删{_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}size_t find(char ch,size_t pos=0){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t find(const char* str , size_t pos = 0){const char* ptr = strstr(_str, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}}string substr(size_t pos = 0, size_t len = npos){assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size) //有多少取多少{end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}void clear(){_size = 0;_str[0] = '\0';}private:size_t _capacity = 0;size_t _size = 0;char* _str = nullptr;const static size_t npos = -1; //特殊处理,本来是不可以,加上const后可以在这里定义//const static double npos = -1; //这个设计只针对整数,对其他类型都不支持};//流插入重载很特殊在全局重载ostream& operator<<(ostream& out, const string& s){//直接进行输出即可for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear();char buff[128]; //如果输入的字符串比较长,就需要很多次扩容,使用buff就不需要考虑没空间的问题char ch = in.get(); //这里要用in.get()函数才能取到//in >> ch; //cin默认取不到换行和空格int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get(); //这里要用in.get()函数才能取到}if (i > 0){buff[i] = '\0';s += buff;}return in;}void print_str(const string& s){for (size_t i = 0; i < s.size(); i++){cout << s[i] << endl;}cout << s.c_str() << endl;//迭代器的使用 本身可以修改指向的内容不可修改string::const_iterator it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;}void test_string1() //测试函数{string s1("hello world");cout << s1.c_str() << endl;string s2("hello world");cout << s2.c_str() << endl;//遍历for (size_t i = 0; i < s1.size(); i++){s1[i]++;}cout << s1.c_str() << endl;//对于const对象迭代器的使用string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;//傻瓜式的替换成迭代器for (auto ch : s1){cout << ch << " ";}cout << endl;print_str(s1);cout << endl;}void test_string2(){string s1("hello world");cout << s1.c_str() << endl;//s1 += "";//s1 += "assss";//cout << s1.c_str() << endl;s1.insert(5, 'y');s1.insert(0, 'y');cout << s1.c_str() << endl;s1.insert(5, "yxxxx");cout << s1.c_str() << endl;}void test_string3(){string s1("hello world");cout << s1.c_str() << endl;s1.erase(5, 3);cout << s1.c_str() << endl;s1.erase(5, 100);cout << s1.c_str() << endl;}void test_string4(){string s1("hello world");cout << s1.c_str() << endl;string s2("xxxxxx");//std::swap(s1, s2);s1.swap(s2);cout << s1.c_str() << endl;}void test_string5(){string s1("hello world");string s2(s1); //需要深拷贝cout << s2.c_str() << endl;string s3 = "xxxxxx"; //需要写赋值构造,因为要考虑空间不同的问题s1 = s3;cout << s1.c_str() << endl;}void test_string6(){string s1("hello world");cout << s1.c_str() << endl; //传统打印//实现直接打印cout << s1 << endl;cin >> s1;cout << s1 << endl;}
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
int main()
{//A::test_string1();//A::test_string2();//A::test_string3();//A::test_string4();//A::test_string5();A::test_string6();return 0;
}
相关文章:
【C++庖丁解牛】模拟实现STL的string容器(最后附源码)
📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1.vs和g下string结构…...
不要在代码中随便使用try...catch了
前言 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步! 🍅 个人主页:南木元元 目录 背景 js中的try...catch try...catch运行机制 js的事件循环机制 try...c…...
网络编程(3/6)
使用C语言完成数据库的增删改 #include<myhead.h> int do_add(sqlite3 *ppDb) {int numb;char name[50];int salary;printf("请输入员工信息:工号、姓名、薪水\n");scanf("%d %s %d",&numb,name,&salary);char sql[128];char *e…...
(day 2)JavaScript学习笔记(基础之变量、常量和注释)
概述 这是我的学习笔记,记录了JavaScript的学习过程,我是有一些Python基础的,因此在学习的过程中不自觉的把JavaScript的代码跟Python代码做对比,以便加深印象。我本人学习软件开发纯属个人兴趣,大学所学的专业也非软件…...
Spring Boot中全局异常处理器
文章目录 1.Spring Boot中两种异常处理方式2.为什么需要全局异常处理呢?3. 全局异常处理器测试4.ControllerAdvice 详解5.ExceptionHandler 详解 1.Spring Boot中两种异常处理方式 要想解决测试中存在的问题,我们需要对程序中可能出现的异常进行捕获&am…...
【JAVA重要知识 | 第七篇】Java异常知识总结(声明、抛出、捕获异常)
7.Java异常知识总结(声明、抛出、捕获异常) 7.1异常定义 在程序运行过程中,如果JVM检测出一个不可能执行的操作时,就会出现运行时错误(runtime error)。在Java中,运行时错误会作为异常抛出。异…...
SSM整合项目(Vue3环境搭建)
SSM整合项目(Vue3环境搭建) 1.下载node.js 1.卸载原来的node.js 2.检测是否卸载成功 3.下载node.js(10.16.3) 一路next就可以 4.检测是否安装成功 2.全局安装Vue插件cli 命令行输入 npm install -g vue/cli 3.新建Vue项目 1.…...
Golang 方法的接收器 receiver 指针和值的区别
一、如果receiver是指针类型 package mainimport "fmt"type Count struct {count int }func main() {c : Count{count: 0}c.incr()fmt.Println(c.count)c2 : &cc2.incr()fmt.Println(c2.count) }func (c *Count) incr() {c.count }//打印结果 1 2 incr 方法的 …...
【蓝桥杯】节省时间
一、对于string类型变量的连接,可以直接用“”或者“”来进行字符串的直接连接 string a"1"; string b"2"; string c; cab"12"; string操作符两边既可以都是string类型,也可是string与char类型 注意: (1)“”…...
矩阵乘法--Strassen算法
一、矩阵乘法 从中可以看出,计算两个矩阵的乘积,需要三个 for 循环,可以简单写出代码: for(int i1;i<m;i)for(int j1;j<p;j)for(int k1;k<n;k)c[i][j]a[i][k]*b[k][j]; 时间复杂度的分析:很明显,…...
Unity笔记:C#基础(1)
杂项 虚函数 CSDN - C虚函数详解 cnblog - C#中的虚函数virtual 常量池与new 在C#中,string是不可变的,这意味着对string对象的操作通常会返回一个新的string对象,而不会修改原始的string对象。因此,几乎所有涉及更改string内…...
计算机科技与心理学的紧密交织:一场跨学科的深度对话
随着信息技术的飞速发展,计算机科学与心理学这两门看似迥异的学科日益呈现出密不可分的关系。本文将深入探讨计算机科学与心理学之间的相互影响和融合,揭示二者在研究方法、应用实践以及对未来社会发展的影响等方面的高度关联性。 计算机科学为心理学研究…...
【JAVA类】利用接口的多继承实现———图书管理系统【附源码】
引言 在我们学习了一些java的基础语法之后,需要把这些知识点可以串起来,这里使用一个简单的小项目可以很好的帮助我们牢记这些知识点,今天就带大家学习一个有关java的小项目,很多学校也经常把这个项目作为他们的课程设计——经典的…...
Linux进程概念僵尸进程孤儿进程
文章目录 一、什么是进程二、进程的状态三、Linux是如何做的?3.1 R状态3.2 S状态3.3 D状态3.4 T状态3.5 t状态3.6 X状态3.7 Z状态 四、僵尸进程4.1 僵尸进程危害 五、孤儿进程 一、什么是进程 对于进程理解来说,在Windows上是也可以观察到的,…...
实体店如何引流成交裂变?打造流量新引擎的秘诀
在数字化浪潮席卷的今天,实体店经营面临着前所未有的挑战与机遇。社区店作为连接居民日常生活的桥梁,如何在激烈的市场竞争中脱颖而出,实现引流、成交与裂变,成为摆在每一位实体店创业者面前的重要课题。 作为一名鲜奶吧开店5年的…...
蓝桥杯(日期问题纯暴力)
纯纯暴力,写的想吐,玛德服了。 但是复习了vector去重方法,日期的合法性判断。 #include <iostream> #include <vector> #include <cstring> #include <algorithm>using namespace std; vector<int> res; st…...
ES: ES+Kibana 环境部署
ESKibana 部署 机器信息 10.10.8.62 10.10.8.63 10.10.8.64版本选择:6.8.1 基础环境优化 所有节点 # 关闭防火墙 systemctl stop firewalld.service systemctl disable firewalld.service# 查看selinux getenforce # 关闭selinux setenforce 0 # 永久关闭se…...
Find My产品越来越得到市场认可,伦茨科技ST17H6x芯片赋能厂家
苹果发布AirTag发布以来,大家都更加注重物品的防丢,苹果的 Find My 就可以查找 iPhone、Mac、AirPods、Apple Watch,如今的Find My已经不单单可以查找苹果的设备,随着第三方设备的加入,将丰富Find My Network的版图。产…...
Linux系统——Haproxy高性能负载均衡软件
目录 一、Haproxy介绍 1.Haproxy定义 2.Haproxy主要特性 二、安装Haproxy 1.yum安装 2.第三方rpm包安装 3.编译安装 3.1解决Lua环境 3.2编译安装Haproxy 三、配置文件详解 1.状态页 2.日志管理 2.1定义日志到其他主机站点 3.指定进程线程个数 4.cpu亲缘性 5.多进…...
Python办公自动化之PDF(二)
Python操作PDF二 1、PyMuPDF简介2、 1、PyMuPDF简介 PyMuPDF(也称Fitz)开源,提供了一整套用于处理PDF文件的综合工具。使用PyMuPDF,用户可以高效地执行打开PDF、提取文本、图像和表格、操作旋转和裁剪等页面属性、创建新PDF文档以…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
pycharm 设置环境出错
pycharm 设置环境出错 pycharm 新建项目,设置虚拟环境,出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)
+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...
