C++ —— string类(上)

目录

string的介绍

string类功能的使用介绍

constructor —— 构造

介绍使用(1)(2)(4) :构造、拷贝构造、带参构造

 介绍(3):拷贝string类对象的一部分字符,从pos位置开始,拷贝len个字符

 npos

介绍(5):取字符串的前n个字符拷贝

介绍(6) :连续n个C字符初始化

介绍(7):用迭代器区间构造

destructor —— 析构

operator= —— 赋值重载

string类对象的容量操作

size()  length()    max_size()

capacity()

reserve() 和 resize()

clear()

empty()

遍历及访问string所需的一些接口

operator[] —— []运算符重载

iterator —— 迭代器 (正向)

begin()

end()

reverse_iterator —— 反向迭代器

rbegin和rend

const修饰的迭代器

范围for

auto

 typeid().name()


string的介绍

string严格来说是属于C++标准库的,不是属于STL(标准模板库)。

STL:

我们看到里面其实是没有string的,string产生的比STL较早的,在头文件中,会单独看到一个string的头文件。C++文档

所谓的string其实就是一个字符串 ,它要解决的就是要管理字符串。(日常当中字符串是很多的,如:身份证号码,家庭地址,电话号码等等)

打开string头文件可以看到:

 有4种,这个与所谓的编码有关系,这里我们先看string:

由于编码的原因,string是typedef出来的,它的原生类型是:

//原生类型
basic_string<char>

 除了char,还有其他字符的类型(编码的原因)

//原生类型
basic_string<char16_t>

//原生类型
basic_string<char32_t>basic_string<wchar_t>

注意:basic_string其实是一个模板,所以string底层还是一个模板。

 所以,string是一个管理字符串的类。

string的介绍:

总的来说,string是一个对象,是用一个字符的顺序表实现的(也就是字符数组实现的),所以它就是一个字符顺序表(或字符数组),它可以动态的增长。(可以主要兼容UTF-8)

 string底层大致成员变量如下:

class string
{
public:/*成员函数(功能接口)*/
private:char* str;   //指针指向堆上的空间,这些空间存储对应字符串size_t size;   //有效字符个数size_t capacity;  //容量
};

下面将介绍string类的常用接口说明。 

string类功能的使用介绍

string主要包含接口如下:

我们要使用string类要记得包含一个头文件:

#include <string>   //C++//C语言的是
#include<string.h>    //注意两者区分

constructor —— 构造

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

但string构造总的来说是有七种构造方式的:

我们来看C++98版本的,string它有七种构造方式 ,下面来介绍一下:

default (1)	
string();        //无参构造(也是默认构造,构造一个空字符串,长度为0个字符)copy (2)	
string (const string& str);    //拷贝构造substring (3)	
string (const string& str, size_t pos, size_t len = npos);   //子串构造函数(复制str中,从字符位置pos开始,到len位置字符的部分)from c-string (4)	
string (const char* s);    //带参构造(用一个常量字符串构造)from sequence (5)	
string (const char* s, size_t n);    //拷贝前n个字符初始化fill (6)	
string (size_t n, char c);    //连续n个C的字符去初始化range (7)	
template <class InputIterator>
string  (InputIterator first, InputIterator last);    //迭代器区间初始化
介绍使用(1)(2)(4) :构造、拷贝构造、带参构造

我们先来介绍使用(1)(2)(4):

(string重载了流插入、流提取)

#include<iostream>
#include<string>using namespace std;int main()
{string s1;           //默认构造string s2("12345");  //带参构造string s3(s2);		 //拷贝构造//string也重载了流插入流提取(>>,<<)cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cin >> s1;cout << s1 << endl;return 0;
}

 介绍(3):拷贝string类对象的一部分字符,从pos位置开始,拷贝len个字符
substring (3)	
string (const string& str, size_t pos, size_t len = npos);   //拷贝构造的一个变形//也就是拷贝一部分字符,从pos位置开始,拷贝len个字符
//接着上面演示的构造代码
int main()
{string s1;           //默认构造string s2("1234567");  //带参构造string s3(s2);		 //拷贝构造//string也重载了流插入流提取(cout,cin)cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cin >> s1;cout << s1 << endl;string s4(s2, 2, 4);  //这里就是让s4去拷贝s2的下标为2的位置开始,拷贝4个字符cout << s4 << endl;return 0;
}

我们观察输出结果,看到从3开始拷贝四个字符到6,3456四个字符就到s4里去了。若此时这里给的字符超过字符串长度,会不会报错呢?

string s2("1234567"); 
string s4(s2, 2, 15);   //让它从pos=2开始拷贝15个字符

我们观察结果,是没有报错的:

文档中: 

 

也就是说string太短了,不够len个字符,它就直到它结束为止(有多少拷贝多少 );这里还说或者为npos也是拷到它的结束。

第三个构造它的len是有缺省值npos的,如果不传第三个参数len,那么它就用这个缺省值,直到拷贝它的结束为止。

string s2("1234567"); 
string s4(s2, 2);
cout<<s2<<endl;
cout<<s4<<endl;

运行结果:

 下面对npos介绍一下

 npos

npos是一个string的const静态的成员变量,它可以直接在类里面用,若在外面用的时候,需要指定类域。

npos的值真实的值其实不是-1,在这里-1存的就是补码(-1补码是全1) ,这里又赋值给size_t类型,size_t是unsigned int(无符号整型),-1就会变成整型最大值。

所以这里npos要表达的是:从pos位置取len个字符,如果len给npos,就是要取整型的最大值,

字符串不可能有这么长(字符串中遇到\0会停止),且又给了整型的最大值了,就是要让它拷贝到字符串的结束。

介绍(5):取字符串的前n个字符拷贝
string (const char* s, size_t n);    //第(4)个(带参构造)的变形//取字符串的前n个字符拷贝

 

也就是取一个字符串的前n个字符初始化。

测试一下: 

int main()
{string s6("hello world", 5);cout << s6 << endl;return 0;
}

 运行结果:

介绍(6) :连续n个C字符初始化
string (size_t n, char c);    //n个C字符初始化

也就是连续n个C的字符初始化,例如:'X'就是一个C字符。

int main()
{string s7(10, 'X');    //用10个X字符初始化cout << s7 << endl;return 0;
}

运行结果: 

介绍(7):用迭代器区间构造
int main()
{string s0("Initial string");string s8(s0.begin(), s0.begin() + 7);  //迭代器区间构造cout <<"s0: "<< s0 << endl;cout <<"s8: " << s8 << endl;
}

运行结果:

可以看到s8中用了s0起始位置开始的7个字符初始化了 。

destructor —— 析构

string的析构函数会自动调用,string的底层是一个动态开辟的数组,构造和析构函数是自动调用的(类和对象这里有介绍)。

operator= —— 赋值重载

赋值运算符重载也是默认成员函数(类和对象有介绍)。

string (1)	
string& operator= (const string& str);  //支持string的赋值c-string (2)	
string& operator= (const char* s);      //支持char*的赋值character (3)	
string& operator= (char c);             //支持一个字符的赋值

 测试:

int main()
{string s1;           string s6("hello world", 5);s1 = s6;//本质就是调用了operator=cout << s1 << endl;s1 = "abcde";cout << s1 << endl;s1 = 'x';cout << s1 << endl;return 0;
}

运行结果:

string类对象的容量操作

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
max_size获取最大长度(能最大开多长)
capacity返回空间容量的大小
empty检查字符串释放为空串,若是空串返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间
resize将有效字符的个数改成n个,多出的空间用字符c填充

size()  length()    max_size()

size()和 length()它们两个功能上完全一致,只是一般size用的比较多,是为了与其他容器接口保持一致。

这里拿size()来说,string提供了一个size的接口,它是用来获取string的长度(有多少个字符)。

 max_size()是用来获取它最大的长度,就是它最大能开多长

int main()
{string s("FFDUST");cout << s.length() << endl;cout << s.size() << endl;cout << s.max_size() << endl;
}

capacity()

 capacity()接口也就是返回该string当前空间容量的大小是多少

int main()
{string s("FFDUST");cout << "s的空间大小为:"<<s.capacity() << endl;
}

 

(注意它和size,length接口的区别)

这里还要注意容量扩容问题(每个编译器处理扩容几倍不一致),观察下面程序:

int main()    
{string s;size_t sz = s.capacity();cout << "capacity : " << sz << '\n';cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}return 0;
}

 VS下结果:

 所以VS下,扩容是先扩2倍,再后面扩1.5倍。

reserve() 和 resize()

 reserve()接口就是先给该对象预留多少空间,不改变有效元素个数,若reserve的参数小于string的底层空间总大小时,reserve不会改变容量大小(这里VS下不会缩容,其他编译器可能会缩容,C++规定没有约束)。

int main()
{string s("FFDUST");cout << "s的空间大小为:" << s.capacity() << endl;s.reserve(100);cout << "s的空间大小为:" << s.capacity() << endl;s.reserve(1);cout << "s的空间大小为:" << s.capacity() << endl;return 0;
}

VS这里会做整数倍对齐,所以这里capacity可能会比传的参数较大一些。

注意:谈string容量的时候默认不包含\0的,所以开空间实际上会多1位用来放\0,但capacity返回的是不带\0的。

reserve的用法:提前开空间,避免扩容,提高效率。

int main()
{string s;s.reserve(100);size_t sz = s.capacity();cout << "making s grow:\n";for (int i = 0; i < 100; ++i){s.push_back('c');if (sz != s.capacity()){sz = s.capacity();cout << "capacity changed: " << sz << '\n';}}return 0;
}

这里就是给s预留了足够大的空间,再插入时候不会发生扩容。 

resize()接口(将有效字符的个数改成n个,多出的空间用字符c填充),相当于扩容插入。

这里它是有两个版本的:resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0(也就是\0)来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意的是:resize若比size小的话会删除数据(缩容不确定),比capacity大就会扩容。

int main()
{string s("ffdust");cout << s << endl;cout << "s的空间大小为:" << s.capacity() << endl;s.resize(100);      //默认插入\0cout << s << endl;cout << "s的空间大小为:" << s.capacity() << endl;
}

int main()
{string s("ffdust");cout << s << endl;cout << "s的空间大小为:" << s.capacity() << endl;s.resize(100,'1');        //用字符1填充多出的元素空间,注意这里会扩容cout << s << endl;cout << "s的空间大小为:" << s.capacity() << endl;}

 

 clear()

clear()也就是清空有效字符,一般严格来说不会清理掉容量,只是把数据清理掉了

int main()
{string s("ffdust");cout << s << endl;cout << &s << endl;cout << "s的空间大小为:" << s.capacity() << endl;s.clear();cout << s << endl;cout << &s << endl;cout << "s的空间大小为:" << s.capacity() << endl;return 0;
}

 运行结果:

 empty()

empty()接口时用来判空的(有没有字符),也就是检测字符串是否为空串,若是返回true,否则返回false。

int main()
{string s("ffdust");cout << s << endl;if (s.empty()){cout << s << ",是空串" << endl;}else{cout << s << ",不是空串" << endl;}cout << "s的空间大小为:" << s.capacity() << endl;s.clear();cout << s << endl;if (s.empty()){cout << s << ",是空串" << endl;}else{cout << s << ",不是空串" << endl;}cout << "s的空间大小为:" << s.capacity() << endl;return 0;
}

运行结果:

遍历及访问string所需的一些接口

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

遍历string有三种方式:

  1. 下标+[]
  2. 迭代器
  3. 范围for

operator[] —— []运算符重载

string重载operator[],访问pos位置的字符,返回这个字符的引用。 (可以读和修改)

string重载了[],这样就可以让string当成数组一样使用下标访问。string底层意思大致理解是有一个指针,指针指向一个数组,是动态开辟的,它还要有size和capacity,重载operator[],就可以让它去访问第i个位置的字符:

class string
{
public:char& operator[](size_t i){return _str[i];           //返回第i字符的引用(这里引用返回,不只是可以减少拷贝,还能让它被修改)}
private:char* str;size_t size;size_t capacity;
};

使用如下:

int main()
{string s6("hello world");cout << s6 << endl;s6[5] = 'a';     //将下标为5的字符修改成acout << s6 << endl;return 0;
}

运行结果:

就相当于用数组一样,可以获取这个位置的字符,也可以修改这个位置的字符,但这里的底层就是函数调用,调用的operator[],注意:这里越界了可以检查出来的。 

char& operator[](size_t i)
{assert(i < _size);   //这里断言,只要越界了就直接报错return _str[i];
}

我们看到这里15位置越界访问了,就报错了。

所以,我们可以搭配着size接口防止越界访问来一个一个字符的遍历:

int main()
{string s6("hello world1111");//下标+[]for (int i = 0; i < s6.size(); i++){cout << s6[i] << " ";}cout << endl;return 0;
}

 运行结果:

iterator —— 迭代器 (正向)

 迭代器遍历:

int main()
{string s6("hello world");//迭代器遍历——正向string::iterator it = s6.begin();while (it != s6.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

正向遍历(begin+end) :

注:迭代器是STL六大组件之一,迭代器是用来遍历和访问这些容器的 。(迭代器有可能是原生指针,也有可能不是)。 

迭代器是类似于一个指针一样的东西,通过解引用可以访问对应位置的数据,迭代器还规定,不管它底层是怎么定义的,它是属于对应容器的类域。(上面string的迭代器属于string的类域,所以要写成 string::iterator),所以所有的容器都可以通过迭代器来访问(通用的访问容器的方式)。

 begin()

begin()就是返回开始位置的迭代器。

string这里就是返回的是第一个有效字符的位置的迭代器。

注:这里有两个版本,第一个就是普通对象用来调用的,第二个是const修饰的对象的const迭代器所调用(这里普通对象也能调用,但是只能读,不能写)。

end()

end()返回的是最后一个有效数据的下一个位置的迭代器。

string这里就是返回最后一个有效字符的下一个位置的迭代器。

注:这里有两个版本,第一个就是普通对象用来调用的,第二个是const修饰的对象的const迭代器所调用(这里普通对象也能调用,但是只能读,不能写)。

reverse_iterator —— 反向迭代器

 这里就与iterator完全相反着了

 反向遍历(rbegin+rend): 

int main()
{//反向迭代器遍历// string::reverse_iterator rit = s6.rbegin();// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型auto rit = s6.rbegin();while (rit != s6.rend()){cout << *rit <<" ";++rit;}cout << endl;
}

 运行结果:

rbegin和rend

这两个接口就相当于begin与end接口相反的。  

也就是rbegin获取最后一个字符迭代器 , rend获取第一个字符前一个位置的迭代器

const修饰的迭代器

 const修饰的对象,要用迭代器遍历时,是需要const修饰的迭代器的,它不能使用普通迭代器,

const迭代器修饰的是迭代器的指向,而不是迭代器的本身(类似于const修饰指针)

如下所示:

int main()
{//const对象const string cs("const hello world");//const修饰的(正向)迭代器string::const_iterator cit = cs.begin();  //这里也可以用cbegin接口,不过begin接口有提供的const修饰的版本,这就已经够用了while (cit != cs.end())     //cend同上也可以用{//错误	C3892	“cit” : 不能给常量赋值//*cit += 'x';cout << *cit << " ";++cit;}cout << endl;//const修饰的反向迭代器string::const_reverse_iterator crit = cs.rbegin();   //这里也可以用crbegin接口,不过rbegin接口有提供的const修饰的版本,这就已经够用了while (crit != cs.rend())    //crend同上也可以用{//错误	C3892	“crit” : 不能给常量赋值//*crit += 'x';cout << *crit << " ";++crit;}cout << endl;return 0;
}

 运行结果:

const修饰的迭代器,普通对象也是可以使用的(也就是权限可以缩小,但不能放大),不过只能读,不能写。

int main()
{//普通对象string s("hello world");//const修饰的迭代器string::const_iterator it = s.begin();while (it != s.end()){//错误	C3892	“it” : 不能给常量赋值//*it += 'x';cout << *it << " ";++it;}cout << endl;//const修饰的反向迭代器string::const_reverse_iterator rit = s.rbegin();while (rit != s.rend()){//错误	C3892	“rit” : 不能给常量赋值//*rit += 'x';cout << *rit << " ";++rit;}cout << endl;return 0;
}

运行结果:

 (这里也可以用auto自动推导它的类型,方便代码的编写)

范围for

  • 对于一个有范围的集合而言,C++11中引入了基于范围的for循环。for循环后的括号由冒号分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。
  • 范围for可以作用到数组和容器对象上进行遍历。
auto
在早期 C/C++ auto 的含义是:使用 auto修饰的变量,是具有自动存储器的局部变量。 C++11 中,赋予了 auto 全新的含义即: auto 不再是一个存储类型 指示符,而是作为一个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期 推导而得

范围for和auto就可以配合使用来遍历string:
int main()
{string s6("hello world");//字符赋值,自动迭代,自动判断结束for (auto e : s6)  //auto这里自动推导类型为char{cout << e << " ";}cout << endl;return 0;
}

 运行结果:

所以这里范围for想表达的是:

  • 注意:范围for的底层角度是跟迭代器一样的,也就是说范围for这段代码被编译了之后会被替换成迭代器。(所以要支持范围for前提是要支持迭代器遍历)

从汇编层可以看到,这里就是被替换成了迭代器: 

 范围for跟迭代器就相当于没有区别,范围for只是方便编写代码。这里对比迭代器,也展示出auto的价值就是缩短代码(简化代码),不过这样会使代码可读性降低。

//这两个it的类型是一致的
string::iterator it = s6.begin();//auto会自动推导成上面的string::iterator
auto it = s6.begin();

  • 迭代器是可以修改的。
int main()
{string::iterator it = s6.begin();cout << s6 << endl;while (it != s6.end()){*it += 2;           //让里面的每一个值都加等2cout << *it << " ";++it;}cout << endl;for (auto e : s6){cout << e << " ";}cout << endl;return 0;
}

 运行结果对比一下:

可以看到每一个值都被改变了,范围for打印数据也受到影响。范围for里也是可以修改数据的,下面在范围for里面打印原来的值:

int main()
{string::iterator it = s6.begin();cout << s6 << endl;while (it != s6.end()){*it += 2;           //让里面的每一个值都加等2cout << *it << " ";++it;}cout << endl;for (auto e : s6){e -= 2;           //只需要让e减等2即可cout << e << " ";}cout << endl;return 0;
}

运行结果:

  •  注意:这里有坑的是,范围for打印出来的结果看似把s6恢复原状了,这时cout一下s6可以看到s6还是没有变回去的。这个原因是:

这里若想在范围for里修改,只需要加& 符号,也就是引用

for (auto& e : s6)    //这里auto自动推导为char类型,//若想是char的引用,就需要主动添加一个&
//这时e就是s6里面(*it)的别名,也就是取它每个字符的别名
{e -= 2;           //这时修改e,就修改了string里的值cout << e << " ";
}
cout << endl;

注意:用auto声明指针类型时,用auto和auto*没有任何区别(也就是当是一个指针类型时,可以直接写auto即可),但用auto声明引用类型时则必须加上&(auto &)。 

 typeid().name()

 typeid()可以帮助我们观察类型

typeid(变量名).name()   //可以看到该变量的类型
int func1()
{return 10;
}int main()
{int a = 10;auto b = a;auto c = 'a';auto d = func1();cout << typeid(b).name() << endl;cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;int x = 10;auto y = &x;auto* z = &x;auto& m = x;cout << typeid(x).name() << endl;cout << typeid(y).name() << endl;cout << typeid(z).name() << endl;return 0;
}

运行结果: 

 

这里auto使用时还需要注意一些事项: 

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

不能这样定义,它不知道e到底是多大,auto定义的变量类型一定是由右边的变量或者函数调用表达式的返回值推导的。

// 不能做参数
void func2(auto a) //这里就算给了缺省值也不支持的
{}

 auto不能做参数类型,但auto可以做返回值。

// 可以做返回值,但是建议谨慎使用
auto func3()
{return 3;
}

// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto cc = 3, dd = 4.0;

注意在一行里面定义,定义时要用同一类型

// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] = { 4, 5, 6 };

auto不能定义一个数组(C++规定)

在这篇先总结一部分string的接口使用,剩下的部分接口总结使用在后续文章介绍。

制作不易,若有不足之处或出问题的地方,请各位大佬多多指教 ,感谢大家的阅读支持!!!   

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

关于Qt C++中connect的几种写法

目录 1. 传统的槽函数写法 2. 使用函数指针的connect写法&#xff08;5.0&#xff09; 3. Lambda表达式作为槽函数&#xff08;C11&#xff09; 4.使用QOverload选择重载信号的写法 这connect函数就像是编程世界里的“茴”字&#xff0c;千变万化&#xff0c;各有千秋。咱们…

(二)PyTorch简要教学

文章目录 PyTorch教学一、训练一个神经网络二、用PyTorch是怎么去做的&#xff08;一&#xff09;Dataset & DataLoader&#xff08;二&#xff09;Tensors&#xff08;1&#xff09;Tensor是什么&#xff08;2&#xff09;怎么获得Tensor&#xff08;3&#xff09;Tensor相…

GRPC实现

1.首先下载对应编译插件&#xff0c;这里不再提供下载 2.编写proto文件 3.编写完成用命令生成go文件 protoc --go_out. --go-grpc_out. *.proto --go_out. 其中的. 是说你要编译的 .proto 文件目录为当前目录&#xff0c;按需修改 --go-grpc_out.&#xff0c;其中的. 是说你生…

Path does not exist: file:/D:/pythonProject/spark/main/datas/input/u.data

出现标题中的错误原因可能是&#xff1a; 1.文件路径书写错误&#xff1b; 2.文件本身不存在。 从图中可以看出&#xff0c;数据源文件是存在的&#xff0c;但是读取不到文件&#xff0c;说明代码中的文件路径写错了&#xff0c;从报错的结果可以看出&#xff0c;python在D:/…

Wallpaper壁纸制作学习记录01

导入图像 打开wallpaper软件&#xff0c;找到下方的播放列表&#xff0c;选择壁纸编辑器。 弹出下列界面&#xff0c;在创建壁纸处可以选择图片拖入。 在开始导入任何图像之前&#xff0c;请首先确保主背景图像表示实际屏幕分辨率。展示示例图像是 1920 x 1080&#xff0c;这…

【知识科普】统一身份认证CAS

什么是CAS 综合概述一、CAS概述二、CAS的组成与工作原理三、CAS的特性与支持四、CAS的应用场景 示例展示场景设定CAS认证过程 其他认证细节CAS认证过程的细节CAS认证过程的特性 参考文献 综合概述 统一身份认证CAS&#xff08;Central Authentication Service&#xff09;&…

计算机网络-MSTP工作原理

前面大致学习了MSTP基础概念与组成&#xff0c;今天来了解MSTP的工作原理。 一、MSTP拓扑计算&#xff1a; MSTP可以将整个二层网络划分为多个MST域&#xff0c;各个域之间通过计算生成CST&#xff0c;域内生成IST&#xff0c;CST和IST构成了整个交换设备网络的CIST。 域内还可…

面试题:Kafka(一)

1. Kafka如何保证消息不丢失 生产者发送消息到Brocker丢失 设置异步发送 消息重试 消息在Brocker中存储丢失 发送确认机制acks 消费者从Brocker接收消息丢失 Kafka 中的分区机制指的是将每个主题划分成多个分区&#xff08;Partition&#xff09;topic分区中消息只能由消费者…

odoo 创建应用

1、通过手脚架命令创建文件结构&#xff08;完全可以手动一个个文件/文件夹建&#xff09; odoo-bin scaffold <模块名> <模块放置路径> 在odoo根目录路径执行命令行&#xff1a;./odoo-bin scaffold my_library exte_addons 执行结果&#xff1a; 参考文章&…

十四、SpringMVC的执行流程

文章目录 1. SpringMVC常用组件2. DispatcherServlet初始化过程3. DispatcherServlet调用组件处理请求4. SpringMVC的执行流程 1. SpringMVC常用组件 2. DispatcherServlet初始化过程 3. DispatcherServlet调用组件处理请求 4. SpringMVC的执行流程

java基础概念30:常见API-System

一、System工具类的常用方法 System也是一个工具类&#xff0c;提供了一些与系统相关的方法。 计算机的时间原点:1970年1月1日0:0:0&#xff0c;我国在东八区&#xff0c;有8小时时差。 二、常用方法说明 2-1、System.exit()方法 该方法用于终止当前运行的Java虚拟机&#x…

基于AOA算术优化的KNN数据聚类算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于AOA算术优化的KNN数据聚类算法matlab仿真。通过AOA优化算法&#xff0c;搜索最优的几个特征数据&#xff0c;进行KNN聚类&#xff0c;同时对比不同个数特征下…

三维测量与建模笔记 - 点特征提取 - 4.3 Harris特征点

在3D重建应用中&#xff0c;很重要的一个场景是找到两幅图像中的同名特征点&#xff0c;这个过程需要对特征点进行提取和描述。 从上面描述可以看出&#xff0c;如果窗口处于颜色变化不明显或者没有变化的区域&#xff0c;E的值很小或为0&#xff1b;如果窗口处于边缘位置&…

labview使用报表工具从数据库导出数据

之前写了一篇labview从数据库导出数据到excel电子表格&#xff0c;但是是基于调用excel的activeX控件&#xff0c;有时候会有一些bug&#xff0c;就比如我工作机就无法显示方法&#xff0c;后面大哥指点才知道没有的原因是excel安装不完整。像我的工作机就没有这个选项。就需要…

WIFI-TTL透传模块说明书

WIFI-TTL透传模块说明书 V 1.0 2022-11-24 目录 1 简介... 4 2 模块参数... 4 3 接口定义... 5 4 设备配网... 6 5 AT指令... 11 6 恢复出厂... 12 7 设备配置... 13 7.1 配置界面说明... 13 7.2 TTL串口配置... 13 7.3 …

基于YOLOv8深度学习的智慧交通非机动车驾驶员头盔佩戴检测系统

随着智慧交通系统的快速发展和城市交通的日益复杂化&#xff0c;非机动车驾驶员的安全问题引起了广泛关注&#xff0c;尤其在发生交通事故时&#xff0c;佩戴头盔作为基础的保护措施&#xff0c;能够有效降低头部受伤的风险&#xff0c;保障非机动车驾驶员的生命安全。然而&…

Flutter踩坑:原生安卓页面向Flutter通信

Flutter踩坑&#xff1a;原生安卓页面向Flutter通信 前言 在 Flutter APP 的开发过程中&#xff0c;有时不仅需要使用 Flutter 提供的组件&#xff0c;还需要使用原生的组件。 例如在对接外部 SDK 时&#xff0c;如果自己重新实现 SDK 的逻辑&#xff0c;无疑是本末倒置。 前…

w043基于springboot的“衣依”服装销售平台的设计与实现

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0…

【计算机网络安全】湖北大学-mysql事务隔离性实验

参考数据库实验&#xff1a;并发控制实验&#xff08;MySQL&#xff09;-CSDN博客&#xff0c;大佬写的很好 实验环境 实验需要mysql环境&#xff0c;如果我们本机有mysql客户端&#xff0c;或者安装过phpstudy都可以直接用&#xff0c;Kali似乎也有。 本机启动phpstudy&…

UE5 猎户座漂浮小岛 08 构造脚本

视频教程 90后小陈老师的个人空间-90后小陈老师个人主页-哔哩哔哩视频 1.构造脚本 1.1 简介 类似构造函数代码创建多个效率高 1.2 添加静态网格体 1.3 散乱立方体 使用for循环生产10个立方体 随机位置生成10个散乱的立方体 1.4 整齐立方体 1.5 随机物体 1.6 样条实现曲度墙…