C++——string类

发布于:2022-12-23 ⋅ 阅读:(790) ⋅ 点赞:(0)

目录

浅谈什么是string类 

string类的常用接口 

 赋值操作符

 string类对象的容量操作

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

迭代器 

反向迭代器和const迭代器 

string类对象的修改操作 

扩容机制 

习题  仅仅反转字符串

 习题 字符串相加


 

浅谈什么是string类 

1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
4. 不能操作多字节或者变长字符的序列。在使用string类时,必须包含#include头文件以及using namespace std;
 

string类的常用接口 

 

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


 

将原始试图展开后,我们看到不仅存了hello,还存了/0, 说明遵守C语言的规则,所以表格里说用C格式字符串构造string类对象s2

 

sting类可以直接使用cout和cin,因为底层实现了运算符重载 

string类也可直接拷贝构造

substring 
string (const string& str, size_t pos, size_t len = npos);
substring可实现部分拷贝,从pos位置开始拷,拷len个字符,如果从pos往后数len个字符,还未到len的时候,字符串已经结束,此时拷到结尾即可说明:

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).

 第三个参数可以不传,给了一个缺省参数npos,npos是一个静态成员变量,值为-1,注意是size_t类型的-1,也就是说npos是一个特别大的数字,如果没传第三个参数,则从pos位置一直拷贝到结束。

 

from sequence (5)
string (const char* s, size_t n);
用s字符串的前n个去构造对象
fill (6)
string (size_t n, char c);

 

用n个c字符去初始化 

还可以这样写,这是先构造s8,然后再拷贝构造,把hello world拷贝构造给s8, 发生了隐式类型转换,前面博客有提到过

sting里面也有析构,除了作用域自动调用

 赋值操作符

 

 支持拷贝赋值,字符串赋值,字符赋值

s2="hello world"是构造+拷贝构造-》优化为直接构造

 string类对象的容量操作

函数名称 功能说明
size(重点) 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty (重点) 检测字符串释放为空串,是返回true,否则返回false
clear (重点) 清空有效字符
reserve (重点) 为字符串预留空间**
resize (重点) 将有效字符的个数该成n个,多出的空间用字符c填充

 size返回string有多少个字符

 

length和size作用一样,但是建议用size,不用length 

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

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

在底层实现了[]的运算符重载,[]可以让string类对象像数组一样去访问,可以用[]对字符进行修改,[]底层大概实现的逻辑

 用[]结合前面的size遍历string

也可修改字符,让s1[i]++,修改字符

const对象也能用[]去遍历,但是不能修改

 []越界后程序直接崩溃,这是因为[]内部会检查越界

 at和[]功能一致

 

 at越界之后,会抛异常

迭代器 

 迭代器是一个像指针一样的对东西

 

void test_string4()
{
	string s("hello");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
}
int main()
{
	test_string4();
	return 0;
}

迭代器是一个像指针一样的东西,有可能是指针,但也有可能不是指针,只不过用法像指针,iterator是迭代器,it是名字,可随便取,使用迭代器时要说清楚是哪个类的,如string""iterator,这里的迭代器是string类,

end指向最后一个字符的下一个位置 ,一开始让it指向h,之后++it遍历

对于string迭代器用的不多,string有[],[]更好用

list/map/set.....这些只能用迭代器

迭代器重要是因为它是所有容器通用的访问方式,并且用法类似

C++中范围for的底层其实就是迭代器

这里有迭代器 

范围for底层原理,可以观察出范围for是用迭代器实现的

 范围for里面人为++后不起作用,前面博客有提到过,若要修改则要加一个引用

反向迭代器和const迭代器 

  rbegin和rend

rbegin + rend rbegin()返回一个逆向迭代器,指向字符串的最后一个字符,rend()函数返回一个逆向迭代器,指向字符串的开头(第一个字符的前一个位置

 反向迭代器++倒着走

可以这样理解rbegin在倒数第一个字符处,rend在第一个的前一个位置 

 还有const迭代器

void PrintSring(const string& str)
{
	string::const_iterator it = str.cbegin();//const begin
	while (it != str.cend())
	{
		cout << *it;
		it++;
	}
	cout << endl;
}
void test_string5()
{
	string s("hello");
	string::reverse_iterator rit = s.rbegin();
	
	while (rit != s.rend())
	{
		cout << *rit ;
		rit++;
	}
	cout << endl;
	PrintSring(s);
}
int main()
{
	test_string5();
	return 0;
}

这里的*it不能修改,因为被const所修饰 

也可用auto替代,自动识别类型 

const也有反向迭代器,加——reverse

string类对象的修改操作 

函数名称 功能说明
push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串
operator+= (重点) 在字符串后追加字符串str
c_str(重点) 返回C格式字符串
find + npos(重点) 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回

push_back可以插入字符,注意是字符


 

插入字符串用append

也可用+=在字符串后追加字符串,可+=字符,也可+=字符串,也可+=string类

 +=的底层是用pushback和attend实现的

append还会提供支持迭代器版本的初始化

 

 还可以这样使用,添加一部分

 汉字的存储规则跟字符类型不一样

 

 

max_size可以查看最多能存储多少数据,这个接口一般不用

 

capacity可查看容量大小 

扩容机制 

 string下的扩容机制

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

 在vs下首次扩容是15,实际是16,因为没有给/0给空间

 不同软件不同版本扩容机制不同

我们可以提前开空间,reserve只负责开空间,不初始化

 

这里开1000个空间,实际给了1007个空间,LInux下给1000,开1000个空间

 还有一个resize,resize是开空间+初始化

 

 

resize不仅会改变容量,还会改变size ,还会给初始值0

习题  仅仅反转字符串

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

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

 习题 字符串相加

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

class Solution {
public:
    string addStrings(string num1, string num2) {
int end1=num1.size()-1;
int end2=num2.size()-1;
int next=0;
   string str;
while(end1>=0||end2>=0)
{
    int val1=end1 >=0 ?num1[end1]-'0':0;
      int val2=end2>=0?num2[end2]-'0':0;
      int sum=val1+val2+next;
      next=sum>9?1:0;
      char a=sum%10+'0'; //如果是俩位数,其高位放在next,取模留低位,
   str.insert(0,1,a);//在第0个位置插1个字符a
   --end1;
   --end2;
}
if(next)
str.insert(0,1,'1');
return str;
    }
};

 从最后一位开始,对应位相加,等遍历到字符头部的时候跳出循环,跳出循环后,如果进位还是1,则把1要头插进去

这个写法效率比较低,因为是头插,头插之后要把后面的挪到前面,挪的时间复杂度O(N^2)

 方法二:尾插之后,进行逆置

class Solution {
public:
    string addStrings(string num1, string num2) {
int end1=num1.size()-1;
int end2=num2.size()-1;
int next=0;
   string str;
while(end1>=0||end2>=0)
{
    int val1=end1 >=0 ?num1[end1]-'0':0;
      int val2=end2>=0?num2[end2]-'0':0;
      int sum=val1+val2+next;
  next=sum>9?1:0;
  str+=('0'+sum%10);
   --end1;
   --end2;
}
if(next)
str+='1';
reverse(str.begin(),str.end());//逆置
return str;
    }
};

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到