C++STL初阶(1):string的使用及初阶原理

news2024/11/17 1:47:07

此文作为学习stl的笔记,许多普及、概念性的知识点将不再罗列(如stl的发展、背景等)

便于读者作为复习等方法了解。

0.STL简介(笔记向)

      STL不是祖师爷本贾尼实现的,是在惠普实验室中实现的。其作为一个数据结构与算法的库(主要是数据结构),现在有很多商业公司开发的目的,有的开源有的收费,目前使用最多的是(也是我们使用的)继承自HP版本的SGI版本。

学习STL有三重境界:能用、明理、能扩展(如自己扩展B树等)

此篇我们介绍string的运用。


目录

0.STL简介(笔记向)

1.string

2.string的构造函数(constructor,重点是"abcde"会被当作const char*)

3.string的运算符重载[ ](两种重载,带const和不带const)

4.string的遍历 

5.string在VS编译器下的大小(了解)

6.将string按照字典序排序

7.string的插入和修改

7.1 push_back 和 append

7.2更常用的添加方法:运算符重载 +=

7.3 assign函数

7.4 重点: insert 与 erase

7.5 replace替换函数 (注意两个迭代器传参时的左闭右开)

8.小试牛刀

9.capacity大类

​编辑 9.1 max_size(了解)

9.2clear

9.3扩容

9.4 resize和reserve  ★★

9.5 shrink_to_fit

9.6 at

9.7 c_str

10.find

11. string相关的部分外部函数

12.编码表UNICODE


1.string

string,也就是常说的数据结构“串”。string出现的比stl早,所以有一些功能较冗杂

C 语言中,字符串是以 '\0' 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str 系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP(面向对象编程) 的思想,而且底层空间需要用户自己管理,稍不留神可 能还会越界访问。
比如 strcpy和strcat,你需要自己管理空间大小、是否有/0、操作的方法。。。。
非常麻烦。
许多都需要使用string存储,如身份证号码(数据很大,用int存储很不划算,并且还有可能带X)

string也叫basic_string,并且被默认为管理char类型数据的容器。 

                                    

还可以管理wchar:

                                       

                                                     wchar是一种 双字节 字符

在大多数情况下,我们可以认为string就是一个char类型的数组、顺序表。 

 string - C++ Reference (cplusplus.com)

string一共一百二十个左右的成员函数,其实很多函数是冗余的。


2.string的构造函数(constructor,重点是"abcde"会被当作const char*)

我们要学会通过文献来使用函数

掌握默认构造、拷贝构造、带参构造、其他的会查网站看明白即可。

第一个是默认构造,第二个是我们最常用的常量区赋值:

string s="abcdefg";

如果我们定义一个空字符串 string str1;  那么str1中是包含了\0的

 默认构造出的s2中间不是乱码,而是什么都没有。

s可以直接被cout输出,也可以通过cin直接获得内容。甚至可以使用中文。

第三种初始化substring是按照使用者主观定制长度拷贝,第一个参数是数组名(字符串名字),第二个是开始拷贝的位置(包括该位置,数据结构喜欢左闭右开),第三个是拷贝的长度。长度的默认参数是npos

成员变量npos:

                                       

注意:npos是无符号整形size_t,所以npos其实是四十二亿左右。当然,一个string没有那么长。

             (其中str is too short的意思是相对于我传的参数len,str可以被拷贝的内容太短了)

                                      

                          (不传参或者写npos就相当于直接拷贝到末尾,都属于str is too short)

同样的,学会通过英文来了解含义

  


                            


常量字符串被认为是const char*,调用的是第五个构造:

这种直接常量字符串接数字表示前三个字符,如果是一个string接数字表示从第数字个位置开始,省略了一个要打印个数的参数。

 


如下,单参数构造函数的隐式类型转换:const char* 转化成string,中间会生成一个临时变量string(构造),再加上一个拷贝构造(有时候直接优化成一次构造)

                              

因此对于引用:

                             

string s4 = "abcdw";//涉及隐式类型转换,"abcdw"被当作char*

3.string的运算符重载[ ](两种重载,带const和不带const)

size和length在此处是相同的作用,length是一开始被设计的,size是在之后stl设计时从整体角度考虑的,便于和其他stl统一。

size和length都不会计算'\0'

                  


使用较少的调用方法:

                                            

这样,我们的自定义类型就能像数组一样使用。


作为自定义、尤其是一些需要深拷贝的类型,我们可以传引用返回来减少拷贝次数以提升效率,那么此处的运算符重载[ ]为什么也要使用传引用返回?拷贝几个字节很费劲吗?

我们先只看第一排的重载

!!! 传引用返回的第二大作用得以体现:可修改容器中的返回值:

                          

string底层开的空间在堆上。

并且由于string的底层的越界检查很多都是使用assert,所以错误都能检查出来。

那么。面对不希望被修改的字符串,如下图的被const修饰的常量字符串(string底层开的空间在堆上

我们再观察之前的声明,[ ]有两种运算符重载,相互构成函数重载:第一种的参数不含const,也就是刚刚提到的展示引用返回作用的重载;还有一种是参数带const的,那么我们对返回值也进行const的修饰,使常量字符串不得被修改。

                              

再加上匹配机制(优先匹配最合适的),为了让不能被修改的就一直不被修改,可以修改的就可以修改,所以我们需要实现两个重载。(只使用带const的重载也可以调用非常量字符串,但是这样的话非常量字符串也不能修改, 不太方便。但是大部分函数用const修饰是只有好处没有坏处的,这样的观念我们在前文提到过)

所以,到底提供几个const,需要在实战中按照实际需要来确定。


4.string的遍历 

根据刚才的size功能和运算符重载,我们可以通过循环实现数组的遍历。

for (size_t i = 0; i < s4.size(); i++) {
	cout << s4[i] << " ";
}

C++还有一种方法遍历:迭代器 iterator。

首先,iterator是定义在类域中的,必须在string域中使用。

string::iterator it1 = s4.begin();
while (it1 != s4.end()) {
	cout << *it1 << " ";
	it1++;
}

那什么又是begin和end呢?

最重要的概念:所有容器的begin和end都满足左闭右开

begin()返回首字符“指针”,end()返回最后一个有效字符的下一个的“地址”

迭代器就可以看做是类似指针的东西。但是其本质不一定是指针。

我们可以利用typeid来看一下其类型,非常奇怪。

                    

                       (typeid还可以将被typedef的类型的原名称给显式出来)

                          

it1被当作指针一样,想访问其对应的内容就直接解引用。

就对于string而言,  直接使用[ ]下标访问 更方便,但是对于大部分数据结构,iterator更加主流。

上文中的begin和end的用法也都符合所有的容器。


利用范围for遍历(C++11才支持):

                                    

e是赋值拷贝,对e++或者--等操作不会实际影响s4中的字符大小。

     但是范围for的自动循环在编译时其实和迭代器的底层是几乎一样的,所以对于容器的学习主要掌握迭代器版本。


 5.begin等函数的重载

                                      

const string s1("abcdefg");

由匹配规则我们知道,当s1被const修饰时,如果再调用begin,会调用下面那个重载。

      (定义s1使用的const对应的是const_iterator begin() const中后面的那个const,有const修饰和没const修饰的被认为是两种参数类型) 

然后返回一个const类型的iterator,这样返回的iterator就是只读的。

细心的读者可能已经发现了,为什么不是const iterator,而是const_iterator?

const限定的是迭代器所指向的内容,迭代器指向的内容不能修改不代表迭代器本身不能修改。

如果迭代器本身都被限制,得到的迭代器甚至不能++,无法遍历。

               

此处的差距就可以类比于指针的const的不同的修饰:

string :: const_iterator 

                                        

const string :: iterator(这种我们一般不使用),更多使用string :: iterator和string :: const_iterator


还可以通过auto来减少输入量。

          

除了iterator和const_iterator,还有反向迭代器(用得不多):

                          

         

此时对反向迭代器++就是向左走,--就是向右走。他同样有两种:带const的和不带const的。带const的同样不能通过迭代器修改迭代器所指向的参数。

                                    

因为可以用方括号遍历的缘故,string其实很少这样遍历。直接循环即可。

string::reverse_iterator re_it2= s2.rbegin();
while (re_it2 != s2.rend()) {
	cout << *re_it2 << " ";
	re_it2++;
}

一共四种迭代器,在之后的容器中都是同样的作用。


5.string在VS编译器下的大小(了解)

    

第一点,不同的编译器的计算出的大小是不一样的,此处只针对vs2019进行学习:

string是一个字符串数组,按理来说其由 _str  _size  _capacity三部分组成,在x86也就是32位环境下,应该是一个指针 两个int,一共是12。但是其实string中还有一个大小为16的buffer数组,当字符串数量小于16时,直接存在string内,不存在堆中,因此大小为28  


6.将string按照字典序排序

   首先明确,字典序就是ASCII的顺序。

    其次,此处我们需要引入一个新的头文件<algorithm>,并利用其中的算法:sort

sort是一个函数模板,他不是属于容器string的类函数。sort函数的参数是各种类型的迭代器。

原文介绍如下:

Sort elements in range

Sorts the elements in the range [first,last) into ascending order.
The elements are compared using operator< for the first version, and comp for the second.
Equivalent elements are not guaranteed to keep their original relative order  stable_sort

可以直接排序vector等,不过链表暂时不能直接排序。

底层是快速排序,不具有稳定性。

      

传参时,任然要遵守左闭右开:

如果要全部排列,直接使用迭代器的begin和end

如果只排前n个,则直接让begin+n即可,因为类似于数组,begin返回的是0位置。

                                 

                                     (由于0的存才si,begin()+5其实指的是第六个字符)


7.string的插入和修改

7.1 push_back 和 append

首先介绍老朋友:push_back

插入常量字符串使用append

插入常量字符(只插入一个字符)使用push_back

                                     

append有多种重载,风格非常类似构造函数。

因此,还需要再注意常量字符串是const char*的问题。

可以从指定的位置加入指定数量的字符、全部用某个字符去“覆盖式加入”、使用迭代器区间去添加。

如从指定位置开始的指定个数(注意一定使用string类型变量,不要用常量字符串等const char*类型变量):

 同样可能用到npos,在无符号整形中这就是最大的数据。


7.2更常用的添加方法:运算符重载 +=

对于运算符+=的运算符重载:

                                           

更加形象,更易理解。

而且+=后面既可以是单个字符,也可以是字符串


7.3 assign函数

其本质是一种赋值,会先清空本来有的内容

                                        

则s1中原先的内容会被改为111111。

                                 


7.4 重点: insert 与 erase

insert的第一个参数都是与位置相关的,下标或者迭代器。

                                      

但是insert能少用就少用,因为其时间复杂度是O(n),每次插入都需要把被插入位置之后的所有元素平移。

但是不支持只在某个位置只插入一个字符:

这样是不可以的: 

                                              

这样是可以的:

                                           

但是给y加上一个双引号呢?(应该是没问题的)但是必须有支持单个字符的情况,否则无法应对诸多情况(如字符作为变量):

                                        

毕竟字符串不可能完全替代字符的插入。

如下,4的意思是在第四个字符的位置插入(包括0),会将位置4之后的元素都平移。

                     

频繁使用平移插入时需要注意效率。

此处只实现第三种(对应的是方法7)就好,也就是通过迭代器加入对应的区间中的字符串。                                                                                     

不过还是那句话,insert的时间复杂度不低,慎用。

erase同样时间复杂度为O(n),也要慎用。

关于erase,共有三种用法,一种是关于下标的,从pos位置删len个。还有两种是迭代器版本的。

我们观察第一个重载,默认是从0位置开始,删除size_t类npos个数据(很多个),也就是如果什么都不传就会全部删完。 同理,如果还是"is too short",依然是有多少删多少。

长度超了就自动删完,但是下标超了或者迭代器超了,就会报错。下标和迭代器必须合法!

                                   


7.5 replace替换函数 (注意两个迭代器传参时的左闭右开

         

                                   

                 将第五个位置开始的一个元素换成后面的内容(此处为const char*)

对于迭代器版本:i1和i2依然指的是一个左闭右开的空间。

        针对是char* 还是string&的问题 ,之前的解释都没有错,但是只实现一个string版本的就可以了,因为string类型可以用char* 去自动构造,只是在char*和string都存在时,会使用更匹配的那个。

      之前的构造函数中需要注意两种参数的不一样是因为 char*作为参数和string作为参数的功能是不一样的(前者表示前n个,后者表示从下标为n的位置开始的)。这次是一样的,所以只用实现一个。

但是隐式类型转换又会一定程度上较低效率,而c++就是以效率著称的语言.......

比如我们想实现一个功能,将给出的string 类型的s中的所有空格都替换为%20:

string& DeleteSpace(string& s) {
	for (auto it = s.begin(); it != s.end();) {
		if (*it == ' ') {
			s.replace(it, it+1, "%20");
			it += 3;
		}
		else {
			++it;
		}
	}
	return s;
}

如果写成s.replace(it,it,"%20"),就会不停的在第一个空格处加上%20。左闭右开的目的就是从 左边的闭区间开始替换,但是不替换右边开区间那个位置,因此会不停的替换直到溢出。

使用迭代器时(尤其是两个迭代器)都要注意左闭右开的问题。

                                           (replace处也可以直接使用下标版本) 

但是这样效率并不高,

除非替换的内容相互一样长(将三个元素换成另外三个元素)。

否则每次都涉及平移后面的全部内容。

可以用空间换时间的做法:

string QuickDeleteSpace(string& s) {
	string ret;
	for (auto ch : s) {
		if (ch != ' ') {
			ret += ch;
		}else{
			ret += "%20";
		}
	}
	return ret;
}

但是此处的ret是临时变量,不能再返回引用,而应该传值返回。 


8.小试牛刀

917. 仅仅反转字母 - 力扣(LeetCode)

class Solution {
public:
    bool IsLetter(char c){
        return ('a'<=c&&'z'>=c)||
               ('A'<=c&&'Z'>=c);
    }
    string reverseOnlyLetters(string s) {
        size_t left=0,right=s.size()-1;
        while(left<right){
            //让左右两个下标都找到是字母的位置。
            while(!IsLetter(s[left])&&left<right)
                 left++;
            while(!IsLetter(s[right])&&left<right)
                 right--;

            swap(s[left],s[right]);
            left++;
            right--;
        }
        return s;
    }
};

类似于快排中每一次单趟的方法。

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

利用计数统计,建立哈希映射,两次遍历。

new的开辟是利用了初始化的int数组值都为0

初始化的char数组值都为'\0'

class Solution {
public:
    int firstUniqChar(string s) {
        int* arr=new int[26]{0};
        for(auto e : s){
            arr[e-'a']++;
        }
        for(int i=0;i<s.size();i++){
            if(arr[s[i]-'a']==1){
                delete[] arr;
                return i;
            }
        }
        delete[] arr;
        return -1;
    }
};

在vs中,作为内置类型char的数组,delete delete[] free都可以用于释放new出来的数组,但是在leetcode中只能使用delete[] 

125. 验证回文串 - 力扣(LeetCode)


125. 验证回文串 - 力扣(LeetCode)

回文判断都是头尾指针。

此题最大的坑在于:‘0’+32是P,所以直接用32加减的方法不妥。需要加上判断语句。

class Solution {
public:
    bool IsLetterOrNumber(char& ch){
         if(ch>='A' && ch<='Z'){
            ch+=32;
            return true;
         }
         return (ch>='a' && ch<='z')||(ch>='0'&&ch<='9');
    }

    bool isPalindrome(string s) {
          int left=0,right=s.size()-1;
          while(left<right){
            while(left<right && !IsLetterOrNumber(s[left]))
          left++;
           while(left<right && !IsLetterOrNumber(s[right]))
          right--;

          if(s[left]!=s[right]){
             return false;
              }
              left++,right--;
          }
          return true;
    }
};

难点:大数运算之字符串加减

415. 字符串相加 - 力扣(LeetCode)

class Solution {
public:
    string addStrings(string num1, string num2) {
        int pcur1=num1.size()-1;
        int pcur2=num2.size()-1;
        string ans;            
        ans.reserve(max(num1.size(),num2.size()));
        int next=0;
        
        while(pcur1>=0||pcur2>=0){
             int x1= pcur1 >= 0 ? num1[pcur1]-'0' : 0;
             int x2= pcur2 >= 0 ? num2[pcur2]-'0' : 0;
             next=x1+x2+next;
             ans.insert(0,1,next%10+'0');
             next/=10;

             --pcur1,--pcur2;
        }   
        if(next){
            ans.insert(0,1,'1');
        }
        return ans;
    }
};

此处的reverse是用来先调整capacity大小,目的是提升效率,不加这一句也能跑过。但是不能乱使用resize。如果胡乱加大了size空间,ans串中可能有其他意想不到的值

              

我们进一步优化我们的算法,计算此时的时间复杂度:

算时间复杂度时需要联系每个接口来计算,此处消耗最大的就是每一次的insert(不想使用第二个参数也可以使用iterator版本)。如此处的头插复杂度就是n^2.

所以可以使用尾差加逆置(使用algorithm中的reverse,使用方法直接查cpp官网)的方法,复杂度就是O(n)

 

class Solution {
public:
    string addStrings(string num1, string num2) {
        int pcur1=num1.size()-1;
        int pcur2=num2.size()-1;
        string ans;            
        ans.reserve(max(num1.size(),num2.size()));
        int next=0;
        
        while(pcur1>=0||pcur2>=0){
             int x1= pcur1 >= 0 ? num1[pcur1]-'0' : 0;
             int x2= pcur2 >= 0 ? num2[pcur2]-'0' : 0;
             next=x1+x2+next;
             //ans.insert(ans.begin(),next%10+'0');
             ans.push_back(next%10+'0');
             next/=10;

             --pcur1,--pcur2;
        }   
        if(next){
            ans.push_back('1');
        }
        reverse(ans.begin(),ans.end());
        return ans;
    }
};

9.capacity大类

capacity表示当前string实际开出的空间大小,这一点有别于size,size表示的是此时已有字符串所占用空间的大小。

 9.1 max_size(了解)

max_size没什么用,目的就是告诉你理论上最多能开出多大的空间。

                  

实际上是不可能开出这么大空间的(这已经是九百万TB了)


9.2clear

就是全部清空,类似于不传参的erase

9.3扩容

在vs上,因为buffer数组的存在:

第一次严格意义不算扩容,因为刚开始都是存在buffer数组上的。

第一次change之后就开始在堆上存放数据了。

void TestOfCapacity(string& s) {
	size_t sz = s.capacity();
	cout << "the primary size :" << sz << endl;
	for (int i = 200; i >= 0; i--) {
		s.push_back('p');
		if (sz != s.capacity()) {
			sz = s.capacity();
			cout << "now the size of capacity is : " << sz << endl;
		}
	}
}

capacity显式出来的比实际的capacity的空间少一个,会预留一个给\0

在vs2019中:刚开始是二倍扩容,后面是接近一点五倍扩容

在linux中:

明显没有buffer数组 ,并且每次都是2倍扩容。

g++编译器更加直观,vs的编译器封装更加全面,有其自己的想法。

不过无论g++还是vs,capacity都没有计算斜杠零。


9.4 resize和reserve  ★★

reserve(保留,意为保留一定量的空间,区分于逆置reverse)和resize,分别用来管理capacity和size

但是两者的影响范围又稍有不同:

reserve只影响capacity,resize主要目标是影响size,但是因为影响了size所以也会影响capacity。

reserve:

在vs中:

你需要n个空间,他可能开的比n多。

对于reserve缩容,不同的平台有不同的实现(所以不建议使用)。

并且vs中的string有一个buffer数组,所以再怎么变都不会小于15 (真实大小是16,还有一个位置留给\0)

reserve的真正作用是在知道大致需要多少空间时,节省扩容的动作

                                 

提升了效率。

不过依然有一个问题:

                                              

这样操作会越界,编译器会报错。

operator []只能访问size以内的部分,而reserve不会改变size,所以超过size的部分依然不能被使用。因此在reserve之后是不能实现从尾部开始使用的,任然只能使用原size以内的部分。

resize:

如果不给值,默认值就是ASCII值为0的‘\0’

在已有空间上使用resize并且初始化内容:

不会改变原空间内容,但是会同时对capacity和size造成影响。


练一练: 

//下面程序的输出结果是:
int main()
{
	string str("Hello Bit.");
	str.reserve(111);
	str.resize(5);
	str.reserve(50);
	cout<<str.size()<<":"<<str.capacity()<<endl;
	return 0;
}

在vs2022中,最后输出的结果是 5 :11 

分析:

str.reserve(111); //调整容量为 111

str.resize(5);   //调整元素个数为 5

str.reserve(50);  //调整容量为 50,由于调整的容量小于已有空间容量,故容量不会减小

所以size=5 capacity=111


9.5 shrink_to_fit

该函数是专门用来缩空间的。

但是空间在底层是不能分段释放的

所以其本质是开一个更小的空间,把已有内容拷贝过去。本质是时间换空间的做法,代价较大。

//调用方法:
str.shrink_to_fit();

9.6 at

功能与重载的[]类似,区别在于检查方面:[ ]用assert检查,会直接报错,而at可以在抛异常时捕获。 

但是只有下标版本,没有迭代器版本。


9.7 c_str

string是可以由char*作为参数来构造的,但是char* 不能通过string来构造。

作用是将string转换到c语言中的标准字符串。

                          

目的是与只能编C的接口兼容,如fopen不能使用str,只能使用char* , 此时就能发挥c_str的作用。

string file("string_test.cpp");
FILE* pf = fopen(file.c_str(), "r");

注意,c_str返回的类型是const char* 所以对于下题:

    string a="hello world";
	string b=a;
	if (a.c_str()==b.c_str())
	{
     cout<<"true"<<endl;
	}
	else cout<<"false"<<endl;

 a和b的内容虽然一样,但是存放的地址不一样,所以应该输出false。


10.find

由缺省参数为0可知,find函数默认都是从头开始找。给了数字之后就可以从数字对应的位置开始找。

可以找单个字符也可以找一个句子。

若想从尾部开始找,可使用refind。
 

比如我们想取出一个后缀名suffix:

此时又涉及一个操作:substr

 将pos位置开始长度为len的内容拷贝到一个新生成的字符串中去。

记住substr的两个参数都是整数即可,前一个是位置,后一个是长度。

string file("string_test.cpp");
size_t pos=file.find('.');
string suffix = file.substr(pos, file.size() - 1);

cout << suffix << endl;

 再比如,

希望用find分别得到一个网址的协议,域名,端口 。

网址:

https://leetcode.cn/problems/add-strings/description/
string url("https://leetcode.cn/problems/add-strings/description/");
size_t pos1 = url.find(':',0);
string url1 = url.substr(0, pos1 - 0);
size_t pos2 = url.find('/', pos1 + 3);//从leetcode的l处开始寻找
string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
string url3 = url.substr(pos2 + 1, url.size() - (pos2 + 1));

cout << url1 << endl << url2 << endl << url3 << endl;

此处选取pos时依然利用左闭右开的好处:直接做减法就能获得长度len


find_first_of 四兄弟 (类似于strtok) ,了解即可

使用一个string类型对象调用该函数时,他能正向(或逆向)找出 (非)你给出的参数中第一个与string对象中所包含元素一样的位置。所以最坏时间复杂度是两个串的长度之积,O(m*n)

官网解释:

     Searches the string for the first character that matches any of the characters specified in its arguments.(功能)
     When pos is specified, the search only includes characters at or after position pos, ignoring any possible occurrences before pos.(第二个参数pos的作用)
     Notice that it is enough for one single character of the sequence to match (not all of them). See string::find for a function that matches entire sequences.(与find区分)

find_last_of的实际作用:

比如在一个项目中,需要处理不同系统下的多个文件路径,但是由于Linux中的文件分隔符是右斜杠/,windows中的文件分隔符是左斜杠\  ,如果使用find函数查找这两个·会去找这两个一起出现的字符串(当然找不到),此时就需要find_first_of来发挥作用

void SplitFilename(const std::string& str)
{
	std::cout << "Splitting: " << str << '\n';
	std::size_t found = str.find_last_of("/\\");
	std::cout << " path: " << str.substr(0, found) << '\n';
	std::cout << " file: " << str.substr(found + 1) << '\n';
}

int main()
{
	std::string str1("/usr/bin/man");
	std::string str2("c:\\windows\\winhelp.exe");

	cout << str2 << endl;

	SplitFilename(str1);
	SplitFilename(str2);

	return 0;
}

左斜杠\\需要多加一个表示转义字符,而右斜杠/不用,在windows中的操作要注意这个。

转义字符仍然只是一个字符。

11. string相关的部分外部函数

11.1 operator +

为什么+不能写成成员函数,而是全局重载:

                                  

原因就在于最后这种,无法通过成员函数实现。

          

除了char* + char*没有实现,其他都能直接加。 

11.2 operator <  > ==

类似于strcmp,按照字典序比较两个字符串相应位置的ASCII码值。

成立返回1,不成立返回0。

流插入的优先级较高,需要加括号。

                             

11.3  getline

getline:

想获取一行中包含空格的串,不能直接使用流提取,需要使用getline

                                        

字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)

#include <iostream>
using namespace std;

int main() {
   string s;
   getline(cin,s);
   int pos=s.rfind(' ');
   cout<<s.size()-(pos+1);
   return 0;
}

持续获取:

你如果一次输入按下三次空格再回车,其实这个循环就走了三次,空格作为分隔符,将之后的内容都存在缓存区,cout的时候会输出全部,再等待下次输出。 

 使用 ctrl+Z+回车 可以终止这个程序。


11.4 字符串和int类型的转换

       to_string和stoi(类比于C语言的atoi itoa    a  to  i  ASCII到int      i   to   a    int 到ASCII)

但是stoi(string to int)不能处理大数运算,串长之后就会爆,放不下。

            


12.编码表UNICODE

string的底层是如何装汉字的呢?

计算机中的编码除了ASCII编码,还有万国码(UNICODE)。

万国码支持ASCII编码,并以此为基础增加了世界上绝大多数国家的语言文字。

大部分汉字编入两个字节的部分,部分生僻的编入3字节或者4字节

因此有可能一个字节表示,有可能两个字节表示,有可能三个字节去表示。

 string默认支持UTF8,还有默认支持UTF16和UTF32、以及双字符的wstring

所有的string都是以basic_string为模版实现的。

还有一种库叫GBK库,由于万国码在中文一些方面优化的不够好,这一套GBK作为国标也被很多系统支持,如windows等,更适合中国宝宝的体质。

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

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

相关文章

lora体验

项目链接 GitHub - cloneofsimo/lora: Using Low-rank adaptation to quickly fine-tune diffusion models. 现在如果想体验stable diffusion的lora功能&#xff0c;有很多种渠道吧&#xff0c;虽然lora是微软开源&#xff0c;但是cloneofsimo提供了适配stable diffusion的lor…

Centos 停服倒计时!你的操作系统何去何从?

在计算机技术的不断演进中&#xff0c;操作系统扮演着至关重要的角色。然而&#xff0c;对于许多企业和个人用户来说&#xff0c;CentOS的突然停服消息带来了一场不小的冲击。作为一款备受欢迎的企业级Linux发行版&#xff0c;CentOS的停服意味着用户需要重新评估自己的操作系统…

Win10 WSL2 Ubuntu 22.04 配置深度学习环境

文章目录 WSL安装Anaconda下载Anaconda安装包安装配置conda命令conda换国内源conda初始化shell环境conda init手动源激活脚本&#xff1a; 安装Windows上的NVIDIA GPU驱动WSL安装CUDA Toolkit安装12.1版本环境配置报错1报错2 内存不足导致安装崩溃问题描述问题分析问题解决 WSL…

VMware虚拟机中ubuntu使用记录(8)—— 如何在Ubuntu18.04中安装运行非ROS版本的ORB_SLAM3跑官方数据集(全程手把手教学安装)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 ORB_SLAM3的介绍一、gitee下载ORB_SLAM3源码1. gitee导入gitHub仓库 二、安装支持C特性依赖三、安装Pangolin1. 安装Pangolin的依赖2. 下载编译 四、安装Eigen31.下…

java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)

01.在populateBean 方法中的&#xff1a;用于Autowird&#xff0c;Resource注入 // 后处理器已经初始化boolean hasInstAwareBpps hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck (mbd.getDependencyCheck() ! AbstractBeanDefinition.DEP…

基于FPGA的数字信号处理(11)--定点数的舍入模式(2)向最临近值取整nearest

前言 在之前的文章介绍了定点数为什么需要舍入和几种常见的舍入模式。今天我们再来看看另外一种舍入模式&#xff1a;向最临近值取整nearest。 10进制数的nearest nearest&#xff1a; 向最临近值方向取整。它的舍入方式和四舍五入非常类似&#xff0c;都是舍入到最近的整数…

【电路笔记】-无源高通滤波器

无源高通滤波器 文章目录 无源高通滤波器1、概述2、一阶高通滤波器的频率响应3、高通滤波器示例4、二阶高通滤波器5、RC 差异化因素高通滤波器与低通滤波器电路完全相反,因为这两个组件已互换,滤波器输出信号现在从电阻器两端获取。 1、概述 由于低通滤波器只允许低于其截止…

【论文合集1】- 存内计算加速机器学习

本章节论文合集&#xff0c;存内计算已经成为继冯.诺伊曼传统架构后&#xff0c;对机器学习推理加速的有效解决方案&#xff0c;四篇论文从存内计算用于机器学习&#xff0c;模拟存内计算&#xff0c;对CNN/Transformer架构加速角度阐述存内计算。 【1】WWW: What, When, Where…

C# OpenCvSharp Demo - 棋盘格相机标定

C# OpenCvSharp Demo - 棋盘格相机标定 目录 效果 项目 代码 下载 效果 项目 代码 using OpenCvSharp; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Text; using Sys…

2019年计算机真题

2019年计算机真题 离散数学 一、用逻辑符号表达下列语句(论域为包含一切事物的集合) 1&#xff09;过平面上的两个点&#xff0c;有且仅有一条直线通过。 解: (1) P ( x , y ) : x , y \mathrm{P}_{(\mathrm{x}, \mathrm{y})}: \mathrm{x}, \mathrm{y} P(x,y)​:x,y 是平面上的…

线性表

1.1线性表的定义 线性表&#xff1a;零个或多个数据元素的有限序列。 注&#xff1a; &#xff08;1&#xff09;它是一个序列。元素之间是有顺序的&#xff0c;若元素存在多个&#xff0c;则第一个元素无前驱&#xff0c;最后一个元素无后继&#xff0c;其他元素有且只有一个…

SpringBoot 使用logback(多环境配置)

Logback是由log4j创始人设计的又一个开源日志组件。可用于项目日志功能。官网地址 第1步&#xff1a;添加坐标依赖 <!--logback--> <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version…

该问题未得到解决(仅记录)

https://releases.ubuntu.com/bionic/进入网页下载ubuntu 选择烧录软件将下载的Ubuntu烧录到U盘中 之前用这个U盘烧录过一次&#xff0c;成功了&#xff0c;后来应该是U盘受损或者是什么其他原因使得用这个U盘总是烧录失败

网安面经之xss漏洞

目录 一、XSS漏洞 1、xss漏洞原理与危害 2、xss的类型&#xff1f;区别&#xff1f;修复&#xff1f; 3、常见使用的标签&#xff0c;payload构造 一、XSS漏洞 1、xss漏洞原理与危害 原理&#xff1a;xss就是跨站脚本攻击漏洞&#xff0c;也可以理解为前端的代码注入&…

数字人金融应用技术指南

根据《北京金融科技产业联盟团体标准管理办法》的规定&#xff0c;2024年3月27日经北京金融科技产业联盟第三届理事会第二次会议审议&#xff0c;批准发布《数字人金融应用技术指南》&#xff08;T/BFIA 027—2024&#xff09;、《图数据库金融应用技术要求》&#xff08;T/BFI…

医院转型新突破:精益六西格玛助力管理费大降13%,业务飙升26%

近年来&#xff0c;越来越多的医院开始尝试引入精益六西格玛管理模式&#xff0c;以期实现管理费的降低和业务量的增长。那么&#xff0c;精益六西格玛模式真的有这么牛吗&#xff1f;深圳天行健六西格玛培训公司将从实践角度出发&#xff0c;探讨精益六西格玛如何助力医院实现…

GAME101-Lecture06学习

前言 上节课主要讲的是三角形的光栅化。重要的思想是要利用像素的中心对三角形可见性的函数进行采样。 这节课主要就是反走样。 课程链接&#xff1a;Lecture 06 Rasterization 2 (Antialiasing and Z-Buffering)_哔哩哔哩_bilibili 反走样引入 ​ 通过采样&#xff0c;得到…

Vuex核心概念-state状态

目录 一、目标 二、提供数据 三、使用数据 1.通过store直接访问 2.通过辅助函数&#xff08;简化&#xff09; 一、目标 明确如何给仓库提供数据&#xff0c;如何使用仓库的数据 二、提供数据 State提供唯一的公共数据源&#xff0c;所有共享的数据都要统一放到Store中的…

了解 条码工具 Dynamsoft 在条码读取器中的形态运算

在图像处理中&#xff0c;术语形态学是指分析形状以填充小孔、去除噪声、提取轮廓等的一组操作。形态学操作很像空间卷积中的过滤过程。有两个部分在起作用&#xff1a;结构元素和预定义的计算规则。 点击下载Dynamsoft最新版https://www.evget.com/product/3691/download 结…

C#上位机1ms级高精度定时任务

precisiontimer 安装扩展包 添加引用 完整代码 using PrecisionTiming;using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; us…