【C++】STL之string功能及模拟实现

这篇具有很好参考价值的文章主要介绍了【C++】STL之string功能及模拟实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

前沿

一、标准库中的string类

二、string类的常用接口说明

 1、string类对象的常见构造

 2、string类对象的容量操作

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

 4、string类对象的修改操作

 5、string类非成员函数

 6、vs下string结构的说明

三、string类的模拟实现

 1、构造函数

 2、析构函数

 3、拷贝构造函数

 4、赋值运算符重载

 5、比较运算符重载

 6、push_back ,append,+=

 7、容量(resize,reserve)(重点)

 8、插入和删除(insert,erase)

 9、迭代器


前沿

STL (standard template libaray-标准模板库)是 C++ 标准库的重要组成部分,由六大部分构成:仿函数,空间配置器,算法,容器,迭代器和配接器,其中包含了各式各样的容器,方便我们以后编写程序,比如今天要总结的 string 容器。

【C++】STL之string功能及模拟实现,c++,开发语言

一、标准库中的string类

  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 string。
  4. 不能操作多字节或者变长字符的序列。

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

二、string类的常用接口说明

 1、string类对象的常见构造

(constructor)函数名称 功能说明
string()(重点) 构造一个空字符串,长度为0个字符。
string (const char* s)(重点) 通过C字符串构造string类对象
string (const string& str)(重点) 拷贝构造
string (const string& str, size_t pos, size_t len = npos) 复制str从pos位置开始往后npos个字符
string (const char* s, size_t n) 复制C字符串前n个字符
string (size_t n, char c) string类对象包含n个字符c
template < class InputIterator > string (InputIterator first, InputIterator last) 通过一个字符串区间构造对象
void Teststring()
{
	string s1;              // 构造空的string类对象s1
	string s2("hello bit"); // 用C格式字符串构造string类对象s2
	string s3(s2);          // 拷贝构造s3
}

 2、string类对象的容量操作

函数名称 功能说明
size(重点) 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty(重点) 检测字符串释放为空串,是返回true,否则返回false
clear(重点) 清空有效字符
reserve(重点) 为字符串预留空间**(只改变capacity的值)
resize(重点) 将有效字符的个数该成n个,多出的空间用字符c填充(改变size和capacity的值)

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
  2. clear()只是将string中有效字符清空,不改变底层空间大小。
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大 小,如果是将元素个数减少,底层空间总大小不变。
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s1("hello world");
	s1.reserve(10);
	s1.resize(6, '0');
	return 0;
}

当运行到 reserve 时:

【C++】STL之string功能及模拟实现,c++,开发语言

我们发现当参数小于size时,改操作对参数几乎没有影响。

当运行resize时:

【C++】STL之string功能及模拟实现,c++,开发语言

我们发现这里参数 size 发生了改变,但是没有改变容量的大小。

注意:当 reserve 参数大于 size 时就会开出大于参数的空间,具体是多少由编译器决定,resize 的第一个参数大于 size 时也会扩大空间,同时还将 size 扩大到 n,在原始数据上将后面数据初始化为ch.

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

函数名称 功能说明
operator[] 返回pos位置的字符,const string类对象调用
begin+ end begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围for C++11支持更简洁的范围for的新遍历方式

 (1)可以通过[]+下标的方式来获取该字符串某个位置的字符。

void Test()
{
	string s1("hello World!");
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;
}
int main()
{
	Test();
	return 0;
}

运行结果:

【C++】STL之string功能及模拟实现,c++,开发语言(2)还可以用迭代器的方式访问。

void Test()
{
	string s("hello world!");
	//begin代表字符串第一个位置
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;
	// string::reverse_iterator rit = s.rbegin();
	// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
	auto rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;
}
int main()
{
	Test();
	return 0;
}

运行结果:

【C++】STL之string功能及模拟实现,c++,开发语言

(3) 还可以用范围 for 的方式访问。

void Test()
{
	string s("hello world!");
	for (auto ch : s)
		cout << ch;
	cout << endl;
}
int main()
{
	Test();
	return 0;
}

运行结果:

【C++】STL之string功能及模拟实现,c++,开发语言

 4、string类对象的修改操作

函数名称 功能说明
push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串
operator+= 在字符串后追加字符串str
c_str 返回C格式字符串
find + npos 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
insert 在源字符串某一位置插入字符串str
erase 在某一位置开始进行删除
swap 字符串交换
find_first_of 搜索字符串中与参数中指定的任何字符匹配的第一个字符
find_last_of 在字符串中搜索与参数中指定的任何字符匹配的最后一个字符
find_first_not_of 搜索字符串中与参数中指定的任何字符不匹配的第一个字符
find_last_not_of 在字符串中搜索不匹配其参数中指定的任何字符的最后一个字符
void Test()
{
	string str;
	str.push_back(' ');   // 在str后插入空格
	str.append("hello");  // 在str后追加一个字符"hello"
	str += 'w';           // 在str后追加一个字符'w'   
	str += "orld";          // 在str后追加一个字符串"orld"
	cout << str << endl;
	cout << str.c_str() << endl;   // 以C语言的方式打印字符串

	string s("world");
	s.insert(0, "hello");
	cout << s << endl;


	s.insert(2, "abcdef", 3);//插入该字符串的三个字符在二号位置
	cout << s << endl;

	string s("abcd");
	s.insert(s.find('b'), "abcdef");//找到字符b的位置然后插入"abcdef"
	cout << s << endl;

	s.replace(s.find('f'), 1, "20%");//找到f的位置用20%代替
	cout << s << endl;

	string s("hello world");
	size_t pos = s.find_first_of("eo", 0);//从位置0处找到字符e或字符o第一次出现的位置
	while (pos != string::npos)//如果没有找到匹配项,函数返回string::npos。
	{
		s[pos] = '*';
		pos = s.find_first_of("eo", pos + 1);//将整个字符串中的字符e或字符o换成*
	}

	//将不是e或o的字符都替换成*
	string s("hello world");
	size_t pos = s.find_first_not_of("eo", 0);
	while (pos != string::npos)
	{
		s[pos] = '*';
		pos = s.find_first_not_of("eo", pos + 1);
	}
	cout << s << endl;

	string s("hello world");
	size_t pos = s.find_last_of("eo", s.size() - 1);//从后开始找
	while (pos != string::npos)
	{
		s[pos] = '*';
		pos = s.find_last_of("eo", pos - 1);
	}
	cout << s << endl;
}

注意:

  1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

 5、string类非成员函数

函数 功能说明
operator+ 尽量少用,因为传值返回,导致深拷贝效率低
operator>> 输入运算符重载
operator<< 输出运算符重载
getline(重要) 获取一行字符串
relational operators 大小比较

   这里面的 getline 函数是获取一行字符串的,和单纯的流插入是不一样的,因为字符串中很可能会包含空格的,而 cin 会认为这是结束的标志,进而不再读取字符,因此才有了这个 getline 函数。

具体用法如下:

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	getline(cin, s);
	cout << s << endl;
	return 0;
}

运行结果: 

【C++】STL之string功能及模拟实现,c++,开发语言

 6、vs下string结构的说明

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

   关于string对象应该占多少字节这个问题,我们先大胆猜一下为12个字节(一个指向字符串的指针,一个是整形的 size,一个整形的 capacity),但明显不是这样的,string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义 string 中字符串的存储空间:
  (1)当字符串长度小于16时,使用内部固定的字符数组来存放;
  (2)当字符串长度大于等于16时,从堆上开辟空间。

union _Bxty
{ 
    // storage for small buffer or pointer to larger one
	value_type _Buf[_BUF_SIZE];
	pointer _Ptr;
    // to permit aliasing
	char _Alias[_BUF_SIZE]; 
}_Bx;

   因为大多数情况下字符串的长度都小于16,那 string 对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高;还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量;还有一个指针做一些其他事情。因此总共占16+4+4+4=28个字节

三、string类的模拟实现

 1、构造函数


class string
{
public:
	string(const char* str = "")
		:_size(strlen(str))
	{
		_capcity = _size == 0 ? 4 : _size;
		_str = new char[_capcity + 1];
		strcpy(_str, str);
	}
private:
	char* _str;
	size_t _size;
	size_t _capcity;
};

 2、析构函数

~String()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

 3、 拷贝构造函数

//传统版写法
string(const string& s)
	:_size(s._size)
	, _capcity(s._capcity)
{
	_str = new char[_capcity + 1];
	strcmp(_str, s._str);
}

//现代版写法
String(const string& s)
	:_str(nullptr)
{
	string str(s._str);
	swap(_str, str);
}

注意:第二种现代版写法, 一定要先构造一份对象然后再交换,要不然直接交换的话就把传过来的对象变空了。

 4、 赋值运算符重载

//传统版写法
string& operator=(const string& s)
{
	_size = s._size;
	_capcity = s._capcity;
    // 防止new失败,我们先创建一个临时变量开辟空间
	// 拷贝完后,在给_str
	char* tmp = new char[_capcity + 1];
	strcpy(tmp, s._str);
	delete[] _str;
	_str = tmp;
	return *this;
}

//现代版写法
String& operator=(String s)
{
	swap(_str, s._str);
	return *this;
}

 5、比较运算符重载

bool operator==(const string& s)
{
	return strcmp(_str, s._str) == 0;
}

bool operator>(const string& s)
{
	return strcmp(_str, s._str) > 0;
}

bool operator>=(const string& s)
{
	return *this > s || *this == s;
}

bool operator<(const string& s)
{
	return !(*this >= s);
}

bool operator<=(const string& s)
{
	return *this < s && *this == s;
}

bool operator!=(const string& s)
{
	return !(*this == s);
}

比较运算符的重载可以先写一个大于和等于的函数,其他的比较可以直接用这两个进行复用。 

 6、push_back ,append,+=

//增加一个字符
void push_back(char c)
{
	if (_size == _capacity)
	{
		reserve(_capacity * 2);
	}
	_str[_size++] = c;
	_str[_size] = '\0';
}
//增加一个字符串
void append(const char* str)
{
	int len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str + _size, str);
	_size += len;
}

string& operator+=(char c)
{
	push_back(c);
	return *this;
}

string& operator+=(const char* str)
{
	append(str);
	return *this;
}

 7、容量(resize,reserve)(重点)

void resize(size_t n, char c = '\0')
{
	if (n > _size)
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		memset(_str + _size, c, n - _size);
	}
	_size = n;
	_str[n] = '\0';
}
void reserve(size_t n)
{
	if (n > _capcity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capcity = n;
	}
}

 8、插入和删除(insert,erase)

//插入字符
string& insert(size_t pos, char ch)
{
	assert(pos <= _size);
	//插入前检查是否需要扩容
	if (_size + 1 > _capcity)
		reserve(_size * 2);
	//挪动数据
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	_size++;
	return *this;
}
//插入字符串
string& insert(size_t pos, const char* s)
{
	assert(pos <= _size);
	size_t lens = strlen(s);
	if (_size + lens > _capcity)
		reserve(_size + lens);
	//挪动数据
	size_t end = _size + lens;
	while (end > pos + lens - 1)
	{
		_str[end] = _str[end - lens];
		--end;
	}
	strncpy(_str, s, lens);
	_size += lens;
	return *this;
}
string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
	//挪动数据
	if (len == npos || _size - pos <= len)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[begin - len] = _str[begin];
			++begin;
		}
		_size -= len;
	}
	return *this;
}

 9、迭代器

typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}
const iterator begin()const
{
	return _str;
}
const iterator end()const
{
	return _str + _size;
}

本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。

【C++】STL之string功能及模拟实现,c++,开发语言

老铁们,记着点赞加关注!!! 

【C++】STL之string功能及模拟实现,c++,开发语言文章来源地址https://www.toymoban.com/news/detail-556945.html

到了这里,关于【C++】STL之string功能及模拟实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ stl容器string的底层模拟实现

    目录 前言: 1.成员变量 2.构造函数与拷贝构造函数 3.析构函数 4.赋值重载 5.[]重载 6.比较关系重载 7.reserve 8.resize 9.push_back,append和重载+= 10.insert 11.erase 12.find 14.迭代器 15.流插入,流提取重载 16.swap 17.c_str 18.完整代码+测试 总结: 1.成员变量 首先注意的就是_str,不能是const类型

    2024年04月23日
    浏览(29)
  • C++ STL学习之【string的模拟实现】

    ✨个人主页: Yohifo 🎉所属专栏: C++修行之路 🎊每篇一句: 图片来源 The key is to keep company only with people who uplift you, whose presence calls forth your best. 关键是只与那些提升你的人在一起,他们的存在唤起了你最好的一面。 string 本质上就是一个专注于存储字符的顺序表,使用起来

    2023年04月09日
    浏览(32)
  • 【C++精华铺】10.STL string模拟实现

            STL(标准模板库)是一个C++标准库,其中包括一些通用的算法、容器和函数对象。STL的容器是C++ STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串类型。         STLstring是一个类,它封装了字符串的操作

    2024年02月09日
    浏览(35)
  • 【C++初阶】STL详解(二)string类的模拟实现

    本专栏内容为:C++学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C++。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C++ 🚚代码仓库:小小unicorn的代码仓库🚚 🌹🌹🌹关注我带你学习编程知识 注:为了防止与标准库当中的string类产生命名冲

    2024年02月05日
    浏览(39)
  • 【C++练级之路】【Lv.6】【STL】string类的模拟实现

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C语言,共同探讨和进步哦! 学习专栏 : 《进击的C++》 关于 STL容器 的学习,我会采用 模拟实现 的方式,以此来更加清楚地了解其 底层原理和整体架构 。而string类更是有100多个接口函数,所以模拟实现的时候只会调重点和

    2024年01月18日
    浏览(37)
  • 【C++】:STL中的string类的增删查改的底层模拟实现

    本篇博客仅仅实现存储字符(串)的string 同时由于C++string库设计的不合理,我仅实现一些最常见的增删查改接口 接下来给出的接口都是基于以下框架: C++string标准库中,无参构造并不是空间为0,直接置为空指针 而是开一个字节,并存放‘\\0’ C++中支持无参构造一个对象后,直

    2024年02月05日
    浏览(40)
  • 【C++】STL——string的模拟实现、常用构造函数、迭代器、运算符重载、扩容函数、增删查改

    string使用文章   这里我们 实现常用的第四个string(const char* s)和析构函数     拷贝构造函数实现:   在堆上使用new为当前对象的成员变量_str分配内存空间,大小为s._capacity + 1字节,即字符串的容量加上一个结束符\\0的空间。   我们使用深拷贝而不是浅拷贝,

    2024年02月15日
    浏览(34)
  • 【C++】STL之vector功能及模拟实现

    目录 前沿 一、vector的使用  1、vector 构造函数的声明  2、vector 迭代器的使用  3、vector 空间增长问题  4、vector 的增删查改 二、vector的模拟实现  1、vector 的成员变量  2、迭代器  3、容量相关(resize, reserve)  4、数据访问相关  5、插入删除   5.1 任意位置插入   5.2 任意位置

    2024年02月16日
    浏览(28)
  • 【C++】STL优先级队列(priority_queue)功能介绍以及模拟实现

    点进来的小伙伴不知道学过数据结构里的堆没有,如果学过的话,那就好说了,优先级队列就是堆,如果没学过,没关系,可以参考一下我之前写的一篇关于堆的博客,可以点进去看看:【数据结构】堆(包含堆排序和TOPK问题) 那么了解过堆了的话,我就不讲那么细致了,

    2024年02月16日
    浏览(37)
  • 带你深入理解“栈”(c语言 c++和stl Stack三个版本的模拟实现)

    目录 一.栈的概念及结构 二.栈的实现(c语言版) 2.1静态增长的栈 2.2动态增长的栈 2.3动态栈的模拟实现    1.栈的初始化   2.入栈  3.出栈 4.获取栈顶元素 5.获取栈中有效数据个数 6.检查栈是否为空 7.栈的销毁 三.C++ 版本模拟实现栈  1.C++版本的源代码 四.c语言版本的源代码

    2024年02月08日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包