STL学习-走进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 s1;//空对象,默认全是\0
	string s2("hello world"); 用C格式字符串构造string类对象s2
	string s3(5, 'x');//用n个C格式字符构造string类对象
	string s4(s3);//拷贝构造	

容量操作

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

	string s1("hello world");
	cout << "有效长度为:" << s1.size() << endl;
	cout << "空间总大小为:" << s1.capacity() << endl;
	cout << "s1是否为空:" << s1.empty() << endl;
	cout << s1 << endl << endl;;

	s1.clear();
	cout << "有效长度为:" << s1.size() << endl;
	cout << "空间总大小为:" << s1.capacity() << endl;
	cout << "s1是否为空:" << s1.empty() << endl<<endl;
	
	s1.reserve(500);
	cout << "预留的空间总大小为:" << s1.capacity() << endl<<endl;
	
	s1.resize(5, 'x');
	cout << "有效长度为:" << s1.size() << endl;
	cout << "空间总大小为:" << s1.capacity() << endl;
	cout << "s1是否为空:" << s1.empty() << endl;
	cout << s1 << endl;
}

运行结果为:

在这里插入图片描述

注意:

  • size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
  • clear()只是将string中有效字符清空,不改变底层空间大小。
  • resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
  • reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

访问及遍历操作

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

image-20230525134142970

//验证重载[]
void test_string()
{
	string s1("hello world");
	cout <<s1 << endl;
	//遍历s1,并讲s1的所有字符加1:
	for (int i = 0;i < s1.size();i++)
	{
		s1[i]++;//通过[]修改字符
	}
	cout << s1 << endl;
}
//验证正向迭代器(begin和end函数)
void test_string()
{
	string s1("hello world");
	string::iterator it = s1.begin();
	cout << s1 << endl;
	while (it != s1.end())
	{
		(*it)++;
		cout << *it << " ";
		it++;
	}
}
//验证反向迭代器(rbegin和rend函数)
void test_string()
{
	string s1("hello world");
	cout << s1 << endl;
	string::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *rit;
		rit++;
	}
}
//验证范围for————自动迭代,自动判断结束(底层实际为迭代器)
void test_string()
{
	string s1("hello world");
	cout << s1 << endl;
    //依次取s1中每个字符,赋值给ch
	for (auto& ch : s1)//auto自动判断类型
	{
		ch++;
		cout << ch << " ";
	}
}

注意:若是const迭代器则不能修改字符串的字符!

修改操作

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

其中append、operator+=、find、rfind有多个重载接口,只需要学习常用的:

//验证push_back、insert、erase、append
//push_back接口:
void push_back (char c);
//append接口:
string& append (const char* s);
string& append (const string& str);
//insert接口:
string& insert (size_t pos, const string& str);
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);	
string& insert (size_t pos, const char* s);
string& insert (size_t pos, const char* s, size_t n);	
string& insert (size_t pos, size_t n, char c);
//erase接口:
string& erase (size_t pos = 0, size_t len = npos);
void test_string7()
{
	string s1("hello");
	cout << s1 << endl;
	s1.push_back('-');
	s1.append("world");
	s1.push_back('-');
	
	string s2("I am a student");
	//将s2追加到s1后
	s1.append(s2);
	cout << s1 << endl;
	s1.push_back('-');

	s1.insert(s1.size(), "i am 19");
	cout << s1 << endl;

	s1.erase(0,1);
	cout << s1 << endl;
}
//验证operator+=:
//operator接口:
string& operator+= (const string& str);	
string& operator+= (const char* s);	
string& operator+= (char c);

void test_string()
{
	string s1("hello");
	cout << s1 << endl;
	string s2(" world");
	//string& operator+= (const string& str);
    s1 += s2;
	//string& operator+= (char c);
    s1 += '-';
	s1 += '-';
	//string& operator+= (const char* s);
    s1 += "i am a student";
	cout << s1 << endl;
}
  • 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

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

//验证c_str
//c_str接口:
const char* c_str() const;

void test_string()
{
	//简单验证:
	string s1("hello");
	cout << s1 << endl;
	cout << s1.c_str() << endl;
	s1 += '\0';
	s1 += " world";
	//string对象以size为准,c语言字符串以\0为准
    cout << s1 << endl;
	cout << s1.c_str() << endl;
    //实践验证
    string filename("test.cpp");
	cout << filename << endl;
	//返回c语言风格字符串
	cout << filename.c_str() << endl;
	FILE* fout = fopen(filename.c_str(), "r");
	//如果不以c语言字符串做参数则会报错,错误示范:
	//FILE* fout = fopen(filename, "r");
	assert(fout);
	char ch = fgetc(fout);
	while (ch!=EOF)
	{
		cout << ch;
		ch = fgetc(fout);
	}
}
//验证find和substr,以分割URL(协议+域名+路径)为例
//find的四大接口:
size_t find (const string& str, size_t pos = 0) const;	
size_t find (const char* s, size_t pos = 0) const;
size_t find (const char* s, size_t pos, size_t n) const;
size_t find (char c, size_t pos = 0) const;
//substr接口:
string substr (size_t pos = 0, size_t len = npos) const;

void DealUrl(const string& url)
{
	size_t pos1 = url.find("://");
	if (pos1 == string::npos)
	{
		cout << "非法url" << endl;
		return;
	}
	//从0位置开始截取pos1个字符
	string protocol = url.substr(0, pos1);
	cout << protocol << endl;

	size_t pos2 = url.find('/',pos1+3);
	if (pos2 == string::npos)
	{
		cout << "非法url" << endl;
		return;
	}
	string domain = url.substr(pos1 + 3, pos2 - pos1 - 3);
	cout << domain << endl;

	string uri = url.substr(pos2+1);
	cout << uri << endl << endl;
}

void test_string()
{
	string url1("https://cplusplus.com/reference/string/string/find/");
	string url2("https://blog.csdn.net/weixin_73178229?type=lately");
	DealUrl(url1);
	DealUrl(url2);
}


image-20230525193528992

image-20230525194354240

//验证rfind,以取一个文件的后缀名为例:
//rfind接口:
size_t rfind (const string& str, size_t pos = npos) const;	
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;

void test_string() {
	string filename("test.cpp.tar.zip");
	size_t pos = filename.rfind('.');//从后往前找
	if (pos == string::npos)
	{
		cout << "failed document" << endl;
		return;
	}
	string stuff = filename.substr(pos, filename.size() - pos);
	cout << stuff << endl;
}

在这里插入图片描述

注意:不是所有的接口都要记,只需会使用

非成员函数

函数名称功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators大小比较
void test_string()
{
	string s1("hello");
	string s2(" world");
	string s3 = s1 + s2;
	cout << s3 << endl;
	cin >> s3;
	cout << s3 << endl;
	cout << (s1 == s2) << endl;
	cout << (s1 > s2) << endl;
	
}

在这里插入图片描述

原因:重载>>使用时,读取到空格或回车便停止读取,所以我们不能使用>>将含有空格的字符串读入到string对象中,需要用到getline函数:

//验证getline
void test_string()
{
	string s1("hello");
	string s2(" world");
	string s3 = s1 + s2;
	cout << s3 << endl;
	//cin >> s3;
    getline(cin,s3);
	cout << s3 << endl;
	cout << (s1 == s2) << endl;
	cout << (s1 > s2) << endl;
}

在这里插入图片描述

vs和Linux中g++的string结构的说明

vs下的string结构

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节

string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger one
 value_type _Buf[_BUF_SIZE];
 pointer _Ptr;
 char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。

故总共占16+4+4+4=28个字节。

在这里插入图片描述

g++下string的结构

g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};

好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高

其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量

最后:还有一个指针做一些其他事情。

故总共占16+4+4+4=28个字节。

[外链图片转存中…(img-kB9skccx-1685676392182)]

g++下string的结构

g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};
  • 指向堆空间的指针,用来存储字符串。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值