【C++】string

一、STL简介 

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

在我们的学习C++的过程中,对STL的学习是必不可少的,只有熟悉了STL,才算是真正学过C++。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。STL的发展过程中,有过许多不同的版本,诸如HP 版本、P. J. 版本、RW版本、SGI版本,其中,SGI版本被Linux采纳,该版本的源码可移植性强、可读性好,在我们的学习过程中,可以结合该版本STL的源码进行学习,以加深我们对STL的理解。

STL有六大组件:仿函数、算法、迭代器、空间适配器、八大容器和配接器,接下来会对八大容器中的string作较为详细的介绍。

一句话总结:STL很重要!!!

学习STL的三大境界:能用,明理,能扩展。

二、string的介绍

我们在学C++的时候可能疑惑,我们在C语言阶段已经学过了字符串的表示类型,可以通过字符数组或者字符指针来定义字符串,为什么C++要添加string类呢,这么做有什么好处?

很明显,C语言中字符串的两种定义方式,都不是直接定义字符串类型,而是通过字符数组将字符串中的每个字符(包括'\0')存储在内,用这种方式定义字符串不方便调节数组的大小;或者是通过字符指针来定义字符串,但是这种方式定义的字符串具有常性,字符串本身的内容不可修改的;如果定义动态数组(用malloc()在堆区上申请空间,可以对字符串进行增删查改,但是需要我们自己去写函数,就算是调用C语言库函数,也需要用户自己控制,比较麻烦。所以,不论是哪种定义方式,都不够直接,而且使用起来不方便。

STL的string类很好的体现了类封装的好处,我们在定义字符串,操作字符串时,实际上是调用的string类的接口函数,可以简化代码,增强代码的复用性。

string类的特性:

1. string是表示字符串的字符串类。
2. 该类的接口与其他容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
4. 不能操作多字节或者变长字符的序列。

注意:在使用string类时,必须包含头文件<iostream>、展开std命名空间,即

#include <iostream>
using namespace std;

三、string的定义

string类是basic_string模板的一个实例化。

basic_string模板可以被实例化成string、wsting、u16string、u32string。

1.        typedef basic_string<char> string;

char的大小是1个字节。

string 一个字符占一个字节  而且string有一个字符表示结尾 这个字符不会被length计算 所以length+1就是所占字节 sizeof出来的数字是默认开的空间 也就是只要定义一个string 系统就会为它分配32字节。

2.        typedef basic_string<wchar_t> wstring;   

wchar_t 在windows上大小是2个字节, 在linux下是4个字节,一个字符占了4个byte,与Java的String不一样。

3.        typedef basic_string<char16_t> u16string; 

sizeof(char16_t)=2,无论什么平台, char16_t占两个字节。

4.        typedef basic_string<char32_t> u32string; 

sizeof(char32_t)=4,无论什么平台, char32_t占4个字节。

在sizeof(wchar_t)=4的系统,wstring与u32string是一样的,在sizeof(wchar_t)=2的系统,wstring与u16string是一样的。

class string
{
public:
   //string类的成员函数

private:
    char* _str;
    size_t _capacity;
    size_t _size;
    static const size_t npos = -1;
}

四、string的常用接口

1.构造函数和赋值重载

1.1构造函数

string类重载了多个构造函数,结合上图进行说明:

(1)无参构造函数:构造一个空字符串对象。

(2)拷贝构造函数:构造一个内容同str的字符串对象。

(3)拷贝构造函数:构造一个内容同str子串(从pos下标对应的字符开始的长度为len的子串,如果pos+len > str.length(),则将从pos位置开始的剩余部分全部拷贝)的字符串对象。

(4)构造一个内容同s指向的字符串的字符串对象。

(5)构造一个内容同s指向的字符串的前n个字符相同的对象(如果n>strlen(s),则构造一个内容同s指向的字符串相同的对象)。

(6)构造一个内容为n个c字符组成的字符串对象。

(7)以相同的顺序构造一个内容同[first,last)的字符序列的字符串对象。

代码测试各个构造函数的使用:

void test1()
{
	string s0("Initial string");

	// constructors used in the same order as described above:
	string s1;              //(1)
	string s2(s0);          //(2)
	string s3(s0, 8, 3);    //(3)
	string s4("A character sequence");   //(4)
	string s5("Another character sequence", 12);  //(5)
	string s6a(10, 'x');   //(6)
	string s6b(10, 42);      // 42 is the ASCII code for '*'    (6)
	string s7(s0.begin(), s0.begin() + 7);   //(7)

	cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;
	cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6a: " << s6a;
	cout << "\ns6b: " << s6b << "\ns7: " << s7 << '\n';
}

运行结果:

1.2赋值重载函数

赋值重载和拷贝构造大同小异:

同:用已有的string对象或字符串来构造其他string对象

异:拷贝构造是构造新对象,即定义时构造(初始化);赋值重载是赋值给已存在的对象,即赋值。

注意二者的区别。

代码测试各个赋值重载函数的使用:

void test2()
{
	string str1, str2, str3;
	str1 = "Test string: ";   // c-string
	str2 = 'x';               // single character
	str3 = str1 + str2;       // string

	cout << str3 << '\n';
}

运行结果:

2.析构函数

析构函数是用来释放类对象占用资源的函数,如果用户未定义,则编译器会自动生成默认析构函数。但是默认析构函数无法释放堆区资源,只能用户自己定义。

如果不存在占用堆区资源的类对象,可以不用自定义析构函数。

另外,析构函数无需显式调用,编译器在类生命周期结束时自动调用

3.迭代器相关的函数

3.1        begin()

返回指向string类对象首元素的迭代器,string类的迭代器可以理解为指针。

3.2        end()

返回指向string类对象最后一个元素的后一个位置的迭代器。

3.3        rbegin()

迭代器包括正向迭代器和反向迭代器,即iterator和reverse_iterator。

正向迭代器和反向迭代器的主要显式区别就是进行++和--运算时,移动方向相反。

rbegin()返回指向string类最后一个元素的反向迭代器。

3.4        rend()

rend()返回执行string类第一个元素的前一个位置的反向迭代器。

另外,cbegin()和cend()的返回类型是const迭代器;crbegin()、cend()的返回类型是const反向迭代器。

代码测试各个跟迭代器相关函数的使用:

//3.测试迭代器相关的函数
void test3()
{
	string str("Test string");
	//1.begin()  end()
	for (string::iterator it = str.begin(); it != str.end(); ++it)
		std::cout << *it;
	cout << '\n';

	//2.rbegin()  rend()
	for (string::reverse_iterator rit = str.rbegin(); rit != str.rend(); ++rit)
		std::cout << *rit;
	cout << '\n';
}

运行结果:

4.容量有关的函数

4.1        size()

返回string类对象的字符串长度。

4.2        length()

函数功能同size()。不过size()函数是求STL容器大小(区别于STL容器容量)的通用函数,length()函数接口只有string容器有。

4.3        resize()

重置string类的_size,即充值string类字符串的大小。

如果n > _size,则

void resize(size_t n);   将string类字符串大小置为n,增长的部分用' '(即空格)补充;

void resize(size_t n, char c);    将string类字符串大小置为n,增长的部分用字符变量c的值补充。

如果n < _size,则舍弃string类字符串长度大于n的部分。

4.4        capacity()

返回string类对象的容量。

4.5        reserve()

当n > capacity时,扩容,函数调用结束后capacity == n;当n < capacity时,本该缩容,但是缩容代价大,编译器不会这么做,所以capacity的值不变。

4.6        clear()

清空string类对象字符串,size == 0。

4.7        empty()

判空函数,判断string类对象是否为空。

为空返回true;不为空返回false。

代码测试各个和size以及capapcity相关函数的使用:

//4.测试容量相关的函数
void test4()
{
	string str("I like to code in C");
	//1.size()   length()
	cout << "The size of str is " << str.size() << " bytes.\n";
	cout << "The size of str is " << str.length() << " bytes.\n";

	cout << str << '\n';

	unsigned sz = str.size();

	//2.resize()
	str.resize(sz + 2, '+');
	cout << str << '\n';

	str.resize(14);
	cout << str << '\n';


	//3.capacity()
	cout << "capacity:" << str.capacity() << endl;

	//4.reserve()
	str.reserve(100);
	cout << "capacity:" << str.capacity() << endl;

	str.reserve(10);
	cout << "capacity:" << str.capacity() << endl;

	//5.clear()   empty()
	cout << "调用clear()函数前:";
	if (str.empty())
		cout << "str为空" << endl;
	else
		cout << "str不为空" << endl;
	
	str.clear();

	cout << "调用clear()函数后:";
	if (str.empty())
		cout << "str为空" << endl;
	else
		cout << "str不为空" << endl;

}

 运行结果:

5.元素访问相关的函数

5.1        operator[]

下标访问字符串元素。

对越界访问会做检查,assert暴力检查。

5.2        at()

下标访问字符串元素。

对越界访问会做检查,抛错。

 代码测试operator[]()和at()函数的使用:

void test5()
{
	string str("Test string");
	for (int i = 0; i < str.length(); ++i)
	{
		cout << str[i];
	}
	cout << endl;

	for (int i = 0; i < str.length(); ++i)
	{
		cout << str.at(i);
	}
	cout << endl;

	越界检查
	//for (int i = 0; i <= str.length()+1; i++)
	//{
	//	cout << str[i];
	//}
	//cout << endl;

	//for (int i = 0; i <= str.length(); ++i)
	//{
	//	cout << str.at(i);
	//}
	//cout << endl;
}

运行结果:

6.修改容器相关的函数

6.1        operator+=

添加string字符串的内容。

1)string& operator+= (const string& str);    添加string类对象str的字符串到*this

2)string& operator+= (const char* s);         添加字符串s到*this

3)string& operator+=(char c);                     添加字符c到*this

6.2        append()

向string对象追加相应的内容。

6.3        push_back()

尾插,将字符变量c追加到string对象。

6.4        insert()

在下标pos或迭代器p表示的位置插入相应的内容。

6.5        erase()

擦除string类对象的部分内容。

6.6        replace()

代替string类对象的部分内容。

6.7        swap()

交换两个string类对象的内容。

6.8        pop_back()

尾删。

7.字符串操作相关的函数

7.1        c_str()

返回string类对象的成员c_str的首元素的地址。

7.2        find()

string类对象的内容查找。

7.3        rfind()

功能同find(),从后往前找。

7.4        substr()

返回一个以sring类对象内容的一个子串为内容构造的string类对象,即返回一个新string类对象,其内容是原string对象内容的子串。

8.特殊的成员变量

静态成员变量npos:

npos的值通常被定义为-1,表示没有匹配的下标,用作部分函数的返回值。 

四、总结

string是STL的八大容器之一,内部数据结构是字符串,支持元素的随机访问(通过下标访问)。相比用C语言的字符串额日眼,这里很好的体现了类封装的优势,可以在类内定义函数,对外提供函数接口,方便用户调用常用功能函数来进行平时的代码设计和实现,提高效率。

string容器的模拟实现可以加深我们对其的理解,大家感兴趣的可以自己尝试,我就不再多做说明。如果本文有陈述不正确的地方,欢迎各位斧正!

另外补充说明:本文中关于string接口函数原型的截图以及代码测试来源为官网:Reference - C++ Reference,以后STL容器相关的博客也会参照该网站。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值