string类

1. 标准库中的string类

1.1 string类(了解)

string - C++ Reference

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

1.2 auto和范围for

1)auto关键字

  • 作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
  • 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
  • auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
  • auto不能直接用来声明数组

2)范围for

  • for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束
  • 范围for可以作用到数组和容器对象上进行遍历
  • 范围for的底层很简单,容器遍历实际就是替换为迭代器,从汇编层可以看到

1.3 string类的常用接口说明

1)string类对象的常见构造

string::string - C++ Reference

string::size - C++ Reference  函数名称    功能说明
string() (重点) 构造空的string类对象,即空字符串
string(const char* s) (重点) 用C-string来构造string类对象
string(size_t n, char c) 用n个连续字符c填充string类对象
string(const string&s) (重点) 拷贝构造函数
string s1;//构造空的string类对象s1
string s2("hello world");
string s3(s2);

2) string类对象的容量操作

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

注意:

  1. string类对象支持直接用cin和cout进行输入和输出
  2. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
  3. clear()只是将string中有效字符清空,不改变底层空间大小
  4. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:如果是大于原空间容量(n>=capacity),将元素有效个数增多,可能会改变底层容量的大小默认插入'\0'),如果是小于原空间容量(n<capacity),将元素有效个数删除,底层空间总大小不变
  5. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
    string s("hello, bit!!!");

	// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
	s.clear();

	// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
	s.resize(10, 'a');
	// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
	// 注意此时s中有效字符个数已经增加到15个
	s.resize(15);
	// 将s中有效字符个数缩小到5个
	s.resize(5);

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

函数名称 功能说明
operator[](重点) 返回pos位置的字符,const string类对象调用
begin+ end  begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器
rbegin + rend begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器(反向迭代器)
范围for C++11支持更简洁的范围for的新遍历方式
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
string s("hello Bit");
 
  // 1. 下标+[]
for (size_t i = 0; i < s.size(); ++i)
{
	cout<<s[i]<<endl;
}

  //2.迭代器
string::iterator it = s.begin();
//auto it = s.begin();
while (it != s.end())
{
	cout << *it << endl;
	++it;
}
// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,推导迭代器的类型
auto rit = s.rbegin();
//string::reverse_iterator rit = s.rbegin();
while (rit!=s.rend())
{
	cout << *rit << endl;
	++rit;
}

  // 3.范围for
//自动取容器数据赋值给左边对象
//自动++,自动判断结束
//底层是迭代器
for (auto ch : s)
{
	cout << ch << " ";
}
cout << endl;
//修改s中数据
for (auto& ch : s)
{
	ch++;
}
for (auto& ch : s)
{
	cout << ch << " ";
}
cout << endl;

4)string类对象的修改操作

函数名称

功能说明

push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串
operator+=(重点) 点) 在字符串后追加字符串str
c_str(重点) 返回C格式字符串
find+npos(重点) 从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置
rfind 同上
substr 在str中从pos位置开始,截取len个字符,然后将其返回
erase 在str中从pos位置开始,删除len个字符,然后将其返回

注意:

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

 5) string类非成员函数

函数 功能说明
getline(重点) 从is中提取字符并将其存储到str中,直到找到分隔符delim(或换行符'\n')
operator+ 返回新字符串,因为传值返回,导致深拷贝效率低(尽量少用)
operator>>(重点) 输入运算符重载
operator<<(重点) 输出运算符重载
relational  operators(重点)
 
大小比较

经典例题

仅仅反转字母

找字符串中第一个只出现一次的字符

字符串里面最后一个单词的长度

验证字符串是否回文

字符串相加

字符串部分区间反转

反转字符串中单词

字符串相乘

2.string类模拟实现

最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数

注:strlen函数在计算字符串长度时不包括\0,所以开空间的时候要多开一个

2.1 模拟实现

string实现

2.2 深拷贝

1)传统版写法的String类

	string::string(const char* str)
		:_size(strlen(str))
	{
		//_str = new char[_size];多分配一个空间给\0
		//_str = str;类型不同不能直接赋值
		_str = new char[_size + 1];
		strcpy(_str, str);
		_capacity = _size ;
	}
	string::string(const string& str)
	{
		_str = new char[str._size + 1];
		strcpy(_str, str._str);
		_size = str._size;
		_capacity = str._capacity;
	}
	string& string::operator=(const string& str)
	{
		//if (_str != str._str)
		if(this != &str)
		{
			delete[] _str;
			_str = new char[str._size + 1];
			strcpy(_str, str._str);
			_size = str._size;
			_capacity = str._capacity;
		} 
		return *this;
	}
	string::~string()
	{
		delete[] _str;
		_str = nullptr;//漏了这一步
		_capacity = 0;
		_size = 0;
	}

2)string中swap函数

3)现代版写法的String类

string::string(const char* str)
{
	if (str == nullptr)
	{
		assert(false);
		return;
	}
	_str = new char[strlen(str) + 1];
	strcpy(_str, str);
}
void string::swap(string& str)
{
	std::swap(this->_str, str._str);
	std::swap(this->_size, str._size);
	std::swap(this->_capacity, str._capacity);
}

string::string(const string& str)
{
	string tmp(str._str);
	string::swap(tmp);
}
string& string::operator=(const string& str)
{
	if (this != &str)
	{
		string tmp(str._str);
		string::swap(tmp);
	}
	return *this;
}
string::~string()
{
	delete[] _str;
	_str = nullptr;
	_capacity = 0;
	_size = 0;
}

2.3 写时拷贝

  是在浅拷贝的基础之上增加了引用计数的方式来实现的

  引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该 资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源, 如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有 其他对象在使用该资源

上一篇:【Linux】查找文件和查找文件匹配内容-一、查找文件


下一篇:13. Node.js会话控制