C++的string类

1.string简介

string不是STL的一部分,但是和STL一起学习会更加容易融会贯通。

而实际上string是一个类模板,使用字符的顺序容器实现(也就是字符的顺序表),string整个系列支持char的动态增长(字符编码有几篇文章值得看一下,是耗子哥推荐的)。

String are objects represent sequences of characters.(字符串是表示字符序列的对象。)

string实际上是由basic_traits类实例化而来

    typedef basic_string<char> string;

string类中至少有三个成员变量:

    class string{private:char* _str;//指向由new开辟出来的动态空间,该空间存储字符串size_t _size;//string对象对应字符串的大小size_t _capacity;//string对象的容量,在存储的时候会多存储一个空字符,方便转化为C风格的字符串,在不同的环境扩容机制有些许不同,有的直接2倍,有的1.5倍};

2.类构造函数

2.1.string()

调用构造函数后,会构造一个长度为0的空字符串(但是有一定的容量)。

    #include <iostream>#include <string>int main() {std::string str;//使用范围for循环遍历字符串中的每个字符std::cout << "大小 = " << str.size() << std::endl;std::cout << "容量 = " << str.capacity() << std::endl;//size()是string类内部的成员函数,//可以求得string对象内部存储的字符串大小,//而capacity()则是求得容量大小//后续还会继续讲解return 0;}

2.2.string (const string& str)

构造一个str的副本,也就是复制str构造一个新的string对象。

2.3.string (const string& str, size_t pos, size_t len = npos)

复制string对象的部分的字符(从pos开始到len的部分)来构造一个新的string对象。

或者复制到str结束部分(即:str太短了或者len值为string::pos)。

补充:什么是string::pos呢?string::pos实际上是string内部的常量成员,下面是其定义:

    static const size_t npos = -1;

我们知道size_t是无符号整数类型的别名,因此npos取得了有符号的一个最大值,这基本不可能是str的长度,也可能是字符串的索引值。

len默认值为npos的目的是:如果没有给予len的值,将会一直读取到字符串的末尾,这一点细节我们实现一个简易的string类再来细细探究。

2.4.string (const char* s)

复制s指针指向的字符序列(以空字符\0结尾的C语言风格的字符序列)构造出一个string对象。

2.5.string (const char* s, size_t n)

s指向的字符数组中复制前n个字符。

2.6.string (size_t n, char c)

n个相同得c字符连续填充字符串。

2.7.template <class InputIterator> string (InputIterator first, InputIterator last)

以和原string对象相同的顺序,来复制原string对象中范围为[first,last)(迭代器)的字符序列,构造新的string对象。由于我们还没有学过迭代器,因此我们直接上代码观看迭代器的时候会更加好一下。

    #include <iostream>#include <string>int main(){//直接初始化一个字符串std::string str1("Hello World");std::cout << "str1(\"Hello World\") = "<< str1 << std::endl;//使用迭代器范围构造一个新的字符串std::string str2(str1.begin(), str1.begin() + 7);//输出:Hellostd::cout << "str2(str1.begin(), str1.begin() + 7) = " << str2 << std::endl;  return 0;}

这里begin()就是迭代器的使用,您可以简单理解为有一个指针,指向了这个string对象的字符串开头位置,迭代器的出现就是为了能是使得一些对象可以类似指针一样使用。

2.8.string (initializer_list<char> il)

以列表il中的每个字符来构造string对象,这和使用string(const string& str)构造有些许不同。

    #include <iostream>#include <string>int main(){//使用列表方式初始化字符串std::string str{ "Hello" };//输出:Hellostd::cout << "str: " << str << std::endl;return 0;}

补充:std::initializer_list 是一个模板类,它允许创建一个包含任意数量元素的列表。它类似于数组,可以通过下标访问其中的元素,但与数组不同的是,std::initializer_list 是不可变的,不能进行元素的添加或删除操作。(但是这不是初始化列表的意思!)

2.9.string (string&& str) noexcept

这个函数被称为“移动构造”,可以将源string对象转移到目标string对象中。

移动构造函数通常是浅拷贝,它将源对象的资源所有权转移给目标对象,而不是创建新的资源副本。这种机制能够提高性能,并确保在源对象析构时不会重复释放资源。

下面代码如果没能看懂可以以后再来查看:

    #include <iostream>#include <string>int main(){//使用构造函数构造源string对象std::string source = "Hello World";//使用移动构造函数转移资源std::string target(std::move(source));std::cout << "target = " << target << std::endl;//输出:target = Hello Worldstd::cout << "source = " << source << std::endl;//输出:source = (source的内容已被移动)return 0;}

注意:源对象的状态会变为有效但未指定的状态,并且应该避免继续使用移动后的对象或访问其值,上述代码在大部分编译器(比如VS2022)中有可能在打印source的时候提示出类似下面的警告:使用已移动的 from 对象: source (lifetime.1)

2.10.英文备注

以下是参考CPlusPlus网站的英文解释

(1) empty string constructor (default constructor)

Constructs an empty string, with a length of zero characters.

(2) copy constructor

Constructs a copy of str.

(3) substring constructor

Copies the portion of str that begins at the character position pos and spans len characters (or until the end of str, if either str is too short or if len is string::npos).

(4) from c-string

Copies the null-terminated character sequence (C-string) pointed by s.

(5) from buffer

Copies the first n characters from the array of characters pointed by s.

(6) fill constructor

Fills the string with n consecutive copies of character c.

(7) range constructor

Copies the sequence of characters in the range [first,last), in the same order.

(8) initializer list

Copies each of the characters in il, in the same order.

(9) move constructor

Acquires the contents of str.

str is left in an unspecified but valid state.

3.类析构函数

~string()会释放所有存储容量(该存储容量由字符串所使用的分配器分配)。

也就是说string类的析构函数~string()主要是释放掉string类产生的string对象的资源,因为string的本质是一个顺序表。

4.赋值符号重载函数

类别函数声明
string (1)string& operator= (const string& str);
c-string (2)string& operator= (const char* s);
character (3)string& operator= (char c);
initializer list (4)string& operator= (initializer_list<char> il);
move (5)string& operator= (string&& str) noexcept;

上述的作用基本都是将一个新值赋给已定义后的string对象,来替换其当前string对象的内容。下面是测试例子:

    #include <iostream>#include <string>int main(){//创建多个string对象std::string str1, str2, str3, str4;str1 = "hello";                        //赋值C风格的字符串(2)std::cout << str1 << std::endl;str2 = 'x';                            //赋值单字符(3)std::cout << str2 << std::endl;str3 = str1 + str2;                    //赋值string对象(1)std::cout << str3 << std::endl;str3 = { 'H', 'E', 'L', 'L', 'O' };    //初始化列表(4)std::cout << str3 << std::endl;str4 = std::move(str3);                //移动构造(5)std::cout << str3 << std::endl;std::cout << str4 << std::endl;return 0;}

其中初始化列表函数和普通的赋值只是内部实现细节有点区别,其他地方都很类似。

最后一个函数您若是没有看懂,暂且先用着,我们以后再来解释……

5.迭代器(Iterators)

如果需要遍历一个string对象需要怎么做呢?目前我们有多种方法可以遍历。

类别可读可修改可读不可修改
正向迭代器iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
反向迭代器reverse_iterator rbegin() noexcept;
reverse_iterator rend() noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
类别函数声明
普通对象的常量迭代器const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
常量对象的常量迭代器const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

其中iterator是一个类模板:

    template <class T>//这里假设T实例化为string class iterator { //1.正向迭代器//迭代器的成员函数和操作符重载定义iterator begin() noexcept;//可以找到string对象的头const_iterator begin() const noexcept;//可以找到const string对象的头iterator end() noexcept;//可以找到string对象的尾const_iterator end() const noexcept;//可以找到const string对象的尾//2.反向迭代器reverse_iterator rbegin() noexcept;const_reverse_iterator rbegin() const noexcept;reverse_iterator rend() noexcept;const_reverse_iterator rend() const noexcept;//...};

5.1.正向迭代器:begin()和end()

5.1.1.for遍历

这里会使用string类的成员函数size(),这个函数专门计算string对象的大小,这在后面讲解string类的成员函数时,还会进行讲解。

    #include <iostream>#include <string>int main(){std::string str("abcd");for (int i = 0; i < str.size(); i++){std::cout << str[i] << " ";//[]运算符重载}return 0;}

还需要注意的是,[]运算符重载使得string对象能够像数组一样遍历,我们之后还会再提一次。

5.1.2.迭代器

迭代器的行为十分类似指针(但是不是指针)使得string对象可以像数组一样被使用:

    #include <iostream>#include <string>int main(){std::string str("abcd");std::string::iterator it = str.begin();//这里如果类型名字太长,可以使用auto来自动推导while (it != str.end())//这里不推荐使用'<'符号{std::cout << *it << " ";(*it)++;it++;}std::cout << std::endl << str << std::endl;return 0;}

while (it != str.end())这里不推荐使用<符号,尽管在string对象里使用或许不会出错,但是在其他类里可能就不满足了。(之前说过迭代器的行为类似指针,因此string的底层很有可能就是指针,因此使用<不会出太多错)

但是迭代器不仅仅是string类使用,基本所有库里的类都可以使用迭代器遍历,迭代器有几种便利之处:

  1. 通用,在一些不支持[]重载的结构也可使用迭代器

  2. 是无需考虑差1问题

  3. 屏蔽底层实现细节,可以直接使用

而其他类底层的迭代器是靠纯指针实现的,例如:链表,因此使用<会有风险。

实际上迭代器就是C++封装的一种体现。

5.1.3.范围for

    #include <iostream>#include <string>int main() {std::string str = "Hello";//使用范围for循环遍历字符串中的每个字符for (auto ch : str) {std::cout << ch << " ";}return 0;}

实际上范围for的底层代码就是迭代器,只不过范围for封装得更加厉害罢了(在一些编译器内部调试的时候可以切到范围for的汇编语句来检验两者关系)。

5.2.反向迭代器:rbegin()和rend()

    #include <iostream>#include <string>int main(){std::string str("abcd");std::string::reverse_iterator rit = str.rbegin();while (rit != str.rend()){std::cout << *rit << " ";(*rit)++;rit++;//注意依旧是++,而不是--}std::cout << std::endl << str << std::endl;return 0;}

5.3.常量迭代器

常量迭代器也没有什么声明的,只是将上述两种迭代器从“可读可写”设置为“只可读不可写”,这点我们就不再深入讲解了。

    #include <iostream>#include <string>using namespace std;int main(){//const string str1 = "abcdef";//auto it = str1.begin();//while (it != str1.end())//{//    cout << (*it)++ << " ";////    it++;//}//cout << endl;string str2 = "abcdef";auto cit = str2.cbegin();while (cit != str2.cend()){cout << *cit << " ";cit++;}cout << endl;return 0;}

6.容积(Capacity)

类别函数声明
获取长度和容量size_t size() const noexcept;
size_t length() const noexcept;
size_t capacity() const noexcept;
改变长度和容量void resize (size_t n);
void resize (size_t n, char c);
void reserve (size_t n = 0);
获取环境最大容量数值size_t max_size() const noexcept;
清理对象内容void clear() noexcept;
判空bool empty() const noexcept;

6.1.size()和length()和capacity()

首先我们有一个问题,为什么会有两个计算长度的成员函数呢?这是因为string类得诞生比STL早,最开始设计的是length(),后来出现了STL才开始使用了(并且建议使用)size()。两个都可以计算string对象内部字符串长度,在功能上是等效的。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdefghijklmn");cout << str.size() << endl;cout << str.length() << endl;return 0;}

补充:如果查看其他的结构会发现都是使用size(),而没有length()

而另外一个函数capacity()则可以求得当前string对象的容量。

6.2.clear()

这个成员函数会对string对象的字符串作清理,但是不会释放空间,同时原string的容量不变,对象的siez()值置为空。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdefghijklmn");cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl;str.clear();cout << str << endl;cout << str.size() << endl;cout << str.length() << endl;return 0;}

6.3.max_size()

这个成员函数用于告知用户该环境下的string对象最多可以申请到多少字节的空间。

但是这个值是写死的,只是在不同环境下会不同罢了,因此基本没怎么用到这个。

    #include <iostream>#include <string>using namespace std;int main(){string str1("abcdefghijklmn");cout << str1.max_size() << endl;string str2("abcdefghijklmn");cout << str2.max_size() << endl;return 0;}

6.4.resize()

resize()可以将string对象的大小(即:size()的值),有以下集几种情况:

  1. 如果n小于string对象原有的大小,则删除n个字符以外的字符(这个操作比较危险)

  2. 如果n大于string对象原有的大小,则尾插任意字符(默认表现为空字符\0)扩充到当前的内容,使得大小达到n。因此如果有填充某个字符串的需求,也是可以用这个函数的

  3. 如果指定了字符c,则多开辟的空间初始化为字符c

    #include <iostream>#include <string>using namespace std;int main(){//1.创建一个"abcde"为内容的string对象,size()值为5,capacity()默认值为15string str("abcde");cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//2.调整size()值为20的string对象,而在VS2022中表现为填充15个空字符'\0',而空字符是没有打印出来的str.resize(20);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//3.调整size()值为30的string对象,因此:前5个字符为"abcde",后面有15个'\0',并且还有10个'c'字符,整体打印出"abcdecccccccccc"str.resize(30, 'c');cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//4.缩小size()值,并且可以看到,扩容的空间依旧有效str.resize(5);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;return 0;}

6.5.reserve()

这个函数请求改变容量,要求将string对象调整到大小为n的容量(或者比n更大)这个函数对字符串的长度没有影响,也不能改变它的内容。

如果使用者做了缩小容量的操作,并且影响到原有string对象字符串的存储时,该函数会拒绝请求(即:non-binding无约束力的)。

    #include <iostream>#include <string>using namespace std;int main(){//1.创建一个"abcde"为内容的string对象,size()值为5,capacity()默认值为15string str("abcde");cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//2.调整capacity()值为20的string对象str.reserve(20);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//3.调整capacity()值为30的string对象str.reserve(30);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//4.减小capacity()值为4的string对象(失败请求)str.reserve(4);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;//4.增大capacity()值为10的string对象str.reserve(10);cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;return 0;}> 注意:函数名`reserve`有时候会和单词`reversal`(反转)搞混,这个注意一下就行……

6.6.empty()

该函数返回字符串是否为空(即:size()是否为0),这个函数不会以任何方式修改字符串的值。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdef");cout << str.empty() << endl;str.resize(0);//为空返回truecout << str.empty() << endl;return 0;}

6.7.shrink_to_fit()

shrink to fit这个词的意思就是“缩小以适应”,该函数请求减少容量capacity来适应大小size,当然这只是一种请求(即:non-binding无约束力的)。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcde");cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;str.reserve(1000);//明明大小只有5,容量确高达1000多,浪费资源cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;str.shrink_to_fit();//使用该函数请求缩小容量(非强制)cout << str << endl;cout << str.size() << endl;cout << str.capacity() << endl << endl;return 0;}

7.访问元素(Element access)

7.1.operator[]

    //重载形式char& operator[] (size_t pos);const char& operator[] (size_t pos) const;

pos的起始位置为0,这意味这和数组的使用形式类似。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdef");for (int i = 0; str[i]; i++){cout << str[i] << " ";}return 0;}

如果pos等于string对象的size值,这个符号重载函数就会返回一个指向空字符的引用,该字符位于string对象最后一个字符之后(这个字符不可被修改,只所以可以被允许访问的其中一个原因就是方便写循环终止条件,就像上面的循环例子)。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdef");cout << "空字符->[" << str[str.size()] << "]" << endl;//可以看到空字符没有办法打印出来return 0;}

7.2.at()

    //重载声明char& at (size_t pos);const char& at (size_t pos) const;该函数可以获取字符串中的字符,返回一个`pos`指向字符的引用,该函数自动检查`pos`是否是`string`对象中合法的字符索引,非法则抛出异常。那么`at()`和`[]`有什么区别呢?答案在于自动检查出现非法时,例如下面的代码:#include <iostream>#include <string>using namespace std;int main(){try{string str("abcdef");cout << "str = " << str << endl;cout << "str.size() = " << str.size() << endl;//cout << "str[7] = " << str[7] << endl;//第一情况//cout << "at(7) = " << str.at(7) << endl;//第二情况}catch (const std::out_of_range& e)//出现异常时执行下面代码{cout << "error" << endl;}return 0;}

使用[]的报错和使用at的报错不太相同。

总的来说,抛异常会更加得安全(并且抛异常是面向对象语言的一个显著特征)具体的抛异常细节我们以后再来学习……

7.3.back()

    //重载声明char& back();const char& back() const;

该函数返回最后一个有效字符的引用(不是指空字符),此函数不能再空string对象上调用。

    #include <iostream>#include <string>using namespace std;int main(){string str1("abcdef");cout << "str1 = " << str1 << endl;cout << "str1.size() = " << str1.size() << endl;cout << "str1.back() = " << str1.back() << endl;string str2("");cout << "str2 = " << str2 << endl;cout << "str2.size() = " << str2.size() << endl;//cout << "str2.back() = " << str2.back() << endl;//直接会出现报错return 0;}

7.4.front()

    char& front();const char& front() const;

类似end()的使用,不过获取到字符是开头的字符。

    #include <iostream>#include <string>using namespace std;int main(){string str1("abcdef");cout << "str1 = " << str1 << endl;cout << "str1.size() = " << str1.size() << endl;cout << "str1.front() = " << str1.front() << endl;string str2("");cout << "str2 = " << str2 << endl;cout << "str2.size() = " << str2.size() << endl;//cout << "str2.front() = " << str2.front() << endl;//直接会出现报错return 0;}

8.修改器(Modifiers)

8.1.operator+=()

用得比较多。

    //重载声明string& operator+= (const string& str);string& operator+= (const char* s);string& operator+= (char c);

8.2.append()

尾插,即便有很多重载,但是用的比较少,这里只给出声明,您可以自己玩玩看。

    //重载声明string& append (const string& str);string& append (const string& str, size_t subpos, size_t sublen);string& append (const char* s);string& append (const char* s, size_t n);string& append (size_t n, char c);template <class InputIterator>  string& append (InputIterator first, InputIterator last);string& append (initializer_list<char> il);

8.3.push_back ()

尾插单字符,爷用的比较少,您也可以自己玩玩。

    //函数声明void push_back (char c);

添加字符cstring对象的末尾,并且size()长度+1

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdef");cout << "str = " << str << endl;cout << "str.size() = " << str.size() << endl;str.push_back('x');cout << "str = " << str << endl;cout << "str.size() = " << str.size() << endl;return 0;}

可以拿尾插类的函数来测试环境的扩容机制:

    #include <iostream>#include <string>using namespace std;#define NUMBER 100void test(){string str;size_t old = str.capacity();cout << "Init:" << old << endl;size_t newValue = 0;for (size_t i = 0; i < NUMBER; i++){str.push_back('x');//推送newValue = str.capacity();//记录新值if (newValue != old)//新值和旧值对比{cout << "dilatation = " << newValue << " ";old = newValue;cout << endl;}}cout << endl;}int main(){test();return 0;}

8.4.instert()

    //重载声明string& insert (size_t pos, const string& str);//插入string对象内的字符串string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);//插入子串,注意这里后两个参数不是输入子串的首尾索引,而是输入子串的起始索引和子串长度string& insert (size_t pos, const char* s);//插入C风格的字符串string& insert (size_t pos, const char* s, size_t n);插入C风格的字符串,并且限定长度string& insert (size_t pos, size_t n, char c);//重复插入n个c字符iterator insert (const_iterator p, size_t n, char c);//使用迭代器来,插入重复的字符,并返回指向第一个插入元素的迭代器iterator insert (const_iterator p, char c);//使用迭代定位,插入一个字符,并返回指向第一个插入元素的迭代器template <class InputIterator>iterator insert (iterator p, InputIterator first, InputIterator last);//将迭代器[first, last)的字符插入到目标string对象的迭代器定位处string& insert (const_iterator p, initializer_list<char> il);//在迭代器定位处插入字符序列

可以看到这个函数有很多重载版本,让我们来用一个测试例子研究一下,不过这个函数能不用就最好别用,效率不高。

    #include <iostream>#include <string>using namespace std;int main(){cout << "1.string& insert (size_t pos, const string& str);" << endl;string str1("abcdef");cout << str1 << endl;string sstr1 = "xxxxx";str1.insert(0, sstr1);cout << str1 << endl << endl;cout << "2.string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);" << endl;string str2("abcdef");cout << str2 << endl;string sstr2 = "ABCDEEEEFGHIJK";str2.insert(str2.size(), sstr2, 4, 4);cout << str2 << endl << endl;cout << "3.string& insert (size_t pos, const char* s);" << endl;string str3 = "abcdef";//这是一个C风格的字符串cout << str3 << endl;const char* sstr3 = "__________";str3.insert(str3.size(), sstr3);cout << str3 << endl << endl;;cout << "4.string& insert (size_t pos, const char* s, size_t n);" << endl;string str4("abcdef");cout << str4 << endl;const char* sstr4 = "bbbbbxxxxxxxxxxxxxx";//只想截取长度为前5个字符的字符str4.insert(1, sstr4, 5);cout << str4 << endl << endl;;cout << "5.string& insert (size_t pos, size_t n, char c);" << endl;string str5("ABCDEFG");cout << str5 << endl;str5.insert(1, 20, '+');cout << str5 << endl << endl;cout << "6.iterator insert(const_iterator p, size_t n, char c);" << endl;string str6  = "QWERT";str6.insert(str6.begin() + 3, 10, 'c');cout << str6 << endl << endl;cout << "7.iterator insert (const_iterator p, char c);" << endl;string str7 = "AAAAAAAAA";str7.insert(str7.end(), 'c');cout << str7 << endl << endl;cout << "8.template <class InputIterator> " << endl;cout << "  iterator insert(iterator p, InputIterator first, InputIterator last);" << endl;string str8 = "XXXX";string sstr8 = "|bcde|";str8.insert(str8.begin(), sstr8.begin() + 1, sstr8.end() - 1);cout << str8 << endl << endl;cout << "9. string& insert (const_iterator p, initializer_list<char> il);" << endl;string str9 = "XXXX";str9.insert(str9.begin() + 2, { 'a', 'b', 'c' });cout << str9 << endl << endl;return 0;}

8.5.erase()

    string& erase (size_t pos = 0, size_t len = npos);//全部删除(默认)iterator erase (const_iterator p);//使用迭代器删除指定位置iterator erase (const_iterator first, const_iterator last);//

这个函数主要是用来擦除字符串的一部分,并减小size()的值,直接使用则默认为全部删除,这个函数能不用就最好别用,也是效率不太高。

    string str = "abcdef";cout << str << endl;str.erase(str.begin() + 3);cout << str << endl;str.erase(str.begin(), str.end() - 2);cout << str << endl;str.erase();cout << str << endl;

8.6.replace()

    //string (1)    string& replace (size_t pos,        size_t len,        const string& str);string& replace (const_iterator i1, const_iterator i2, const string& str);//substring (2)    string& replace (size_t pos, size_t len,        const string& str, size_t subpos, size_t sublen);//c-string (3)    string& replace (size_t pos, size_t len, const char* s);string& replace (const_iterator i1, const_iterator i2, const char* s);//buffer (4)    string& replace (size_t pos, size_t len,        const char* s, size_t n);string& replace (const_iterator i1, const_iterator i2, const char* s, size_t n);//fill (5)    string& replace (size_t pos, size_t len,        size_t n, char c);string& replace (const_iterator i1, const_iterator i2, size_t n, char c);//range (6)    template <class InputIterator>string& replace (const_iterator i1, const_iterator i2, InputIterator first, InputIterator last);//initializer list (7)    string& replace (const_iterator i1, const_iterator i2, initializer_list<char> il);

该函数主要用于替换,参数也很多,您可以稍微玩一下:

    string str = "abc def";cout << str << endl;str.replace(3, 2, "&%#");//将str索引为3起始的长度为2的子串替换成"&%#"cout << str << endl;

也不太建议使用,效率比较低下,需要的时候可以使用一下。当然我们也可以自己设计一个算法解决:

    int main(){//设计一个算法让下面string对象的空格改为20%string str = "abc def";string strx;cout << str << endl;//空间换时间for (auto ch : str){if (ch != ' '){strx += ch;}else{strx += "20%";}}//str = strx;//或者使用全局的swap(),但是使用这个会效率很低,内部有一次拷贝构造两次赋值,很糟糕str.swap(strx);//string的swap()效率就会高很多,这个成员函数只是交换了指针指向,效率很高cout << str << endl;//这样的效率都会比较高//不过需要注意的是,就算我们直接使用全局的swap()也不会真的调用到最麻烦的哪一个,因为库已经针对全局的swap()进行了重载,内部调用的也是成员的swap()return 0;}

8.7.swap()

    void swap (string& str);

成员交换函数,效率比全局的要高,就是前面代码使用的那一个函数。后面介绍的全局swap()调用的就是这个成员函数。

9.字符串运算(String operations)

9.1.c_str()和data()

    //函数声明const char* c_str() const noexcept;const char* data() const noexcept;

有时候为了和C兼容,需要使用C风格的字符串(以\0)。而c_str()可以使得string对象返回底层的C风格的字符串成员变量,从这里我们也可以看到,虽然string不以\0作为结尾标志,但是依旧会在构造string对象的时候存储\0,以保证能与C兼容。

    #include <iostream>#include <string>using namespace std;int main(){string str("abcdef");cout << "str" << " = " << str << " 类型:" << typeid(str).name() << endl << endl;char* ch;cout << typeid(ch).name() << endl;cout << "str.c_str()" << " = " << str.c_str() << " 类型:" << typeid(str.c_str()).name() << endl;cout << "str.data()" << " = " << str.data() << " 类型:" << typeid(str.data()).name() << endl;return 0;}

data()s_str()的使用是等价的,没有太大区别。

    #include <iostream>#include <string>#include <cstring>using namespace std;int main(){int length;string str = "Test string";const char* cstr = "Test string";if (str.length() == strlen(cstr)){cout << "1" << endl;if (memcmp(cstr, str.data(), str.length()) == 0){cout << "1" << endl;}}return 0;}

9.2.cpoy()

    //函数声明size_t copy (char* s, size_t len, size_t pos = 0) const;将`string`对象当前值,从`pos`位置开始的长度为`len`子串拷贝到`s`指向的数组中。#include <iostream>#include <string>int main(){std::string str("abcd");char s[3] = { 0 };//如果这里没有加入初始化{ 0 },那么后续打印s数组就会出现乱码,原因就是找不到空字符str.copy(s, 2);std::cout << s << std::endl;return 0;}

9.3.find()和rfind()

    size_t find (const string& str, size_t pos = 0) const noexcept;size_t find (const char* s, size_t pos = 0) const;size_t find (const char* s, size_t pos, size_type n) const;size_t find (char c, size_t pos = 0) const noexcept;

该函数可以寻找字符串或者string对象中的内容,。

    #include <iostream>#include <string>using namespace std;int main(){//1.寻找string子串string str1("abcd-abcd-abcd");string str2("abcd");cout << str1.find(str2, 0) << endl;cout << str1.find(str2, 1) << endl;cout << str1.find(str2, 6) << endl << endl;//2.寻找C风格子串string str3("ABCD-ABCD-ABCD");const char* str4 = "ABCD";cout << str3.find(str4, 0) << endl;cout << str3.find(str4, 1) << endl;cout << str3.find(str4, 6) << endl << endl;//3.在范围内寻找C风格子串string str5("ABCD-AB-ABCD-AB-ABCD");const char* str6 = "ABCD";cout << str5.find(str6, 0, 2) << endl;cout << str5.find(str6, 1, 2) << endl;cout << str5.find(str6, 6, 2) << endl;cout << str5.find(str6, 9, 2) << endl;cout << str5.find(str6, 14, 2) << endl << endl;//4.寻找字符string str7("xxxxxxx_xxxx_xxxxxxxxxx_");char ch = '_';int j = -1;for (int i = 0; i < str7.size(); i++){int z = str7.find(ch, i);if (j != z){cout << z << " ";j = z;}}return 0;}

rfind()就是逆向查找,具体定义看下文档就行,使用和find()类似。

    //重载声明size_t rfind (const string& str, size_t pos = npos) const noexcept; size_t rfind (const char* s, size_t pos = npos) const; size_t rfind (const char* s, size_t pos, size_t n) const;size_t rfind (char c, size_t pos = npos) const noexcept;

9.4.substr()

    //函数声明string substr (size_t pos = 0, size_t len = npos) const;

这个函数可以用来生成一个新的子串对象(从源string对象的pos开始的长度为len的子字符串)。

    #include <iostream>#include <string>using namespace std;int main(){string str("awwwwf");cout << str.substr(1, 4) << endl;return 0;}

9.5.compare()

    //重载函数int compare (const string& str) const noexcept;//比较两个string对象int compare (size_t pos, size_t len, const string& str) const;//在源string对象的子串和str对象比较int compare (size_t pos, size_t len, const string& str, size_t subpos, size_t sublen) const;//在源string对象的子串内和str对象的子串比较int compare (const char* s) const;//和C风格的字符串比较int compare (size_t pos, size_t len, const char* s) const;//string对象的子串和C风格的字符串比较int compare (size_t pos, size_t len, const char* s, size_t n) const;//string对象的子串和C风格的字符串的子串比较

该函数主要是比较字符大小,和C语言的strcmp()类似返回指定的字符序列。

    #include <iostream>#include <string>using namespace std;// comparing apples with apples#include <iostream>#include <string>int main(){string str1("green apple");string str2("red apple");//g < r <=> g - r < 0//"green apple" < "red apple"if (str1.compare(str2) < 0)cout << str1 << " is not " << str2 << endl;//"apple" == "apple"if (str1.compare(6, 5, "apple") == 0)cout << "still, " << str1 << " is an apple" << endl;//"apple" == "apple"if (str2.compare(str2.size() - 5, 5, "apple") == 0)cout << "and " << str2 << " is also an apple" << endl;//"apple" == "apple" if (str1.compare(6, 5, str2, 4, 5) == 0)cout << "therefore, both are apples" << endl;const char* str3 = "green apple";if (str1.compare(str3) == 0)cout << "one and the same" << endl;return 0;}

不过这个函数不怎么使用,原因是后面有很多的判断符号重载,这些重载更加方便。

9.6.find_first_of()

    //重载函数size_t find_first_of (const string& str, size_t pos = 0) const noexcept;size_t find_first_of (const char* s, size_t pos = 0) const;size_t find_first_of (const char* s, size_t pos, size_t n) const;size_t find_first_of (char c, size_t pos = 0) const noexcept;

在字符串中查找第一次出现指定字符集合中任何一个字符的位置。

    int main(){string str1 = "xxx_big_apple_xxx";string str2 = "be";cout << str1.find_first_of(str2, 0) << endl;//输出4cout << str1.find_first_of(str2, 5) << endl;//输出12return 0;}

吐槽:这个函数的名字真不好理解……

9.7.find_last_of()

    //重载函数size_t find_last_of (const string& str, size_t pos = npos) const noexcept;size_t find_last_of (const char* s, size_t pos = npos) const;size_t find_last_of (const char* s, size_t pos, size_t n) const;size_t find_last_of (char c, size_t pos = npos) const noexcept;

在字符串中查找最后一次出现指定字符集合中任何一个字符的位置。

    int main(){string str1 = "xxx_big_apple_xxx";string str2 = "be";cout << str1.find_last_of(str2, str1.size() - 1) << endl;//输出12cout << str1.find_last_of(str2, 11) << endl;//输出4return 0;}

9.8.find_first_not_of()

    //重载函数size_t find_first_not_of (const string& str, size_t pos = 0) const noexcept;size_t find_first_not_of (const char* s, size_t pos = 0) const;size_t find_first_not_of (const char* s, size_t pos, size_t n) const;size_t find_first_not_of (char c, size_t pos = 0) const noexcept;这个函数可以查找不是指定字符集合中的任何一个字符的位置。下面是一个例子(制作屏蔽词语):
```cppint main(){string str("saldgcaslucdgleakjslaejcsfkajsasducgiasfg");size_t index = str.find_first_not_of("abcdef", 0);cout << str << endl;while (index != string::npos){str[index] = '*';index = str.find_first_not_of("abcdef", index + 1);}cout << str << endl;return 0;}

9.9.find_last_not_of()

    //重载函数size_t find_last_not_of (const string& str, size_t pos = npos) const noexcept;size_t find_last_not_of (const char* s, size_t pos = npos) const;size_t find_last_not_of (const char* s, size_t pos, size_t n) const;size_t find_last_not_of (char c, size_t pos = npos) const noexcept;

上面这个函数就是从后面开始查找,这里就不再举例了……

9.10.get_allocator()

    //函数声明allocator_type get_allocator() const noexcept;

这个函数就比较复杂了,您可以先去查看一下文档,或者以后我们有机会再来详谈……

9.11.assign()

    //重载函数string& assign (const string& str);string& assign (const string& str, size_t subpos, size_t sublen);string& assign (const char* s);string& assign (const char* s, size_t n);string& assign (size_t n, char c);template <class InputIterator>string& assign (InputIterator first, InputIterator last);string& assign (initializer_list<char> il);string& assign (string&& str) noexcept;这个函数也不经常使用,实际在一种赋值的工作:
```cppint main(){string str1 = "abcdef";string str2 = "xxxxxx";str1.assign(str2);cout << str1 << endl;cout << str2 << endl;return 0;}

10.常量成员(Member constants)

string类的常量成员,也就是前面介绍的npos,之前已经简单提到过,这里就放一下npos的定义使得本文章足够完整:

    //常量定义static const size_t npos = -1;

11.非成员函数重载(Non-member function overloads)

11.1.operator+()

    string operator+ (const string& lhs, const string& rhs);string operator+ (const string& lhs, const char* rhs);string operator+ (const char* lhs, const string& rhs);string operator+ (const string& lhs, char          rhs);string operator+ (char lhs, const string& rhs);

+的代价要比+=大,因此非必要不要轻易使用+,这里就不演示+的使用了,您玩一下就行。

11.2.relational operators

一些比较运算符重载:

    //(1)bool operator== (const string& lhs, const string& rhs);bool operator== (const char*   lhs, const string& rhs);bool operator== (const string& lhs, const char*   rhs);//(2)    bool operator!= (const string& lhs, const string& rhs);bool operator!= (const char*   lhs, const string& rhs);bool operator!= (const string& lhs, const char*   rhs);//(3)    bool operator<  (const string& lhs, const string& rhs);bool operator<  (const char*   lhs, const string& rhs);bool operator<  (const string& lhs, const char*   rhs);//(4)    bool operator<= (const string& lhs, const string& rhs);bool operator<= (const char*   lhs, const string& rhs);bool operator<= (const string& lhs, const char*   rhs);//(5)    bool operator>  (const string& lhs, const string& rhs);bool operator>  (const char*   lhs, const string& rhs);bool operator>  (const string& lhs, const char*   rhs);//(6)    bool operator>= (const string& lhs, const string& rhs);bool operator>= (const char*   lhs, const string& rhs);bool operator>= (const string& lhs, const char*   rhs);

内部使用string::compare()函数来进行比较。

11.3.swap()

    //重载声明void swap (string& str);

全局交换函数,内部调用了成员函数swap(),这个重载是为了防止调用到低效的全局交换函数。

11.4.operator>>()和operator<<()

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

这个就是辅助string进行流插入和流输出的重载,也是看下就行,IO类的函数我们以后还会深入讲解。

11.5.getline()

    istream& getline (istream& is, string& str, char delim);istream& getline (istream& is, string& str);

这个函数使用的频率也比较高,主要是从流is中获取一行输入string对象,直到遇到文件结束符EOF分隔符,由于返回的是流is的引用,因此可以重复读取。

    //方式1string str1;cin >> str1;cout << "str1:" << str1 << endl;//方式2string str2;getline(cin, str2);cout << "str2:" << str2 << endl;//方式3string str3;getline(cin, str3, '$');//遇到$就停止读取,抛弃掉$,不再进入后续的输入cout << "str3:" << str3 << endl;

方式1不可以输入带有空格的 ,方式2可以,方式3可以指定结束符号。

12.string相关题目练习

12.1.字符串中的第一个唯一字符

    class Solution{public:int firstUniqChar(string s){//1.开始计数,生成计数表int arr[26] = { 0 };//先定义一个数组存储每个字符的出现个数for (auto ch : s){arr[ch - 'a']++;//开始计数}//2.根据string对象和计数表寻找索引int i = 0;for (i = 0; i < s.size(); i++){if (arr[s[i] - 'a'] == 1){return i;}}return -1;}};

12.2.将网址分割为:协议、域名、资源名

    int mian(){//1.输入网址urlstring url;//https->://baidu.com->/limou/file/textcin >> url;//2.寻找并且拆解urlstring protocol;//协议string domain;//域名string resource;//资源//2.1.寻找协议int index1 = url.find(":");//5if (index1 == string::npos){cout << "没有协议,退出程序" << endl;exit(-1);}//2.2.寻找域名int index2 = url.find("/", index1 + 3);//17if (index2 == string::npos){index2 = url.size();}//2.3.赋值三个对象protocol = url.substr(0, index1);domain = url.substr(index1 + 3, index2 - (index1 + 3));//17-5-3 = 12-3 = 9if (index2 != url.size()){resource = url.substr(index2 + 1);}//3.输出结果cout << "protocol = " << protocol << endl;if (domain.size() != 0){cout << "domain = " << domain << endl;}else{cout << "没有域名" << endl;}if(resource.size() != 0){ cout << "resource = " << resource << endl;}else{cout << "没有资源" << endl;}return 0;}

12.3.仅仅反转字母

    class Solution {public:bool IsLetter(const char& ch){if ((ch >= 'a' && ch <= 'z')|| (ch >= 'A' && ch <= 'Z')){return true;}return false;}string reverseOnlyLetters(string str){auto begin = 0;auto end = str.size() - 1;while (begin < end){while (!IsLetter(str[begin]) && begin < end){begin++;}while (!IsLetter(str[end]) && begin < end){end--;}swap(str[begin], str[end]);begin++;end--;}return str;}};

12.4.字符串最后一个单词的长度

    #include <iostream>using namespace std;int main(){//1.输入字符句子string str;getline(cin, str);//hello nowcode//2.逆向寻找size_t index = str.rfind(" ");//得到5cout << str.size() - index - 1 << endl;//13-5 = 8  }

但是这么做有些不严谨,再严谨点就是下面代码:

    #include <iostream>using namespace std;int main(){//1.输入字符句子string str;getline(cin, str);//hello nowcode//2.逆向寻找size_t index = str.rfind(" ");//得到5if (index != string::npos){cout << str.size() - index - 1 << endl;//13-5 = 8   }else{cout << str.size();}}

12.5.验证回文串

    class Solution{public:bool IsLetterAndNumber(const char& ch)//判断是否为字符或者数字{return((ch >= 'a' && ch <= 'z')|| (ch >= 'A' && ch <= 'Z')|| (ch >= '0' && ch <= '9'));}bool IsOk(char x, char y)//判断是否构成回文的特征{if (x == y){return true;}if ((x >= 'a' && x <= 'z') && (y >= 'A' && y <= 'Z')){if (x - 'a' + 'A' == y){return true;}}else if ((y >= 'a' && x <= 'z') && (x >= 'A' && x <= 'Z')){if (y - 'a' + 'A' == x){return true;}}return false;}bool isPalindrome(string str){//1.取得首尾索引//str = "A man, a plan, a canal: Panama"int begin = 0;//s[begin] = 'A' int end = str.size() - 1;//str[end] = 'a'//2.循环判断回文while (begin < end){while (begin < end && !IsLetterAndNumber(str[begin])){begin++;}while (begin < end && !IsLetterAndNumber(str[end])){end--;}if (!IsOk(str[begin], str[end]))//如果判断出是相同的就返回true{return false;}begin++;end--;}return true;}};

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

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

相关文章

【iOS安全】开启任意app的WebView远程调试

参考&#xff1a;https://mp.weixin.qq.com/s/bNKxQaVrPaXsZ5BPbsXy7w &#xff08;来自周智老师的公众号&#xff09; 概述 Safari 有一个内置的前端调试器&#xff0c; 在iPhone通过局域网或者USB连接MacBook 并启用Safari 远程调试之后&#xff0c;前端调试器默认情况下对…

mysql之limit语句详解

一、介绍 LIMIT是MySQL内置函数&#xff0c;其作用是用于限制查询结果的条数。 二、使用 1. 语法格式 LIMIT [位置偏移量,] 行数 其中&#xff0c;中括号里面的参数是可选参数&#xff0c;位置偏移量是指MySQL查询分析器要从哪一行开始显示&#xff0c;索引值从0开始&#xff…

【剑指Offer 15】二进制中1的个数,Java解密。

LeetCode 剑指Offer 75道练习题 文章目录 剑指Offer:二进制中1的个数示例:限制:解题思路:剑指Offer:二进制中1的个数 【题目描述】 编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为 汉明重量))。 提示…

构建Docker容器监控系统 (1)(Cadvisor +InfluxDB+Grafana)

目录 Cadvisor InfluxDBGrafana 1. Cadvisor 2.InfluxDB 3.Grafana 开始部署&#xff1a; 下载组件镜像 创建自定义网络 创建influxdb容器 创建数据库和数据库用户 创建Cadvisor 容器 准备测试镜像 创建granafa容器 访问granfana 添加数据源 Add data source 新建 …

java.sql.SQLFeatureNotSupportedException 问题及可能的解决方法

目录 问题 分析&#xff1a; 解决方法 问题 java.sql.SQLFeatureNotSupportedException 分析&#xff1a; 可能是你的 druid的maven依赖版本太低了&#xff0c;我的以前是1.1.16&#xff0c;就出现了异常&#xff01; 解决方法 把druid的maven依赖版本调高&#xff01; 运…

unity海康威视原生SDK拉取网络摄像头画面,并展示在一个Material上

原理是使用sdk获取视频流&#xff0c;格式为YUV&#xff0c;然后分离YUV通道到三张不同的Texture2D上&#xff0c;通过shader将三个通道重新输出为原始图像。 我将所用的各个部分已经整理成一个压缩包&#xff0c;免积分下载 压缩包结构如下 使用步骤 1 DLL:放在Plugins文件…

湘大oj1138爱你一生一世题解:最大公约数 逆向思维 int整除会向下取整

一、链接 爱你一生一世 二、题目 题目描述 在2013年1月4日&#xff0c;这个“爱你一生一世”的特别日子&#xff0c;男生都想向自己的喜欢的女生表达爱意。 你准备在该死的C语言考试后&#xff0c;去向她&#xff08;或者他&#xff1f;&#xff09;告白。告白怎么能缺了礼…

渗透攻击方法:原型链污染

目录 一、什么是原型链 1、原型对象 2、prototype属性 3、原型链 1、显示原型 2、隐式原型 3、原型链 4、constructor属性 二、原型链污染重现 实例 Nodejs沙箱逃逸 1、什么是沙箱&#xff08;sandbox&#xff09; 2、vm模块 一、什么是原型链 1、原型对象 JavaS…

Springboot格式化给前端返回的时间格式

Springboot格式化给前端返回的时间格式 新增注解新增注解执行逻辑建立注解及注解执行逻辑的关联将时间转换注解注入Spring容器在返回对象的属性中加入注解结束 新增注解 Documented Target({FIELD,METHOD}) Retention(RUNTIME) public interface DateFormatTransform {String …

不只是Axure,这5 个也能轻松画原型图!

在设计和开发过程中&#xff0c;原型图是一个至关重要的工具。它是将设计理念转化为可视化、交互式的形式&#xff0c;使团队成员和利益相关者更好地理解和评估产品的功能和用户体验。选择适合的软件工具对于画原型图至关重要&#xff0c;本文将介绍 5 种常用的画原型图软件&am…

spring 面试题

一、Spring面试题 专题部分 1.1、什么是spring? Spring是一个轻量级Java开发框架&#xff0c;最早有Rod Johnson创建&#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack&#xff08;一站式&#xff09;轻量…

【LeetCode 75】第二十三题(2352)相等行列对

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码运行结果&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目很简洁&#xff0c;就是要我们寻找行与列相同的对数。相同行与列不仅是要元素相同&#xff0c;还需要顺序也一样&#xff08…

tomcat虚拟主机配置演示

一.新建用于显示的index.jsp文件&#xff0c;写入内容 二.修改tomcat/apache-tomcat-8.5.70/conf/server.xml配置文件 匹配到Host那部分&#xff0c;按上面格式在后面添加自己的域名和文件目录信息 主要是修改name和docBase 保存退出重启tomcat&#xff0c;确保tomcat运行…

IDEA 修改jar包版本后项目中出现多个版本

问题描述&#xff1a; 使用IDEA&#xff0c;在pom文件中修改了某个依赖jar包的版本后&#xff0c;重新编译发现旧的版本没有删除&#xff0c;项目中同一个jar可以看到有两个版本&#xff0c;导致编译时随机选择了其中一个版本&#xff0c;导致编译失败。 已知问题&#xff1a;…

接口安全防护方案

文章目录 1.认证与授权机制2.参数校验3.接口加密4.防止暴力破解5.安全头设置6.日志监控 1.认证与授权机制 使用令牌&#xff08;Token&#xff09;、OAuth等认证方式&#xff0c;确保只有合法用户可以访问接口。授权机制可以防止未经授权的用户访问敏感接口。 示例&#xff1a;…

如何使用Pycharm 快速搭建 Django 项目 (分享详细图文教程)

1. 准备工作 在开始创建Django项目之前&#xff0c;需要先确保已经安装了Python和Pycharm。并且python中已经安装好了Django依赖。 1安装python&#xff08;这里我安装使用的是python3.11.4稳定版本&#xff09; 官网下载太慢了这里直接贴网盘下载连接了&#xff0c;一起贴出py…

常见监控网络链路和网络设备的方法

网络监控主要包括网络链路监控和网络设备监控&#xff0c;通常系统运维人员会比较关注。 一、网络链路监控 网络链路监控主要包含三个部分&#xff0c;网络连通性、网络质量、网络流量。 连通性和质量的监控手段非常简单&#xff0c;就是在链路一侧部署探针&#xff0c;去探…

Leetcode-每日一题【剑指 Offer 14- II. 剪绳子 II】

题目 2、3、3的三段&#xff0c;此时得到的最大乘积是18。 答案需要取模 1e97&#xff08;1000000007&#xff09;&#xff0c;如计算初始结果为&#xff1a;1000000008&#xff0c;请返回 1。 示例 1&#xff1a; 输入: 2输出: 1解释: 2 1 1, 1 1 1 示例 2: 输入: 10输出…

nginx负载均衡(反向代理)

nginx负载均衡 负载均衡&#xff1a;由反向代理来实现。 nginx的七层代理和四层代理&#xff1a; 七层是最常用的反向代理方式&#xff0c;只能配置在nginx配置文件的http模块当中&#xff0c;而且配置方法名称&#xff1a;upstream模块&#xff0c;不能写在server模块中&#…

leetcode26-删除有序数组中的重复项

双指针—快慢指针 慢指针 slow 走在后面&#xff0c;快指针 fast 走在前面探路&#xff0c;找到一个不重复的元素的时候就让slow前进一步并赋值给它。 流程&#xff1a; 代码 class Solution { public:int removeDuplicates(vector<int>& nums) {int slow 0, fas…