c++初阶篇----string的底层模拟

string类的模拟

目录

  • string类的模拟
    • 功能介绍
    • 各功能的实现
      • 类的构造函数,拷贝构造函数,析构函数
      • 迭代器的实现
      • string的内部容量访问成员函数
      • string的修改成员函数
      • string类的相关联函数
      • string类的输入输出友元
    • 汇总
      • string功能的实现汇总
      • 测试代码

功能介绍

namespace bit
{class string{private:char* _str;size_t _capacity;size_t _size;public:typedef char* iterator;typedef const char*const_iterator;static const int npos;public:string(){}~string(){}//各个成员函数的实现}
}
const int bit::string::npos = -1;

实现思路:

  • 迭代器的实现{typefef 一个指针,模拟指针,这里用的指针}
  • 先实现构造函数{注意初始化的动态内存开辟}
    这里还要注意如何实现缺省值(赋予nullptr? 赋予空)
    ,拷贝构造函数{开辟字符串长度大小的空间,strcpy内存复制}
  • c_str(){返回数组内容}
  • 运算符重载 拷贝 {设计现代写法,利用临时变量在此函数调用其他已经实现的构造函数,与临时变量交换,实现拷贝。第二种普遍想法,开同等大小的空间(注意实际多出1来存‘/0’),内存复制函数}
  • 析构函数{释放空间,成员变量归零}
  • 扩容函数reserve{若空间不足,new,思想和栈,链表开空间一样,临时变量来做缓冲}
  • 调整字符串大小函数resize{若参数的长度小于当前string的长度,便将[n]位置的数据赋予‘/0’

修改:

  • 尾插push_pack{判断扩容,尾部插入字符}
  • append{扩容,复制拷贝字符串}
  • 插入函数insert(字符串,字符){扩容,对pos位置的字符往后排,在pos位置插入字符,或者插入字符串(strncpy,不会复制空字符)}
  • 擦除erase{在pos位置删除之后的字符串}
  • 交换swap{交换函数模板,各成员变量都交换}
  • 查找find(字符,字符串)
    判断pos位置的合理性,循环比较相等
    字符串需要用到库函数strstr
  • 获取从某个位置开始的字符串substr{临时变量string来储存pos之后的字符串,注意的是,谨慎判断pos与_size和npos之间的关系大小
  • 清除clear{_str[0]置空字符,大小变为零}
  • [ ]

各功能的实现

类的构造函数,拷贝构造函数,析构函数

其中涉及到库函数,内存拷贝的4个库函数

  1. void * memcpy ( void * destination, const void * source, size_t num ); 返回值是destination,此函数是复制内存块,将字节数的值从源指向的位置直接复制到目标指向的内存块,不检查是否source是否有终止符号

  2. void * memmove ( void * destination, const void * source, size_t num ); 返回值是destination,此函数是移动内存块。将 num 字节的值从指向的位置复制到目标指向的内存块。复制就像使用中间缓冲区一样进行,从而允许目标重叠

  3. char * strcpy ( char * destination, const char * source ); 返回值是destination,此函数的复制包括结尾的‘/0’

  4. char * strncpy ( char * destination, const char * source, size_t num ); 返回值是 destination,此函数是复制字符串,复制n个字符至des

  5. 构造,拷贝构造

//不建议的写法
string():_str(new char[1]),_capacity(0),_size(0)
{_str[0] = '/0';
}
string(const char* s):_capacity(strlen(s))
{_str = new char[_capacity + 1];_size = _capacity;strcpy(_str, s);
}
string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串
{_str = new char[_capacity + 1];//多开一个空间给‘/0’_size = _capacity;strcpy(_str, str);
}string(const string& s)
{_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);
}string& operator=(const string& s)
{_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);return *this;
}
  1. 现代写法:在实现了一个构造函数,在此基础上调用构造函数来实现另一个构造函数
string& operator=(const string& s)
{//_str = new char[s._capacity + 1];//_capacity = s._capacity;//_size = s._size;//strcpy(_str, s._str);//return *this;string tem(s);swap(tem);//这里交换要注意一开始给成员变量初始值return *this;
}

注意:交换后临时开辟的变量若对成员变量不初始化,临时变量就会变成野指针,在析构可能出现异常

在这里插入图片描述

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

迭代器的实现

string的迭代器这里用的是指针实现,迭代器用指针是比较容易实现的,只需返回指针即可

// iterator
typedef char* iterator;
typedef const char*const_iterator;
const_iterator begin()const
{return _str;
}
const_iterator end()const
{return _str + _size;
}
iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}

实现了迭代器,那么for循环也可以使用,其实for循环的本质也是用的迭代器的原理,表面是任意变量的拷贝,底层是迭代器的转换

	void test(){bit::string s2("abcdefg");bit::string::iterator begin = s2.begin();while (begin != s2.end()){(*begin)++;cout << *begin;begin++;}cout << endl;for (auto ch : s2){cout << ch;}}

在这里插入图片描述

string的内部容量访问成员函数

  1. string大小容量的实现
// capacity
size_t size()const
{return _size;
}size_t capacity()const
{return _capacity;
}bool empty()const
{return _size == 0;
}
  1. 在实现resize和reserve成员函数,应该充分了解他们的底层逻辑,resize只增容不缩容,若参数大于当前容量,扩容并且以参数char c来填充扩容后的内容。
    reserve是用来为其他成员函数扩容的,不缩容,所有扩容函数构造函数都会多开一个空间来留个‘\0’这个空字符
void resize(size_t n, char c = '\0')
{if (n <= _capacity){_str[n] = '\0';_size = n;}else {reserve(n);for (size_t i = _size;i < n; i++)   {_str[i] = c;}_str[n] = '\0';_size = n;}
}void reserve(size_t n)
{if (n > _capacity){char* tem = new char[n + 1];//多开一个空间strcpy(tem, _str);delete[] _str;_str = tem;_capacity = n;}
}

string的修改成员函数

string的修改成员函数

   modify//void push_back(char c);//string& operator+=(char c);//void append(const char* str);//string& operator+=(const char* str);//void clear();//void swap(string& s);//const char* c_str()const
  1. 实现插入,增加的原理是一样的:
    先判断是否扩容,然后插入字符,或者strcpy插入字符串
 void push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c){push_back(c);return *this;}void append(const char* str){int len = strlen(str);if(_size+len>_capacity||_size >_capacity - len)//最大值溢出问题{reserve(_size + len);}strcpy(_str + _size, str);_size = _size + len;}string& operator+=(const char* str){append(str);return *this;}string& operator+=(const string s){reserve(_size + s._size);strcpy(_str + _size, s._str);_size = _size + s._size;return *this;}
  1. 清除函数和交换函数也是很容易实现的
    清除成员函数不需要考虑释放空间,只需要将第一个有效字符设置为’\0’即可
    而交换函数可以利用库函数的模板来实现,将各个成员变量进行交换
 void clear(){_str[0] = '\0';_size = 0;}void swap(string& s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}

在swap中应用中还是不太普遍,需要s1.swap(s2) ,看起来还是不够使用,也不像库函数那样,所以,可以再重载一个全局函数,这里注意是在类域外,全局处重载函数

void swap(string& s1, string& s2)
{s1.swap(s2);
}
  1. 返回字符串函数,返回成员变量_str(开辟空间的地址)即可
const char* c_str()const{return _str;}

string类的相关联函数

  1. find成员函数的实现
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{//pos合理assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == c)return i;}return npos;
}

find的字符串的实现

这里用到了库函数strstr
const char * strstr ( const char * str1, const char * str2 ); 此函数查找子字符串,若匹配了,返回指针,不匹配返回空指针

// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{assert(pos < _size);char* ch = strstr(_str, s);if (ch){//返回俩个指针的差值,就是当前的位置return ch - _str;}else{return npos;}
}

insert插入函数的实现

 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}for (size_t i = _size+1;i > pos;i--){_str[i] = _str[i-1];}_str[pos] = c;_size++;return* this;
}string& insert(size_t pos, const char* str)
{assert(pos <= _size);//和append实现思路一样size_t len = strlen(str);if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题{reserve(_size + len);}for (size_t i = _size  + len;i > pos + len -1 ;i--){_str[i] = _str[i - len]; }strncpy(_str + pos, str,len);_size = _size + len;return *this;
}

erase擦除函数,将pos位置及后面的总计len个字符擦除

// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len=npos)
{assert(pos < _size);//不用取等,最后一个_size位置是不能擦除的,'\0'if (len == npos || len > _size - pos-1){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;
}

string比较函数的实现可以借助库函数的strcmp
int strcmp ( const char * str1, const char * str2 ); 此函数将俩个字符串进行比较,相等返回0 ,str1小于str2返回一个负数(反之)

string类的输入输出友元

注意:此类函数的实现都是基于类域的外面,也就是全局

输出流cout

输入流cin

输入流的实现思路有很多种优化。
很普遍的实现是将输入的字符一个一个塞进string的实例中,一边塞一边扩容 ,获取输入字符可以借助库函数get()

    //istream& operator>>(istream& _cin, bit::string& s)//{//    //清空当前的内容//    s.clear();//    char ch;//    ch = _cin.get();//    s.reserve(64);//    while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取//    {//        s.push_back(ch);//        ch = _cin.get();//    }//    return _cin;//}

优化:用一个大小固定的临时数组来存储当前输入字符,当输入结束或者数组已满,就赋值给string,将数组的有效字符重置,继续下一轮(如果还有输入字符)

istream& operator>>(istream& _cin, bit::string& s)
{s.clear();char ch;ch = _cin.get();char c[128];size_t i = 0;while (ch != ' ' && ch != '\n'){c[i++] = ch;//当数组满时if (i == 127){c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符s += c;i = 0;}ch = _cin.get();}if (i > 0){c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入strings += c;}return _cin;
}

汇总

string功能的实现汇总

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;namespace bit
{class string{private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;friend ostream& operator<<(ostream& _cout, const bit::string& s);friend istream& operator>>(istream& _cin, bit::string& s);public:typedef char* iterator;typedef const char*const_iterator;static const int npos;public://string() :_str(new char[1]), _capacity(0), _size(0)//{//    _str[0] = '/0';//}//string(const char* s) :_capacity(strlen(s))//{//    _str = new char[_capacity + 1];//    _size = _capacity;//    strcpy(_str, s);//}string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串{_str = new char[_capacity + 1];//多开一个空间给‘/0’_size = _capacity;strcpy(_str, str);}string(const string& s){_str = new char[s._capacity + 1];_capacity = s._capacity;_size = s._size;strcpy(_str, s._str);}string& operator=(const string& s){//_str = new char[s._capacity + 1];//_capacity = s._capacity;//_size = s._size;//strcpy(_str, s._str);//return *this;string tem(s);swap(tem);//这里交换要注意一开始给成员变量初始值return *this;}//俩种现代写法,在完成其他成员函数的基础上~string(){delete[] _str;_str = nullptr;            _size = _capacity = 0;}// iteratorconst_iterator begin()const{return _str;}const_iterator end()const{return _str + _size;}iterator begin(){return _str;}iterator end(){return _str + _size;}// modifyvoid push_back(char c){if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c){push_back(c);return *this;}void append(const char* str){size_t len = strlen(str);if (_size >_capacity - len||_size+len>_capacity)//最大值溢出问题{reserve(_size + len);}strcpy(_str + _size, str);_size = _size + len;}string& operator+=(const char* str){append(str);return *this;}string& operator+=(const string s){reserve(_size + s._size);strcpy(_str + _size, s._str);_size = _size + s._size;return *this;}void clear(){_str[0] = '\0';_size = 0;}void swap(string& s){std::swap(_str, s._str);std::swap(_capacity, s._capacity);std::swap(_size, s._size);}const char* c_str()const{return _str;}// capacitysize_t size()const{return _size;}size_t capacity()const{return _capacity;}bool empty()const{return _size == 0;}void resize(size_t n, char c = '\0'){if (n <= _capacity){_str[n] = '\0';_size = n;}else {reserve(n);for (size_t i = _size;i < n; i++)   {_str[i] = c;}_str[n] = '\0';_size = n;}}void reserve(size_t n){if (n > _capacity){char* tem = new char[n + 1];strcpy(tem, _str);delete[] _str;_str = tem;_capacity = n;}} accesschar& operator[](size_t index){return *(_str + index);}const char& operator[](size_t index)const{return *(_str + index);}//relational operatorsbool operator<(const string& s){if (strcmp(c_str(), s.c_str()) < 0){return true;}return false;}bool operator<=(const string& s){return *this == s || *this < s;}bool operator>(const string& s){return !(*this <= s);}bool operator>=(const string& s){return !(*this < s);}bool operator==(const string& s){if (strcmp(c_str(), s.c_str()) == 0)return true;return false;}bool operator!=(const string& s){return !(*this == s);}// 返回c在string中第一次出现的位置size_t find(char c, size_t pos = 0) const{//pos合理assert(pos < _size);for (size_t i = pos;i < _size;i++){if (_str[i] == c)return i;}return npos;}// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos = 0) const{assert(pos < _size);char* ch = strstr(_str, s);if (ch){//返回俩个指针的差值,就是当前的位置return ch - _str;}else{return npos;}} 在pos位置上插入字符c/字符串str,并返回该字符的位置string& insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}for (size_t i = _size+1;i > pos;i--){_str[i] = _str[i-1];}_str[pos] = c;_size++;return* this;}string& insert(size_t pos, const char* str){assert(pos <= _size);//和append实现思路一样size_t len = strlen(str);if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题{reserve(_size + len);}for (size_t i = _size  + len;i > pos + len -1 ;i--){_str[i] = _str[i - len]; }strncpy(_str + pos, str,len);_size = _size + len;return *this;}// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len=npos){assert(pos < _size);//不用最后一个pos是不能擦除的,‘\0'if (len == npos || len > _size - pos-1){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}};const int bit::string::npos = -1;void swap(string& s1, string& s2){s1.swap(s2);}ostream& operator<<(ostream& _cout, const bit::string& s){for (auto ch : s){_cout << ch;}return _cout;}//istream& operator>>(istream& _cin, bit::string& s)//{//    //清空当前的内容//    s.clear();//    char ch;//    ch = _cin.get();//    s.reserve(64);//    while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取//    {//        s.push_back(ch);//        ch = _cin.get();//    }//    return _cin;//}istream& operator>>(istream& _cin, bit::string& s){s.clear();char ch;ch = _cin.get();char c[128];size_t i = 0;while (ch != ' ' && ch != '\n'){c[i++] = ch;//当数组满时if (i == 127){c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符s += c;i = 0;}ch = _cin.get();}if (i > 0){c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入strings += c;}return _cin;}
}

测试代码

#include"imitate_string.h"void test1()
{//bit::string s1;bit::string s2("abcdefg");bit::string::iterator begin = s2.begin();while (begin != s2.end()){(*begin)++;cout << *begin;begin++;}cout << endl;for (auto ch : s2){cout << ch;}//cout << s2[2] << endl;
}void test2()
{bit::string s1("abcdefg");//s1.resize(4);//s1.resize(5, 'i');//cout << s1.c_str() << endl;//bit::string s2;//s2.resize(10, '1');//s1.push_back('2');s1.append("you donot know .... ,just nothing");cout << s1.c_str() << endl;
}void test3()
{bit::string s1;bit::string s2("ijustlovebutitwillbenothing");bit::string s3 = s2;s3 += s2;s2 += "itjustnothing OK";cout << s3.c_str() << endl;cout << s2.c_str() << endl;s1.swap(s2);cout << s1.c_str() << endl;s1.clear();cout << s1.c_str() << endl;
}void test4()
{bit::string s1("abadfadf");//bit::string s2("it just be ok,i think i only gave up");//swap(s1, s2);//bit::string s3 = s2;//cout << s1.c_str() << endl << s2.c_str() <<s3.c_str()<< endl;
}void test5()
{bit::string s1("abadfadf");bit::string s2("ac");size_t i = s1.find('b');//s1.insert(i, '5');//s2.insert(0,"12345");s1.erase(i, 2);s2.erase(0);s2 = s1;s2.erase(3);cout << s1.c_str() << endl;cout << s2.c_str();
}void test6()
{bit::string s1("qwert");bit::string s2("qwert");bit::string s3("qert");//cout << (s1 < s2) << endl;//0//cout << (s1 >= s2) << endl;//1//cout << (s3 <= s2) << endl;//1cout << s1 << endl;cin >> s2;cout << s2;bit::swap(s2, s1);cout << s1;}int main()
{//using namespace bit;//bit::string s1();//bit::string s2("abcdefg");//test5();//test6();bit::string s1("abka");cin >> s1;//s1.append("ab");cout << s1;return 0;
}

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

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

相关文章

Containerd 介绍、安装和使用

Containerd介绍、安装和使用 文章目录 Containerd介绍、安装和使用1.containerd是什么&#xff1f;2.Containerd安装2.1 主机初始化2.1.1 设置ip地址2.1.2 配置镜像源2.1.3 关闭防火墙2.1.4 禁用SELinux2.1.5 禁用swap2.1.6 设置时区 2.2 安装 containerd2.2.1 内核参数调整2.2…

【八大排序】一篇文章搞定所有排序

文章目录 1.排序的概念2.常见排序算法的实现2.1 插入排序2.1.1直接插入排序2.1.2希尔排序 2.2选择排序2.2.1直接选择排序:2.2.2堆排序 2.3交换排序2.3.1冒泡排序2.3.2快速排序Hoare法前后指针法挖坑法非递归版本 2.4归并排序递归版本非递归版本 2.5计数排序3.排序的比较 1.排序…

2024年EDM邮件营销群发平台怎么选?

在2024年选择适合的EDM&#xff08;电子邮件直接营销&#xff09;邮件营销群发平台时&#xff0c;需要考虑以下几个关键要素来评估云衔科技以及其他供应商的产品或服务是否符合您的需求&#xff1a; 一、功能完备性&#xff1a; 1、智能自动化&#xff1a;确保云衔科技提供的…

用DataGrip连接hive时报错:User: root is not allowed to impersonate plck5,解决方法

你可以尝试关闭主机校验 修改hive安装目录下conf/hive-site.xml,将hive.server2.enable.doAs设置成false <property><name>hive.server2.enable.doAs</name><value>false</value><description>Setting this property to true will have H…

警惕.360勒索病毒:如何预防.360勒索病毒攻击

导言&#xff1a; 在网络安全领域&#xff0c;勒索病毒是一种非常危险的恶意软件&#xff0c;它以其独特的加密方式和高昂的赎金要求&#xff0c;给个人和企业带来了严重的损失。.360勒索病毒便是其中之一&#xff0c;它属于BeijingCrypt勒索病毒家族&#xff0c;具有高度的隐…

office安装和卸载

目录 安装文件下载安装破解卸载 安装文件下载 链接&#xff1a;https://pan.baidu.com/s/1cBRv-NwNOf2pQyd7XBRu6A?pwdabcd 提取码&#xff1a;abcd 提供内容如下: ├── pdf │ ├── Access2007宝典.pdf │ ├── Excel2007宝典.pdf │ ├── Project2007完全掌…

scGRN:人与鼠的GRN平台

基因调控网络GRN是包含转录因子TFs与其下游靶基因之间的调控相互作用的可解释图模型。了解GRN的拓扑结构和动力学是解释疾病病因机制和将相应发现转化为新疗法的基础。单细胞多组学技术的最新进展促使从单细胞转录组学和表观基因组学数据中以前所未有的分辨率推断GRN。在这里&a…

ClickHouse03-小白如何快速搭建ClickHouse集群

普通测试通常使用ClickHouse单节点就可以了&#xff0c;但是生产环境不免需要考虑多活、负载等高可用问题&#xff0c;集群就成了基础需求 ClickHouse在集群的选择上&#xff0c;作者已知的有两种&#xff1a; 使用ZooKeeper作为节点协调的组件&#xff0c;使用ClickHouse-Kee…

红米手机Redmi 不会自动弹出USB调试选项,如何处理?(红米小米均适用)

参考&#xff1a; 红米手机Redmi 不会自动弹出USB调试选项&#xff0c;如何处理&#xff1f;&#xff08;红米小米均适用&#xff09; - 知乎 以红米9A为例&#xff1b; 【设置】菜单进入后&#xff0c;找到【我的设备】&#xff0c; 选择【全部参数】&#xff0c; 对准miui版…

SQL96 返回顾客名称和相关订单号(表的普通联结、内联结inner join..on..)

方法一&#xff1a;普通联结 select cust_name, order_num from Customers C,Orders O where C.cust_id O.cust_id order by cust_name,order_num;方法二&#xff1a;使用内连接 select cust_name,order_num from Customers C inner join Orders O on C.cust_id O.cust_id …

测试人员如何提交一条高质量的bug

测试人员在测试软件过程中&#xff0c;发现bug是必然的&#xff0c;那么发现bug后就要提交bug到缺陷管理系统中&#xff0c;如何提交一条高质量的bug&#xff0c;是每一个测试人员值得深思的问题&#xff0c;如果bug提交的不规范&#xff0c;不准确会导致开发人员理解错误&…

Python工具箱系列(五十一)

九宫格与词云 对图片进行九宫格切割&#xff0c;并且放到微信朋友圈曾经风靡一时。对于python来说&#xff0c;这个也非常简单。 from PIL import Image import mathdef ninerectanglegrid(inputfilename):"""实现九宫格切割Args:inputfilename (string): 输入…

golang import引用项目下其他文件内函数

初始化项目 go mod init [module名字] go mod init project 项目结构 go mod 文件 代码 需要暴露给外界使用的变量/函数名必须大写 在main.go中引入&#xff0c;当前项目模块名/要引用的包名 package mainimport (// 这里的路径开头为项目go.mod中的module"project/…

DHCP设置二

华为ensp模拟实验 准备工作 需要设备&#xff1a;路由器 一台 交换机 两台 pc两台 ip划分网段 &#xff1a;192.168.10.0 24 192.168.20.0 24 当我们准备好之后就可以开机了 开机实验 点击菜单栏小三角&#xff0c;开启设备。 输入system-view进入系统视图&#x…

深度解析:Elasticsearch检索请求原理

在上一篇文章中&#xff0c;我们学习了 Elasticsearch 的写入流程&#xff0c;今天我们来学习一下 Elasticsearch 的读取流程&#xff0c;当一个检索请求到达 Elasticsearch 之后是如何进行检索的呢&#xff1f; 下面先说一下一个总的检索流程。 1、客户端发送请求到任意一个…

卓翼飞思全国合作伙伴火热招募,共筑智能生态

摘要&#xff1a;市场有可为&#xff0c;政策高扶持 抢占AI高地&#xff0c;共筑智能生态 卓翼飞思全国合作伙伴招募计划 在人工智能的浪潮下&#xff0c;我们正见证一个由数据驱动、智能主导的新时代的到来。无人智能技术作为人工智能领域的重要分支&#xff0c;正在以其独特…

Qt篇——Qt无法翻译tr()里面的字符串

最近遇到使用Qt语言家翻译功能时&#xff0c;ui界面中的中文都能够翻译成英文&#xff0c;但是tr("测试")这种动态设置给控件的中文&#xff0c;无法翻译&#xff08;lang_English.ts文件中的翻译已经正确添加了tr()字符串的翻译&#xff09;。 上网搜了很多资料&am…

Linux 常用命令(1)

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;专栏系列&#xff1a;Linux 随笔集合 …

【漏洞复现】通天星CMSV6 admin 弱口令漏洞(CVE-2024-29666)

0x01 产品简介 CMSV6平台是基于车辆位置信息服务和实时视频传输服务的创新技术和开放运营理念。为GPS运营商车辆硬件设备制造商、车队管理企业等车辆运营相关企业提供核心基础数据服务。 0x02 漏洞概述 CMSV6 7.31.0.2、7.32.0.3版本中存在弱密码漏洞&#xff0c;未授权的攻…

【Linux】图文详解Xshell远程连接服务器:以Amazon EC2 VPS为例

文章目录 问题描述解决方案Q&A 问题描述 本地cmd或powershell使用ssh -i “your.pem” user_nameip_address是可以登录Amazon EC2云服务器的。 然而&#xff0c;当使用XShell以SSH加载PEM文件方式登录亚马逊EC2云服务器&#xff0c;一直出现输入密码的问题&#xff0c;如…