STL容器之string类

补充:

​ string的优势体现在,1.自动扩容;2.支持字符串使用+=拼接;3.支持下标加[]来遍历;4.内置find,便于字符定位;5.兼容c语言,自动在堆上开辟的空间加’\0’。因为’\0’不支持打印,所以可以使用’\0’来清空缓冲区进行打印。clear的实现就是这样。

​ ASCII码0-32及127对应的字符的显示是不正常的,这些显示不正常的就是不可打印字符,不可打印字符也叫控制字符,其他能显示出来的就是打印字符

​ 严格来说string不属于stl,属于标准库的一部分。string出现的比stl要早。

一、认识string

​ 我们使用的string实际上是basic_string类模板实例化出来的basic_string<char>,即直接就是类型。basic_string设计成模板是为了支持其他字符类型。美国设计ASCII足以满足他们语言的需求,使用01序列和字符形成映射。为了满足各国需要设计了Unicode万国码,而中国设计了GBK,一般汉字2字节。wchar是2字节,char32_t是4字节,char16_t是2字节。使用多字节映射,如:对于汉字是同音字放在一起,可以用编码表实现屏蔽敏感词。

​ 查找c/c++官方文档https://legacy.cplusplus.com/

​ C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

​ 在使用string类时,必须包含#include头文件;

1.什么是string

​ string是管理字符数组的顺序表。

在这里插入图片描述

​ 即string等于basic_string<char>。

2.成员类型

​ Member types是类里面定义的成员类型。

在这里插入图片描述

3.默认成员函数

​ 库实现的默认成员函数,主要是构造,拷贝构造,析构,赋值重载者四个函数,另外两个由编译器自动生成。

在这里插入图片描述

4.迭代器支持

​ STL的六大组件之一。

在这里插入图片描述

5.堆上字符空间相关操作的支持

在这里插入图片描述

6.堆上字符数组的读取

在这里插入图片描述

7.堆上字符数组的写入

在这里插入图片描述

8.堆上字符操作接口

在这里插入图片描述

9.成员常量

在这里插入图片描述

10.非成员函数的重载

在这里插入图片描述

11类型转换函数

​ basic_string里提供了字符类型和其他内置类型相互转换函数

在这里插入图片描述

12.范围访问

​ c++11为了支持范围for,专门设计了<iterator>,包含了begin和end。

二、使用string

1.默认成员函数

1.1常用的构造函数

构造函数共有7个,以下是常用的:

//1.无参构造(默认构造)
string();
//2.用字符串构造
string (const char* s);//c字符串的特点以'\0'结尾。
//3.用n个字符进行构造
string (size_t n, char c);
//4.拷贝构造
string (const string& str);
int main()
{
	//常用
	string s1;//无参构造
	string s2(s1);//拷贝构造
	string s3("hello world");//字符串构造
	string s4(5,'x');//n个字符构造

	//了解
	string ss1(s3, 6, 5);//1.字符串截取构造
    char buffer[4096];
    string ss2(buffer, sizeof(buffer));//读取缓冲区构造
    //迭代器构造
	cout << s3 << endl;
	cout << ss1 << endl;
	return 0;
}

1.2析构函数

​ 自动调用

1.3赋值重载

​ 以下情况使用vs2019处理;

​ 需要注意的是,一般情况下同一行发生赋值加构造,编译器不会优化;

class A
{
public:
	A(int aa = 0 )
		:a(aa)
	{
		cout << "A(int aa = " << aa << ")" << endl;
	}
	A(const A& a1)
	{
		cout << "A(const A& a)" << endl;
		a = a1.a;
	}
	A& operator=(const A& str)
	{
		cout << "A& operator=(const A& str)" << endl;
		a = str.a;
		return *this;
	}
	/*A& operator=(int a1)
	{
		cout << "A& operator=(int a1)" << endl;
		a = a1;
		return *this;
	}*/
	~A() {}

private:
	int a;
};

int main()
{
	A a1;
	A a2(2);
	a2 = 1;
	return 0;
}

在这里插入图片描述

​ 有三个,但是后两个比较冗余,因为自定义类型支持隐式类型转换,产生string临时对象,然后调用第一个赋值重载;

​ 我认为设计的理由可能就是这种隐式类型调用构造与赋值相对于直接赋值效率低,所以设计了新的赋值重载。这样会直接转换成调用这类专门的赋值重载,一步完成,结果如下。

在这里插入图片描述

string& operator= (const string& str);
string& operator= (const char* s);
string& operator= (char c);

2.非成员函数

2.1流插入流提取的重载

ostream& operator>>(ostream& out, const string& str);
istream& operator<<(istream& in, string& str);

2.2运算符重载–关系操作符

bool operator== (const string& lhs, const string& rhs);//无需使用以前strcmp
bool operator<= (const string& lhs, const string& rhs);

2.3getline

istream& getline (istream& is, string& str, char delim);//delim,结束符,默认'\n'。
istream& getline (istream& is, string& str);//常用,cin scanf有一个特点以空格或者换行进行间隔,仅仅读取空格前输入的内容不包括空格,剩下的内存放在缓冲区。

2.4swap

void swap (string& x, string& y);

2.5字符串连接

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

3.成员常量

​ 成员常量只有整型。

static const size_t npos = -1;//用作其他函数参数的缺省值,执行默认行为。

4.堆上字符数组的读取(查/遍历)

​ 对数组元素的读取。

//1.下标加[]
char& operator[] (size_t pos);
//2.迭代器
string::iterator it = s1.begin();
//3.范围for,遍历可迭代对象的元素
for(auto e: s1)
{
	cout << e;
}
//4.at和下标加[]功能一样,只是底层越界访问,at是抛异常,下标加[]是断言,断言仅在dubug下起作用。
char& at (size_t pos);
const char& at (size_t pos) const;

5.堆上字符数组的写入(增删改)

5.1增

void push_back (char c);//插入一个字符;
string& append (const char* str);//最常用插入字符串,和string的构造一样,除了无参重载了6个;
//appead相对于c语言的strcat,1.不需要每次追加都要找'\0';2.append会自动扩容;
string& operator+= (const char* str);//插入字符串对append和push_back的复用;
string& insert (size_t pos, const string& str);//任意位置插入
string& insert (size_t pos, size_t n, char c);

5.2改

//赋值
string& assign (const string& str);
string& assign (const char* s);
//赋值重载=
//replace
string& replace (size_t pos,  size_t len,  const string& str);
//void swap (string& str);

5.3删

string& erase (size_t pos = 0, size_t len = npos);

6.类型转换函数

string to_string (int val);
int stoi (const string&  str, size_t* idx = 0, int base = 10);//base 以10进制

7.堆上字符空间操作

​ 其实string一开始使用的是length,但是为了和stl统一加上了size。使用size和length的功能是一样的。

size_t size() const;
size_t max_size() const;//不同编译器下大小不同
size_t capacity() const;//不同编译器下大小不同Windows和Linux下不一样,扩容逻辑不一样。
void clear();//只清理size,capacity不变
bool empty() const;
//n>=size扩容+初始化,<size删数据不缩容。
//resize注重时间效率,缩容不支持释放一部分空间,而是创建新空间,拷贝需要的,释放原空间。
//对于n<size则是保留前n个字符,后一个设为'\0'。
void resize (size_t n);//开空间加初始化为0
void resize (size_t n, char c);//开空间加初始化字符c
//reserve注重空间利用率
void reserve (size_t n = 0);//知道要使用多大的空间,提前开好>=n的空间,减少扩容,扩容是有代价的。vs和g++实现上有差异。对于缩容,编译器不做优化不会缩,优化的话会根据不同情况决定是否缩容,或者改变容量到合适大小;vs要预留16个字节的空间,当clear空间后,因为最后一个字符为'\0',所以n大于15小于等于当前的capacity就不缩容了,小于等于15缩容,大于就扩容。而g++没有预留空间,该扩容就扩容,该缩容就缩容;若没有clear,vs以size和15中较小的一个为标准,小于等于缩容,大于就扩容。而g++则以size为标准决定扩容还是缩容。
//总之,vs以size和预留空间较小的一个为基准,g++以size为基准。

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

在这里插入图片描述

在这里插入图片描述

8.堆上字符操作接口

//返回字符串,c_str与data就和size和length一样,早年使用data后来使用c_str。
const char* c_str() const;//与c的接口函数配合使用
const char* data() const;
//子串匹配,左闭右开
size_t find (const string& str, size_t pos = 0) const;
size_t rfind (const string& str, size_t pos = npos) const;
//字符匹配
size_t find (char c, size_t pos = 0) const;
size_t rfind (char c, size_t pos = npos) const;
//返回子串
string substr (size_t pos = 0, size_t len = npos) const;
//字符串比较
int compare (const string& str) const;
//用str中的任意一个字符进行查找,若找到返回pos,否则npos;
size_t find_first_of (const string& str, size_t pos = 0) const;
//从后往前找
size_t find_last_of (const string& str, size_t pos = npos) const;
//找不匹配的
size_t find_first_not_of (const string& str, size_t pos = 0) const;

9.迭代器

​ string中有的实现就是指针,其他容器不是指针就是封装了指针。由于容器的数据私有,容器内部不相信任何人,但是还要进行数据访问和修改,所以所以容器对外提供了接口–迭代器,来进行访问。不同的数据底层内核不一样,但是算法对外提供统一的接口模板,通过配合迭代器实现函数模板实例化。迭代器的设计和系统调用接口类似,如:操作系统内核不相信用户,只是提供系统调用接口来为用户提供服务。

9.1迭代器的使用

​ 共四种,分为普通迭代器,const迭代器。begin指向首元素位置,end指向最后一个位置的下一位。rbegin指向最后一个位置,rend指向首元素位置的前一位。

//begin
iterator begin();//读写
const_iterator begin() const;//读
//rbegin
reverse_iterator rbegin();//读写
const_reverse_iterator rbegin() const;//读
int main()
{
    //begin end
	string s1("hello world");
	string::iterator it = s1.begin();
	for (; it != s1.end(); ++it)
	{
		cout << *it;
	}
	cout << endl;
    //rbegin rend
    string::iterator rit = s1.rbegin();
	for (; rit != s1.rend(); ++rit)
	{
		cout << *rit;
	}
	cout << endl;
	return 0;
}

9.2迭代器的++ –

​ 对普通迭代器++ --或者解引用迭代器++ --没有问题,因为底层是指针且此迭代器没有被const修饰。

9.3迭代器的优势

1.支持范围for,范围for的底层会替换为迭代器。范围for不支持反向遍历。栈不支持遍历,没有迭代器,不支持范围for;

2.任何容器都支持迭代器,并且用法是类似的,实现一种通用的遍历方式;

3.迭代器和容器进行配合,来遍历容器内核空间;

4.迭代器和算法的函数模板配合,生成不同的函数来修改容器数据,如:reverse(vit.begin(),vit.end()),注意链表不支持sort;

10.范围访问

​ 支持范围for能够在底层实现调用不同容器的迭代器,对上使用一样的接口。

template <class Container>
auto begin (Container& cont) -> decltype (cont.begin());
template <class Container>
auto begin (const Container& cont) -> decltype (cont.begin());
  • 40
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值