C++学习记录——십 STL初级认识、标准库string类


关于string类的内容,可以在cplusplus.com查看到。

1、什么是STL

STL是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

2、STL简介

STL最初是在惠普实验室诞生的,并向外开源。

原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。

P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,
符号命名比较怪异。

RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。

SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,
可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。一般学习STL要阅读部分源代码,
主要参考的就是这个版本。

STL分为仿函数,算法,容器,迭代器,空间配置器,配接器

3、什么是string类

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

string类则是C++定义的一个类。根据官方文档记录:

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意 这个类独立于所使用的编码来处理字节,如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

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

简单看一个代码

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

int main()
{
	string s1;
	string s2("hello world");
	for (size_t i = 0; i < s2.size(); ++i)
	{
		s2[i]++;
	}
	cout << s2 << endl;
	return 0;
}

string需要头文件string,s2[i]++会让对应的字符往后一位,比如h变为i,e变为f。

在这里插入图片描述

4、string类的常用接口说明

1、常见构造函数

string(const char* s) 和上面的代码一样,可以string s2(“hello world”); 也可以string s2 = “hello world”。

string(const string& str, size_t pos, size_t len = npos)。这个构造函数的意思就是取字符串的一部分,从pos位置开始往后len个字符。

	string s3 = "hello world";
	string s4(s3, 6, 3);

结果是wor。下标6的位置是w,包括w打印3个位置。如果后面的长度过大,那么就会取到字符串结束,而不是按照实际的len去越界。如果不给len,那么就会按照它的缺省值来打印,len的缺省值是npos,定义中给的值是无符号整数-1,所以就是整数的最大值,那么就会打印到字符串结束。

string(const char* s, size_t n),用字符串前n个进行构造。

	string s5("hello world", 6);
	cout << s5 << endl;

打印hello和空格。

string(size_t n, char c),用n个字符c来构造。

	string s6(10, '*');
	cout << s6 << endl;

cplusplus.com可以查到相关文档。

2、容量操作

int main()
{
	string s1("hello world");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	return 0;
}

size和length两个功能一样,返回有效字符串长度。max_size()则是察看能达到的最大值,42亿9千万多,没有多大意义。

size结果是12,而capacity结果是15,不包含’\0’,返回为该string类开辟的空间总大小。

int main()
{
	string s1("hello");
	s1.push_back('?');
	s1.append("asda");
	cout << s1 << endl;
	return 0;
}

push_back只能加字符,而append可以加字符串。append还有别的用法,在cplusplus.com查看。统合这两个功能,+=就可以全实现。

	s1 += '!';
	s1 += "asdghjfk";
	cout << s1 << endl;

在这里插入图片描述

capacity函数,会在原有空间不足时进行自动扩容,这个根据不同的编译器有不同的结果,vs是1.5倍扩容,Linux不一样。

int main()
{
	string s;
	cout << sizeof(s) << endl;
	size_t sz = s.capacity();
	cout << "make s grow:\n";
	cout << "capacity changed: " << sz << '\n';
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}
}

也有方法阻止自动扩容,reserve函数,预留空间。

	string s;
	s.reserve(100);

不一定会多开100空间,但是一定开得比100多。

resize可以开空间并初始化。

int main()
{
	string s1("hello world");
	s1.reserve(100);
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

	string s2("hello world");
	s2.resize(100);
	cout << s2.capacity() << endl;
	cout << s2.size() << endl;
	return 0;
}

在这里插入图片描述

resize括号里第二个参数可以加上字符,用来初始化空白的空间为指定的字符。resize也可以删除数据,括号的数字n比size小,就会保留前n个,但不会缩小capacity,缩容对于系统来说难度比较大,系统原生一般不支持。

3、迭代器

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

分析一下这段代码。我们可以暂且理解为s1指向hello world字符串,s1.begin就是首元素地址,给了it,end则是’\0’的位置,it指向了第一个元素,然后边打印边++。

我们也可以用范围for,不过底层原理都一样,都是迭代器。

	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;

iterator是string类里的typedef的一个函数。用迭代器需要用它。迭代器可以反向使用。

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

反向迭代器需要用到reverse_iterator。rbegin指向hello world的d而不是’\0’,rend则指向h前面的位置,这里正是因为是反向,所以++就等于–。

如果是加上了const

void func(const string& s)
{
	string::const_iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

如果是const的变量,那么就得用const_iterator,刚才的反向迭代器也一样,需要用专用的,并且rbegin这些也得对应着用。const修饰,就只能遍历和读,而不能写;反向迭代器加上const,类的函数就应当是string::const_reverse_iterator。

4、其他的标准库的string类

	string s1("hello world");
	s1[100];
	s1.at(100);
	cout << s1.at(100) << endl;

越界了会直接报错,如果是at,则会抛异常,虽然也报错,但可以捕获异常。不过有的编译器会执行,但到了at这一行就会停几秒然后退出程序,不显示错误。

insert整体是插入的意思,有很多函数,可以插入string类对象,可以插入字符串,可以选择位置来控制插入。

	string s1("hello world");
	//s1[100];
	//s1.at(100);
	s1.insert(0, "hhhh");
	cout << s1 << endl;
	s1.insert(4, 1, ' ');//在4这个位置,插入1个' '
	cout << s1 << endl;
	return 0;

在这里插入图片描述

或者s1.insert(4, " ")这样,但不可以(4, 3, " ")。括号里也可以加上迭代器。

	s1.insert(s1.begin() + 4, ' ');
	cout << s1 << endl;
    string s2("hello world");
	s2.erase(5, 1);
	//s2.erase(s2.begin() + 5);
	cout << s2 << endl;

erase可以用来删除

在这里插入图片描述

如果括号里给的数字过大时,那就会有多少删多少。

insert和erase可能存在挪动数据,效率低下,所以不推荐常用。

之前有在字符串空格处把空格换成别的字符的题,现在用C++写

	string s1("hello, the world");
	size_t pos = s1.find(' ');
	if (pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
	}
	cout << s1 << endl;
	return 0;

这里会把第一个空格改成%20,加个循环就可以把所有空格都换上。

	while(pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ');
	}

不过这个程序还有改进之处。pos = s1.find(’ ', pos + 3),可以从%20的0处往后开始找下一个空格,增加一点效率。replace存在扩容,会把1个字符扩成3个字符。所以我们可以提前开好,避免replace开空间。

	string s1("hello the world");
	size_t num = 0;
	for (auto ch : s1)
	{
		if (ch == ' ')
			++num;
	}
	s1.reserve(s1.size() + 2 * num);
	size_t pos = s1.find(' ');
	while(pos != string::npos)
	{
		s1.replace(pos, 1, "%20");
		pos = s1.find(' ', pos + 3);
	}
	cout << s1 << endl;

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值