C++初阶:STL详解(一)——string类

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:C++:由浅入深篇

小新的主页:编程版小新-CSDN博客

 1.为什么会有string类

C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数,但是要实现对字符串的增删查改等操作还是很麻烦的,而且稍不留神可能还会越界访问,所以我们封装了一个string类来处理字符串的各种问题。

2.标准库里的string类

2.1什么是string类

string类的特性:我们可以动态地存储和操作字符串,无需担心内存管理问题,因为它会自动处理字符串的存储和释放,并且支持各种字符串操作,如拼接,比较,查找,替换等。

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

下面我们就通过代码的示例了解一些string类的常见用法。

2.2string类的常用接口

1.string类对象的常见构造

void string1()
{string s1;  //构建一个空的string对象cout << s1 << endl;string s2("hello world");//直接用常量字符串来构造cout << s2 << endl;string s3(5,'x');//用5(n)个字符来构造string对象cout << s3 << endl;string s4(s2);cout << s4 << endl;//拷贝构造}

运行结果:

2.string类对象的容量操作

函数名称功能说明

size(重点)返回字符串有效字符长度

length返回字符串有效字符长度

capacity返回空间总大小

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

clear (重点)清空有效字符

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

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

void string2()
{string s1("hello world");//string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");cout << "s1:" << s1 << endl;cout <<"size:" << s1.size() << endl;cout << "length:" << s1.length() << endl;//size和length没有什么区别,只是size更具有通用性,更推荐使用cout << "capacity:" << s1.capacity() << endl;//class string//{//private://	char _buff[16];//	char*  _str;////	size_t _size;//	size_t _capacity;//};//capcacity就是我们之前所熟知的容量//这里补充说明一点:当容量不够时会自动扩容,通常是按1.5倍扩,这个和环境有关//在VS中,还特地设有buff存储数据当大于buff时就会将数据拷贝给str,这个过程是按2倍扩的,自后的都是按1.5倍扩cout <<"empty:"<< s1.empty() << endl;//0表示非空,1表示空s1.clear();  //清空字符串,不改变底层空间大小
cout << "s1:" << s1 << endl;s1.reserve(100); //制定开一定的空间(一般都会多开)
cout << "capacity:" << s1.capacity() << endl;s1.resize(5, 'x');
cout << "s1:" << s1 << endl;//j将有效字符的个数改成5(n)个,多出来的空间,用x填充
cout << "size:" << s1.size() << endl;s1.resize(20, 'k');
cout << "s1:" << s1 << endl;//j将有效字符的个数改成20(n)个,多出来的空间,用k填充
cout << "size:" << s1.size() << endl;}

运行结果:

注意:

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, charc)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

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

 小扩展:

我们再来扩展一下与capacity相关的小细节,上面我们即提到1.5倍扩,又提到了2倍扩。具体情况是这样的。

buff的空间开的是16个字节,有一个是‘\0’的位置,我们只能存15个有效字符,当我们要储存的数据小于15字符,该字符串会存到buff里面,而不是str里。

 

当要储存的字符串较大时,就会以buff的容量大小为基础进行扩容,首次是扩容2倍扩,如果空间还是不够,就会1.5倍的进行扩容直至开够足够的空间。 

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

函数名称

功能说明

operator[] (重

点)

返回pos位置的字符,const string类对象调用

begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位

置的迭代器

rbegin + rend

rbegin获取容器最后一个字符 + rend指向容器“反向的开头”,在实际的容器范围之外,它在第一个元素之前。

范围forC++11支持更简洁的范围for的新遍历方式
void string3()
{string s1("hello world");//三种遍历方法//1.下表+[]//2.迭代器(正向迭代器  反向迭代器)//3.范围for(看起来很高级,底层还是用迭代器实现的)//1.下表+[]for (int i = 0; i < s1.size(); i++){//s[i]+=2;cout << s1[i] << ' ';}//[]能直接访问,修改字符串的内容,把他当成我们所熟知的[]使用即可。cout << endl;//2.正向迭代器string::iterator it = s1.begin();while (it != s1.end()){cout << *it << ' ';it++;}cout << endl;//3.反向迭代器string::reverse_iterator rit = s1.rbegin();while (rit != s1.rend()){cout << *rit << ' ';rit++;}cout << endl;//范围for-自动赋值,自动迭代,自动判断结束for (auto& ch : s1){cout << ch << ' ';}cout << endl;
}

 运行结果:

在这里补充2个C++11的小语法。

auto关键字

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。(auto的功能简而言之就是能够自动推导出类型)

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&,当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

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

auto不能直接用来声明数组

int func1()
{return 10;
} //不能做参数
//void func2(auto a)
//{} error// 可以做返回值,但是建议谨慎使用
auto func3()
{return 3;
}void test()
{int a = 10;auto b = a;auto c = 'a';auto d = func1();// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项//auto e;   推断不出来e的类型 errorcout << 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;auto aa = 1, bb = 2;// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型//auto cc = 3, dd = 4.0; error// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型//auto array[] = { 4, 5, 6 }; error}

运行结果: 

使用场景举例: 

auto在一个变量的类型名很长的时候,就发挥了很大的作用,很便捷,但是也有弊端。

 auto自动推断类型
map<string, string> dict;
map<string, string>::iterator mit = dict.begin();
auto mit = dict.begin();//自动推导出mit的类型
使用起来更加便捷,但是代码的可读性会降低

范围for 

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束

范围for可以作用到数组和容器对象上进行遍历,范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

void test2()
{int array[] = { 1, 2, 3, 4, 5 };// C++98的遍历for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){array[i] *= 2;}for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i){cout << array[i]<<" ";} cout << endl;// C++11的遍历//自动迭代,自动取数据,自动判断结束for (auto& e : array)e *= 2;for (auto e : array)cout << e << " " ;cout << endl;string str("hello world");for (auto ch : str){cout << ch << " ";} cout << endl;}

运行结果:

4.string类对象的修改操作 

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
void string4()
{string s1("hello world");s1.push_back('x');cout << "push_back后:";cout << s1 << endl;  //尾插一个字符cs1.append("abc");cout << "append后:";cout << s1 << endl;//尾插一个字符串s1.operator+=("hello");cout << "operator+=后:";cout << s1<<endl;//尾插一个字符串cout << "c_str:";const char* str = s1.c_str();cout << str << endl;//返回C格式字符串//c_str主要是用来将string对象转化为C风格的字符串指针,用于传给const char*类型参数的函数cout << "find:";//size_t find (char c, size_t pos = 0) const;int num1 = s1.find('e');cout << num1 << endl;//pos位置开始往后找字符c,返回该字符在字符串中的位置(正着找)cout << "rfind:";//ize_t rfind (char c, size_t pos = npos) const;//npos static const size_t npos = -1;//his constant is defined with a value of - 1, which because size_t is an unsigned integral type,// it is the largest possible representable value for this type.//总结:npos是一个很大数,该类型的最大值int num2 = s1.rfind('e');cout << num2 << endl;//pos位置开始往前找字符c,返回该字符在字符串中的位置(倒着找)cout << "s1:" << s1 << endl;cout << "substr:";//string substr (size_t pos = 0, size_t len = npos) const;string tmp = s1.substr(2, 5);//从pos位置开始,截取n和字符,然后返回cout << tmp << endl;}

运行结果: 

注意:

1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5.string类非成员函数

函数功能说明

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

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

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

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

relational operators (重点)大小比较

void string5()
{string s1("hello world");string s2("xiaoxin");//string operator+ (const string & lhs, const string & rhs);cout << "operator+:";cout << operator+(s1, s2) << endl;//涉及深拷贝,效率低下,尽量少用cout << "operator+>>";//输入运算符重载string s3;operator>>(cin, s3);cout << s3 << endl;cout << "operator<<";operator<<(cout,s1) << endl;//getline 获取一行字符//getline与比如getchar,scanf之类的区别在于,后者的两个一般都是以空格,换行符分界//如果输入带空格的字符串,后者就会将其当成两个字符串,从而读不到空格//getline就能读到中间的空格,将其当成一个字符串//istream& getline(istream & is, string & str, char delim);// 第一种,delim默认是遇到换行符截止//istream& getline(istream & is, string & str);//这一种是遇到str截止//relational operators//这个函数库里包含了各种比较大小的函数,函数类型为bool类型//知道是这样的就可以std::string foo = "alpha";std::string bar = "beta";if (foo == bar) std::cout << "foo and bar are equal\n";if (foo != bar) std::cout << "foo and bar are not equal\n";if (foo < bar) std::cout << "foo is less than bar\n";if (foo > bar) std::cout << "foo is greater than bar\n";if (foo <= bar) std::cout << "foo is less than or equal to bar\n";if (foo >= bar) std::cout << "foo is greater than or equal to bar\n";}

运行结果:

 

 3.总结

以上介绍的都是string类的主要接口,还有一些string的接口不常用,感兴趣的可以可以自己学一下,下面是我自己整理的,还比较全面,希望有所帮助。下一篇我们就来介绍string类的模拟实现。

感谢各位的观看~

#include<iostream>
#include<string>
#include<vector>
using namespace std;//class string
//{
//private:
//	char _buff[16];
//	char* _str;
//	size_t size;
//	size_t capacity;
//
//};//构造
void string1()
{string s1;string s2("hello world");string s3(s2);cout << s1 << endl;cout << s2 << endl;cout << s2 << endl;//string (const string& str, size_t pos, size_t len = npos);//有多少拷贝多少string s4(s2, 6, 15);cout << s4 << endl;//npos是有缺省值的,可以不传string s5(s2, 6);cout << s5 << endl;//拷贝前n个字符string s6("hello world", 5);cout << s6 << endl;string s7(10, 'x');cout << s7 << endl;
}//三种遍历方式
void string2()
{string s1("hello world");//小标+[]for (size_t i = 0; i < s1.size(); i++){cout << s1[i] << ' ';}cout << endl;//迭代器string::iterator it = s1.begin();while (it != s1.end()){cout << *it << ' ';it++;}cout << endl;//范围for-自动赋值,自动迭代,自动判断结束//底层其实就是迭代器for (auto& ch : s1){cout << ch << ' ';}cout << endl;//auto自动推断类型//map<string, string> dict;//map<string, string>::iterator mit = dict.begin();//auto mit = dict.begin();//使用起来更加便捷,但是代码的可读性会降低}//迭代器
void string3()
{//iterator begin();string s2("hello world");string::iterator it = s2.begin();while (it != s2.end()){*it += 2;cout << *it << ' ';it++;}cout << endl;string::reverse_iterator rit = s2.rbegin();while (rit != s2.rend()){cout << *rit << ' ';rit++;}cout << endl;//const_iterator begin() const;const string s3("hello world");string::const_iterator cit = s3.begin();while (cit != s3.end()){//*cit += 2;cout << *cit << " ";++cit;}cout << endl;string::const_reverse_iterator rcit= s3.rbegin();while (rcit != s3.rend()){// *rcit += 2;cout << *rcit << " ";++rcit;}cout << endl;//只读不写//const_iterator cbegin() const noexcept;string::const_iterator csit = s3.cbegin();while (csit != s3.cend()){cout << *csit << " ";++csit;}cout << endl;string::const_reverse_iterator csrit = s3.crbegin();while (csrit != s3.crend()){cout << *csrit << " ";++csrit;}cout << endl;//begin()返回一个普通的迭代器,可读可写;cbegin()返回一个常量迭代器//只读不写
}void TestPushBack()
{// reverse 反转  逆置// reserve 保留、预留string s;// 提前开空间,避免扩容,提高效率s.reserve(10);size_t sz = s.capacity();//15cout << "capacity changed: " << 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';}}//31 47 70  105//刚开始是二倍扩,后面大概是1.5倍扩//因为刚开始是有个buff存在的,小于buff空间大小就存在buff里面//当大于buff的容量时,就会存到_str里面,地方发生转变的这次是2倍扩,后面在堆上的时候就//大概是1.5倍扩了
}void string4()
{string s2("hello world");//size和length没有区别,只是size更具有通用性,更推荐使用cout << s2.size() << endl;cout << s2.length() << endl;//都不包含\0cout << s2.max_size() << endl;cout << s2.capacity() << endl;//resize:将字符串的长度调整为n个字符//void resize(size_t n);//void resize(size_t n, char c);s2.resize(5);//多的部分用X填充s2.resize(12, 'x');cout << s2 << endl;string s1("hello worldxxxxxxxxxxxxx");cout << s1.size() << endl;//24//不够会扩容,一般是以1.5倍进行扩容cout << s1.capacity() << endl << endl;//31//在VS下一般不会缩容,但是不同的平台不一样,例如g++就会缩容s1.reserve(20);cout << s1.size() << endl;//24cout << s1.capacity() << endl << endl;//31//这里就没有缩容//缩容也有规定,缩容不会缩的比size小s1.reserve(28);cout << s1.size() << endl;//24cout << s1.capacity() << endl << endl;//31//扩容s1.reserve(40);cout << s1.size() << endl;//24cout << s1.capacity() << endl << endl;//47s1.clear();//清空字符串,但是容量不变cout << s1.size() << endl;//0cout << s1.capacity() << endl << endl;//47//判断字符串是否为空std::string str1 = "";if (str1.empty()) {std::cout << "字符串为空" << std::endl;}else {std::cout << "字符串不为空" << std::endl;}//std::string::shrink_to_fit//与reserve相比,这个更有约束力一些,该函数会请求减少string占用的空间//以适用于当前的内容大小,但是也不一定string s4("hello");s4.shrink_to_fit();cout << s4.size() << endl;//5cout << s4.capacity() << endl << endl;//15//原本大小就小于15的,这里就没有缩容std::string str(100, 'x');std::cout << "1. capacity of str: " << str.capacity() << '\n';//111str.resize(10);std::cout << "2. capacity of str: " << str.capacity() << '\n';//111str.shrink_to_fit();std::cout << "3. capacity of str: " << str.capacity() << '\n';//15//这里就缩容了
}//元素访问
void string5()
{//operator[]和at用法上没有区别//oparator[]里面是断言,越界会报错;但是at越界会抛异常,抛out_of_range的异常//back()函数用于获取字符串的最后一个字符//front用于获取字符串的第一个字符}//调节器modifier
void string6()
{//operator+=string s("hello world");s.operator+=('x');s.operator+=("hxxxxxxx");s.operator+=(s);s += 'y';s += "3333333";s += s;cout << s << endl;cout << endl << endl;//append//append除了和operator+=上面相同的用法外,还有下面的用法//1.string& append (const string& str, size_t subpos, size_t sublen);//用于将一个字符串str,从指定的subpos位置开始,长度为sublen追加到当前字符串的末尾string s1("hello world");s1.append(s1, 2, 4);cout << s1 << endl;//string& append (const char* s, size_t n);s1.append("xxxxx", 3);cout << s1 << endl;//string& append (size_t n, char c);s1.append(3,'h');cout << s1 << endl;//template <class InputIterator>//string& append(InputIterator first, InputIterator last);std::vector<char> vec = { 'w','r','o','l','d' };s1.append(vec.begin(), vec.end());cout << s1 << endl;//补充一点:first和last不一定就是起始位置和终止位置;也可以是自己指定位置//但是string类里只给了begin和end,这就需要自定义迭代器,现在我还不会//void push_back (char c);s1.push_back('l');//assign - 用来给字符串赋予新的值//string& assign(const string & str);string s3("hello world");s3.assign(s1);cout << s3 << endl;//string& assign(const string & str, size_t subpos, size_t sublen);s3.assign(s1, 5, 7);cout << s3 << endl;//string & assign(const char* s);s3.assign("xxxxx");cout << s3 << endl;//xxxxx//string& assign(const char* s, size_t n);s3.assign("hello world", 5);cout << s3 << endl;//string& assign(size_t n, char c);s3.assign(5, 's');cout << s3 << endl;//template <class InputIterator>//string& assign(InputIterator first, InputIterator last);std::vector<char> charvec = { 'h','e','l','l','o' };s3.assign(charvec.begin(), charvec.end());cout << s3 << endl;//insert//用法总结:在指定位置插入单个字符、字符串,另一个字符串的一部分//使用迭代器进行插入操作//replace//用于替换字符串里的一部分内容//erase//string& erase (size_t pos = 0, size_t len = npos);//有缺省值可以不给s3.erase(0, 3);//从第几个位置删除几个字符cout << s3 << endl;s3.erase(s3.begin());//删除第一个字符cout << s3 << endl;s3.erase(--s3.end());//尾删cout << s3 << endl;//end()并不是指向最后一个字符,而是最后一个字符的下一个位置//swap//void swap(string & str);s3.swap(s1);//交换s1与s3的内容cout << s3 << endl;cout << s1 << endl;//pop_back//删除最后一个字符}void string7()
{//c_str-返回字符串的指针//data也是返回一个字符串的指针//copy和substr都可用于拷贝字符或者字符串//size_t copy (char* s, size_t len, size_t pos = 0) const;//copy是将string里的内容拷贝到外部的字符串数组s中;// string substr (size_t pos = 0, size_t len = npos) const;//而substr是将string里提取子字符串返回一个新的string对象//find是找一个字符,字符串,string对象;rfind是倒着找// 找到返回索引;找不到返回string::npos//find_first_ofstd::string str("Please, replace the vowels in this sentence by asterisks.");std::cout << str << '\n';std::size_t found = str.find_first_of("abcdef");//找到任意一个都返回索引while (found != std::string::npos){str[found] = '*';found = str.find_first_not_of("abcdef", found + 1);}//find_last_of是倒着找//find_first_not_of是找不到返回索引;find_last_not_of是倒着找,找不到返回索引//cpmpare不参与,用的比较多的运算符重载//get_allocator还不会}void string8()
{//oparator+  用于字符串的拼接// 可以连接两个string对象;string对象和字符串字面量;字符串字面量和string对象//这里解释一下要实现字符串字符串字面量+string对象的底层;该函数不是成员函数,或者将其设为全局函数//举个例子,双目操作符的左操作数默认为类对象,字符串字面量+string对象这个就行不通string s1("hello");string s2 = s1 + "world";cout << s2 << endl;string s3 = "world" + s1;cout << s3 << endl;//swap//这个和上面的一个的使用方式不同,两个参数//void swap (string& x, string& y);//getline//getline与比如getchar,scanf之类的区别在于,后者的两个一般都是以空格,换行符分界//如果输入带空格的字符串,后者就会将其当成两个字符串,从而读不到空格//getline就能读到中间的空格,将其当成一个字符串//istream& getline(istream & is, string & str, char delim);// 第一种,delim默认是遇到换行符截止//istream& getline(istream & is, string & str);//这一种是遇到str截止
}int main()
{string8();//TestPushBack();return 0;
}
///// 范围for和auto的扩展//auto不能做参数,但是可以做返回值,不过不建议使用
//范围for适用于容器和数组//int func1()
//{
//	return 10;
//}
//不能做参数
void func0(auto a = 0)
{}//但是能做返回值,不过不建议使用
//auto func2()
//{
//	//...
//	return func1();
//}
//
//auto func3()
//{
//	//...
//	return func2();
//}//int main()
//{
//	int a = 10;
//	auto b = a;
//	auto c = 'a';
//	auto d = func1();
//	// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
//	//auto e;
//	cout << typeid(b).name() << endl;
//	cout << typeid(c).name() << endl;
//	cout << typeid(d).name() << endl;
//
// auto不能用于数组
//	//auto array[] = { 4, 5, 6 };
//
//	auto ret = func3();
//
//	int array[] = { 1, 2, 3, 4, 5 };
//	// C++98的遍历
//	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
//	{
//		array[i] *= 2;
//	}
//	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
//	{
//		cout << array[i] << endl;
//	}
//	
//	// yyds
//	// 范围for适用于容器 和 数组
//	// C++11的遍历
//	for (auto& e : array)
//		e *= 2;
//
//	for (auto e : array)
//		cout << e << " " << endl;
//
//	return 0;
//}

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

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

相关文章

驾驭不断发展的人工智能世界

从很多方面来看&#xff0c;历史似乎正在重演。许多企业正争相采用生成式人工智能 (Gen AI)&#xff0c;就像它们争相采用云计算一样&#xff0c;原因也是一样的&#xff1a;效率、成本节约和竞争优势。 然而&#xff0c;与云一样&#xff0c;GenAI 仍是一项发展中的技术&…

Kafka 分布式消息系统详细介绍

Kafka 分布式消息系统 一、Kafka 概述1.1 Kafka 定义1.2 Kafka 设计目标1.3 Kafka 特点 二、Kafka 架构设计2.1 基本架构2.2 Topic 和 Partition2.3 消费者和消费者组2.4 Replica 副本 三、Kafka 分布式集群搭建3.1 下载解压3.1.1 上传解压 3.2 修改 Kafka 配置文件3.2.1 修改z…

网络原理之TCP协议(万字详解!!!)

目录 前言 TCP协议段格式 TCP协议相关特性 1.确认应答 2.超时重传 3.连接管理&#xff08;三次握手、四次挥手&#xff09; 三次握手&#xff08;建立TCP连接&#xff09; 四次挥手&#xff08;断开连接&#xff09; 4.滑动窗口 5.流量控制 6.拥塞控制 7.延迟应答…

gazebo 已加载模型但无法显示

目录 写在前面的话问题一&#xff1a;robot_state_publisher 发布机器人信息失败报错一 Error: Error document empty.报错二 .xcaro 文件中有多行注释成功启动 问题二&#xff1a;通过 ros2 启动 gazebo 失败成功启动 问题三&#xff1a;gazebo 崩溃和无法显示模型问题四&…

【Kubernetes】K8s 的安全框架和用户认证

K8s 的安全框架和用户认证 1.Kubernetes 的安全框架1.1 认证&#xff1a;Authentication1.2 鉴权&#xff1a;Authorization1.3 准入控制&#xff1a;Admission Control 2.Kubernetes 的用户认证2.1 Kubernetes 的用户认证方式2.2 配置 Kubernetes 集群使用密码认证 Kubernetes…

Pr:首选项 - 音频

Pr菜单&#xff1a;编辑/首选项 Edit/Preferences Premiere Pro 首选项中的“音频” Audio选项卡主要作用是控制音频的处理设置&#xff0c;包括音量调整、波形生成、音频渲染等选项&#xff0c;这些设置有助于优化音频的处理和编辑工作&#xff0c;适用于不同的剪辑需求和项目…

VS Code 调试go程序的相关配置说明

用 VS code 调试Go程序需要在.vscode/launch.json文件中增加如下配置&#xff1a; // launch.json {// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.…

RISC-V (十二)系统调用

系统模式&#xff1a;用户态和内核态 当前的代码都是实现在machine模式下。 系统模式的切换 epc寄存器的值存放的是ecall指本身的地址 。 用ecall指令 系统调用的执行流程 mret这条指令会利用status的mpp值恢复到之前的特权级别。 蓝色的线表示涉及到权限切换。 系统调用的传…

想要从OPPO手机恢复数据?免费OPPO照片视频恢复软件

此实用程序可帮助那些寻找以下内容的用户&#xff1a; 在OPPO手机中格式化存储卡后可以恢复图片吗&#xff1f;我删除了 OPPO上的视频和图片&#xff0c;我感觉很糟糕&#xff0c;因为里面有我在拉斯维加斯拍摄的视频和照片 免费OPPO照片视频恢复软件 您能恢复OPPO上已删除的…

JavaScript拷贝的艺术:玩转深拷贝和浅拷贝

前言 在实际的项目开发中&#xff0c;我们时刻都在使用数据拷贝功能&#xff0c;赋值、深拷贝和浅拷贝是前端开发中常见的概念&#xff0c;用于复制简单数据类型&#xff08;字符串、数值、布尔值&#xff09;和引用类型&#xff08;对象、数组&#xff09;。它们的主要区别在…

spring中添加@Test注解测试

1、添加maven依赖 <!-- 添加test方便测试--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><dependency><grou…

C语言进阶版第8课—指针(2)

文章目录 1. 数组名的理解2. 指针访问数组3. 一维数组传参本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 1. 数组名的理解 sizeof&#xff08;数组名&#xff09;— 这里的数组名代表整个数组&#xff0c;计算的也是整个数组的大小&数组名 — 这里的数组名…

HTML 基础,尚优选网站设计开发(二)

最近在恶补HTML相关知识点&#xff0c;本人是后端程序员&#xff0c;看到周围很多人都被裁员了&#xff0c;突然想尽早转变成全栈程序员变成独立开发者&#xff0c;有空余接接私单、商单的 尚优选网站设计开发&#xff0c;HTMLCSSJavaScript实际使用 尚优选网站设计开发页面分析…

KDD 2024 时空数据(Spatio-temporal) Research论文总结

2024 KDD&#xff08; ACM SIGKDD Conference on Knowledge Discovery and Data Mining, 知识发现和数据挖掘会议&#xff09;在2024年8月25日-29日在西班牙巴塞罗那举行。 本文总结了KDD2024有关时空数据(Spatial-temporal) 的相关论文&#xff0c;如有疏漏&#xff0c;欢迎大…

初步了解VTK装配体

VTK还不太了解&#xff0c;根据资料&#xff0c; vtk.vtkAssembly 是 VTK库中的一个重要类&#xff0c;允许通过将多个vtkActor对象组合在一起来创建复杂的3D模型。 import vtk import math from vtk.util.colors import *filenames ["cylinder.stl","sphere…

打破AI壁垒-降低AI入门门槛

AI和AGI AI&#xff08;人工智能-Artificial Intelligence&#xff09;&#xff1a; 先说说AI&#xff0c;这个大家可能都不陌生。AI&#xff0c;就是人工智能&#xff0c;它涵盖了各种技术和领域&#xff0c;目的是让计算机模仿、延伸甚至超越人类智能。想象一下&#xff0c;…

苍穹外卖学习笔记(一)

文章目录 开发环境搭建一. 前端环境搭建二. 后端环境搭建1.进入idea项目2.提交git仓库(推送github远程仓库)3.数据库环境搭建4.前后端联调(在源代码中项目已经实现登录功能)nginx反向代理好处: 三. 完善登录功能(md5加密存储)1.首先打开pojo模块中实体类的employee&#xff0c;…

Linux网络测试和故障排查命令

文章目录 ping 命令常用选项&#xff1a;使用示例&#xff1a;域名解析和 IP 地址响应数据停止 ping 命令统计数据延迟统计 traceroute 命令常用选项&#xff1a;使用示例&#xff1a;命令执行&#xff1a;路由节点详情&#xff1a; mtr 命令使用示例&#xff1a;使用结果详解输…

『功能项目』武器的切换实例【34】

本章项目成果展示 我们打开上一篇33战士的A键连击的项目&#xff0c; 本章要做的事情是按键盘E键切换职业时切换手中的武器 首先在资源商店下载免费的武器模型 创建一个空物体 命名为WeaponPos 将武器预制体拖拽至WeaponPos &#xff08;注意调整空物体位置就可以后续文章会更…

深入解析 Dubbo 的 attachments 机制及其应用场景

背景 在分布式系统中&#xff0c;服务之间的调用&#xff08;RPC调用&#xff09;是非常常见的。而在这种服务调用过程中&#xff0c;常常需要在不同服务之间传递一些上下文信息&#xff0c;比如用户身份信息、请求追踪ID、客户端IP等。Dubbo 提供的 attachments 机制&#xf…