string类

一、为什么要学习string类

  • C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
  • 在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

二、标准库中的string类

1.string类

string类的文档介绍

  • 字符串是表示字符序列的类。

  • 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。

  • string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。

  • string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
    string是被 typedef过的
    string是被typedef过的

  • 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结

  • string是表示字符串的字符串类。
  • 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  • string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string。
  • 不能操作多字节或者变长字符的序列。

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

2.string类的常用接口说明

(1) string类对象的常见构造

(constructor)函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s)拷贝构造函数
void Teststring()
{
 string s1; // 构造空的string类对象s1
 string s2("hello yzwzz"); // 用C格式字符串构造string类对象s2
 string s3(s2); // 拷贝构造s3
}

(2)string类对象的赋值重载

在这里插入图片描述

    string s1("hello");
	string s2("world");
	s1 = s2;
	s1 = "www";
	s1 = 'w';

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

函数名称功能说明
operator[]返回pos位置的字符,const string类对象调用
begin+endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin+rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式
①下标+[ ]
    string s1("hello world");
	//第一种方式,下标+[ ]  
	//最方便
	for (size_t i = 0; i < s1.size(); i++)//size()计算大小,不包括'\0'
	{
		cout << s1[i];//s1.operator[i]
	}
	cout << endl;

在这里插入图片描述
第一个可读可写,第二个只可读。
在这里插入图片描述
常对象只能调常成员函数。

在这里插入图片描述
at也可以遍历string,但与[ ]的区别是越界时at抛异常,[ ]报断言错误。

②迭代器

迭代器是什么 ?
迭代器是像指针一样的东西或者就是指针 。
begin指向第一个位置,end指向最后一个数据的下一个位置。
在这里插入图片描述

    //第二种方式,迭代器
	string s1("hello world");
	string::iterator it = s1.begin();
	while (it != s1.end())//不要写小于
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

rbegin指向最后一个位置,rend指向第一个数据的前一个位置。
在这里插入图片描述

    //反向迭代器
	string s("hello world");
	string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;

const迭代器

    //const迭代器
	const string s("hello world");
	string::const_iterator it = s.begin();
	while (it != s.end())
	{
		//*it+=1;//不可写
		cout << *it;//可读
		it++;
	}
	cout << endl;
    //反向的const迭代器
    const string s("hello world");
	string::const_reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;

string::const_reverse_iterator太长了,写起来很麻烦,可以用auto来自动推导。
在这里插入图片描述

③范围for

其实就是被替换成了迭代器。

    //第三种方式,范围for(C++11)
	string s1("hello world");
	for (auto ch : s1)
	{
		cout << ch;
	}
	cout << endl;

(4)string类对象的容量操作

函数名称功能说明
size(推荐使用,与STL匹配)返回字符串有效字符长度
length(使用较少)返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符
reserve为字符串预留空间(提前开好空间,以减少扩容带来的不必要的空间消耗)
resize将有效字符的个数该成n个,多出的空间用字符c填充

在这里插入图片描述
在这里插入图片描述
注意

  • 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不会改变容量大小。

(4)string类对象的修改操作

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

在这里插入图片描述
push_back()和append()可以实现尾插,但其实最好用的还是+=:
在这里插入图片描述
在这里插入图片描述
C++98下两种交换的比较
在这里插入图片描述
返回C形式的字符串
在这里插入图片描述
在这里插入图片描述
从pos位置开始取len长度的字符串:
在这里插入图片描述
分离一个网址的各个部分
在这里插入图片描述
在这里插入图片描述
getline:
在这里插入图片描述
字符串最后一个单词的长度
在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string str;
    getline(cin,str);
    size_t pos=str.rfind(' ');
    if(pos!=string::npos)
    {
        cout<<str.size()-pos-1<<endl;
    }
    else
    {
        cout<<str.size()<<endl;
    }
    return 0;
}

在这里插入图片描述

三、string类的深浅拷贝

1.深浅拷贝

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace yzwzz
{
	class string
	{
		//先实现一个简单的string,只考虑资源管理深浅拷贝问题
		//暂不考虑增删查改
	public:
		string(const char* str)
			:_str(new char[strlen(str) + 1])
		{
			strcpy(_str, str);
		}
		~string()
		{
			if (_str)
			{
				delete[] _str;
			}
		}
		const char* c_str() const
		{
			return _str;
		}
		char& operator[](size_t pos)
		{
			assert(pos < strlen(_str));
			return _str[pos];
		}
		size_t size()
		{
			return strlen(_str);
		}

	private:
		char* _str;
	};
}
int main()
{
	yzwzz::string s1("hello");
	yzwzz::string s2(s1);
	cout << s1.c_str() << endl << s2.c_str() << endl;
	return 0;
}

上述代码有没有什么问题?
有:
上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构 造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
浅拷贝的主要问题在于:
1.析构两次。
2.s1、s2共用同一块内存空间,修改一个会同时改变另外一个。

2.浅拷贝

也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了拷贝。

3.深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
为上面的代码加上深拷贝,就可以正常运行了。

        string(const string& s)
			:_str(new char[strlen(s._str) + 1])
		{
			strcpy(_str, s._str);
		}

赋值运算符重载的深拷贝:

        string& operator=(const string& s)
		{
			if (this != &s)
			{
				//可能开空间失败了,但又把_str给释放了
			    /*delete[] _str;
			    _str = new char[strlen(s._str) + 1];
			    strcpy(_str, s._str);*/
				char* tmp = new char[strlen(s._str) + 1];
				strcpy(tmp, s._str);
				delete[] _str;
				_str = tmp;
			}
			//可能开空间失败了,但又把_str给释放了
			/*delete[] _str;
			_str = new char[strlen(s._str) + 1];
			strcpy(_str, s._str);
			return *this;*/
			char* tmp = new char[strlen(s._str) + 1];
			strcpy(tmp, s._str);
			delete[] _str;
			_str = tmp;
			return *this;
		}

四、扩展阅读

C++面试中STRING类的一种正确写法
STL中的string类怎么了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值