【C++】常用String类型的熟悉

本文详细介绍了C++中String类型的容量管理,包括capacity、resize和reserve等函数的使用,以及string对象的修改方法如operator+=、append、push_back等。同时,文章讨论了成员函数、遍历string对象的不同方式和查找功能,如find、rfind等。
摘要由CSDN通过智能技术生成

本篇文章主讲String类型常用函数的使用

cplusplus中string有关的网站,非常好用

一、容量(capacity)

熟悉:size length resize capacity reserve clear empty
重点: resize和reserve

在这里插入图片描述

而在此之前要先理解string类型结构的底层

在32位下string类型下对象z的大小
在32位下string类型下对象z的大小
理解:
在这里插入图片描述
_capacity _size _prt 的成员变量很好理解分别对应 容量大小 字符个数 一个指针指向存储的字符
而char _BUF[16] 则是vs编译器设计出来的目的是当创建的string类型对象的字符串长度小于16便不需要再去申请堆上的空间,直接使用内部固定的字符串数组buf来存好,效率提高即空间换时间的方式

当然这是VS编译器下的方式并不是所有的编译器都是采取如此的方式,可以理解为string类型只是规定了使用规则,具体的底层每个编译器可以选择适用的方法(如LINUX下的gcc编译器在64位下只有一个指针即大小为8)

熟悉size length capacity的使用
在这里插入图片描述
1 size和length 输出相同(尽量使用size) 因此都 既可以表示 字符串的长度、字符串有效字符的个数、下次填写字符串的下标
2 capacity 即为容量大小 且需要额外留出\0的空间 因此_BUF[16]下capacity并不是16而是15

熟悉 resize reserve使用(重点)
在这里插入图片描述
功能:reserve和resize都是一次性开辟你所需的空间(减少开辟空间的次数提高效率)
在这里插入图片描述
两个的不同处
1 reserve不会对开辟的空间初始化,而resize则是会初始化默认为\0 且可以指定初始化的内容
2 reserve不会改变size的值,而resize则是会根据开辟的空间使size等于开辟空间的值(这也对应了初始化的结果,方便之后若需要在字符串后添加数据)
在这里插入图片描述
在这里插入图片描述
因此结合上述reserve和resize的特性模拟实现的代码即为:

//1 缩容(此缩容只改变size大小并不会改变capacity大小)
//2 扩容(既更改size大小又更改capacity大小)
void resize(size_t n, char ch = '\0')
{
	if (n < _size)
	{
		// 删除数据--保留前n个
		_size = n;
		_str[_size] = '\0';
	}
	else if (n > _size)
	{
		if (n > _capacity)
		{
			reserve(n);
		}

		size_t i = _size;
		while (i < n)
		{
			_str[i] = ch;
			++i;
		}

		_size = n;
		_str[_size] = '\0';
	}
}

//reserve不会缩容也不会更改字符的值
void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;

		_capacity = n;
	}
}

理解:编译器轻易不会为了节省空间而真的去缩容即改变capacity的值,因为并没有函数能实现部分释放空间,只能先释放全部再申请空间效率低
在这里插入图片描述

熟悉:clear empty
clear清空字符串,empty判断字符串是否为空(空true非空false)
在这里插入图片描述

二、修改(Modifiers)

熟悉:operator+= 、append、push_back、insert、erase、swap
重点: operator+=

在这里插入图片描述
熟悉:append、push_back、operator+=
在这里插入图片描述
append和push_back功能:尾插数据,一个是字符串一个是字符

而append功能设计的很冗余主要就是插入字符串
不太用到上述为不太用到
基本用法
在这里插入图片描述
但由于运算符重载operator+=的存在,在日常使用时候append和push_back都不会去使用,但是要去知道实际上operator+=的底层就是调用了append和push_back。
在这里插入图片描述
append、push_back、operator+= 模拟实现

void push_back(char ch)
{
	if (_size + 1 > _capacity)
	{
		reserve(_capacity * 2);
	}

	_str[_size] = ch;
	++_size;

	_str[_size] = '\0';
	//insert(_size, ch);
}

void append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

	strcpy(_str + _size, str);
	//strcat(_str, str);
	_size += len;
	//insert(_size, str);
}

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

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

熟悉: insert、erase
功能:pos位置的插入和删除(效率较低,最差的头插头删效率为O(n))
在这里插入图片描述
insert的设计也很多,这里只熟悉插入字符串
在这里插入图片描述
insert、erase的模拟实现

string& insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);

	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}
	// 挪动数据
	size_t end = _size + len;
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		--end;
	}
	// 拷贝插入
	strncpy(_str + pos, str, len);
	_size += len;

	return *this;

}

string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);

	if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}

	return *this;
}

熟悉:swap
功能:即交换两个string类型对象
在这里插入图片描述
在这里插入图片描述
通过下面swap的模拟实现代码理解string类型swap效率高

void swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_capacity, s._capacity);
	std::swap(_size, s._size);
}

理解std命名空间的swap和string的swap的区别
问:std已经有swap为什么string类型还设计了自己的swap
在这里插入图片描述
在这里插入图片描述

理解:
若string类型没有设计swap那么直接调用std的swap如上述 1先拷贝构造一个对象 2再会调用两次赋值运算符的重载函数;
而string类型设计的swap都只是对内置类型的成员变量简单进行交换(_size,_capacity,对于_str理解为只是改变了指针的执行)因此效率更高。

三、成员函数(Member functions)

在这里插入图片描述
构造函数constructor
在这里插入图片描述
string构造函数设计冗余
主要使用下面
主要使用构造函数的模拟实现

string(const char* str = "")
	:_size(strlen(str))
{
	_capacity = _size == 0 ? 3 : _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}
// s3(s2)
string(const string& s)
	:_size(s._size)
	, _capacity(s._capacity)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
}

理解我这里并没有设计char BUF[16]所以成员变量如下

private:
	char* _str;
	size_t _capacity;
	size_t _size;

赋值运算符重载
在这里插入图片描述
使用
在这里插入图片描述
string& operator= (const char* s)的模拟实现

string& operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		strcpy(tmp, s._str);
		delete[] _str;
		_str = tmp;

		_size = s._size;
		_capacity = s._capacity;
	}

	return *this;
}

四、遍历string对象

第一种:调用迭代器
我现在对string类型的迭代器简单的就理解为类似指针的模型,但是vs的底层确实不同,只是当作理解,因此对于迭代器的模拟实现理解即可
在这里插入图片描述
熟悉:begin end rbegin rend
begin end 搭配使用
在这里插入图片描述

在这里插入图片描述
对于const_iterator begin() constconst_iterator end() const这两个接口用于const string& 类型的对象调用迭代器(理解const类型的对象不能调用非const的接口,原理权限的放大
在这里插入图片描述
理解begin和end的模拟实现([begin,end)左闭右开)

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;
}

rbegin rend搭配使用
在这里插入图片描述
rbegin和rend理解为反向迭代器,遍历字符串是从后往前遍历(用法与begin和end一样)

在这里插入图片描述

在这里插入图片描述
第二种语法糖
在这里插入图片描述
实际本质这个语法糖的底层就是调用迭代器

第三种 [ ]
在这里插入图片描述

在这里插入图片描述
[ ]的底层是设计了运算符的重载
[ ]运算符重载的模拟实现

const char& operator[](size_t pos) const
{
	assert(pos < _size);
	return _str[pos];
}

char& operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}

五、 查找

熟悉:find、rfind、find_first_of、find_last_of
find、rfind
在这里插入图片描述
find返回值为下标 既可以查找对应的字符串的下标也可以查找单个字符的下标,其中pos从下标pos出开始往后查找,若没查找到返回npos即无符号位整数的-1(4294967295)
在这里插入图片描述
理解了find那么rfind就一样,只不过rfind是从后往前找,find是从前往后找。

find_first_of、find_last_of
是查找字符串中所有的字符。first是从前往后找,last则是从后往前找。
理解下面的例子就够了
在这里插入图片描述
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值