C++string类重要成员函数介绍及自我模拟编写string类

 👊👊你间歇性的努力和蒙混过日子,都是对之前努力的清零!

目录

一、string类重要成员函数

1.1 构造函数

1.2 类对象容量操作

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

1.4 string类对象的修改操作

1.4.1 c_str  (返回C格式字符串)

1.4.2 operator+= (在字符串后追加字符串str)

1.4.3 npos 

1.4.4 find+npos (从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置)

 1.4.5  substr(在str中从pos位置开始,截取n个字符,然后将其返回)

1.5 string类非成员函数

1.5.1  operator<< 和 getline

1.5.2 relational operators (比较函数) 

 二、string类自我模拟实现主要成员函数

2.1 string类的基本框架

2.2 构造函数和析构函数

★2.3 “大佬”写的拷贝构造与赋值构造函数(深拷贝!)

2.3 类对象操作函数

2.3.1 size&&length(返回有效字符个数)

2.3.2 capacity(返回开辟空间大小)

2.3.3 empty(判断类字符数组有效字符是否为0)

2.3.4 clear(清理字符数组)

★★2.3.5 reserve (提前设定空间容量大小)

★★2.3.6 resize (修改有效字符个数)

2.4 string类对象的访问及遍历操作函数

2.4.1 operator[](获取字符数组指定位置字符)

2.4.2 begin/end(返回头/尾指针)

2.5 string类对象的修改操作函数

2.5.1 c_str(获取字符数组指针)

2.5.2 push_back(尾插)

★★2.5.3 operator+=(尾插入字符/字符串)

2.5.4 append(从某个字符串截取部分尾插到字符数组中)

2.5.5 insert(指定位置插入字符/字符串  💡需要挪动数据   注重临界!!)

2.5.6 erase(删除指定位置后面指定个数字符)

2.5.6 find (从指定位置向后寻找指定字符)

2.6 string类非成员函数

2.6.1 operator<<(申明为友元函数 !)

★★2.6.2 operator>>(申明为友元函数)

2.6.3 getline(可以获取输入的空格,也申明为友元)

三、总结


一、string类重要成员函数

1.1 构造函数

(constructor)函数名称功能说明
string() (重点)构造空的string类对象,即空字符串
string(const char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点)拷贝构造函数

1.2 类对象容量操作

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

💡resize

如果 n 小于当前字符串长度,则当前值将缩短为其第一个 n 个字符,删除第n个字符以外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入所需数量的字符以达到n大小来扩展当前内容。如果指定了 c,则新元素初始化为c 的副本,否则,它们是初始化字符(空字符)

💡💡这里提一下resize函数间接影响capacity三种情况(对n进行分类讨论):

1.n>原capacity 扩容

2.n=原capacity 不变

3.n<原capacity 不变


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

函数名称功能说明
operator[] (重
点)
返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭
代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭
代器
范围forC++11支持更简洁的范围for的新遍历方式(底层靠迭代器实现)
int main()
{
	string s("hello world!");
	//1.运算符重载[]实现
	for (int i = 0;i < s.size();i++)
	{
		cout << s[i];
	}
	cout << endl;
	//2.迭代器
	string::iterator it = s.begin();//获取头字符
	while (it != s.end())
	{
		cout << *it;
		it++;
	}
	cout << endl;
	//3.范围for(底层依旧是迭代器)
	for (auto ch : s)
	{
		cout << ch;
	}
	cout << endl;

	//4.反向迭代器(从后往前)
	string::reverse_iterator rit = s.rbegin();//取尾
	while (rit != s.rend())
	{
		cout << *rit;
		rit++;
	}
	cout << endl;
	return 0;
}


1.4 string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

1.4.1 c_str  (返回C格式字符串)

 const char* c_str() const


1.4.2 operator+= (在字符串后追加字符串str)

string (1)
string& operator+= (const string& str);
c-string (2)
string& operator+= (const char* s);
character (3)
string& operator+= (char c);

1.4.3 npos 

(size_t npos=-1 表示无符号整型) 是类的静态私有成员,是整形能够表示的最大值


1.4.4 find+npos (从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置)

返回值:第一个匹配项的第一个字符的位置,如果未找到匹配项,则该函数返回string::npos

string (1)
size_t find (const string& str, size_t pos = 0) const;
c-string (2)
size_t find (const char* s, size_t pos = 0) const;
buffer (3)
size_t find (const char* s, size_t pos, size_t n) const;
character (4)
size_t find (char c, size_t pos = 0) const;

 1.4.5  substr(在str中从pos位置开始,截取n个字符,然后将其返回)

申明:string substr (size_t pos = 0, size_t len = npos) const ;

1.5 string类非成员函数

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

1.5.1  operator<< 和 getline

💡💡 cin读取不了空格,空格表示分隔 

比如你输入hello world! 输出只有hello

那么如何获取空格呢?  getline()函数可以

(1)
istream& getline (istream& is, string& str, char delim);
(2)
istream& getline (istream& is, string& str);
//要包头文件
#include<string>
int main ()
{
  std::string name;

  std::cout << "Please, enter your full name: ";
  std::getline (std::cin,name);
  std::cout << "Hello, " << name << "!\n";

  return 0;
}

1.5.2 relational operators (比较函数) 

(1)

bool operator== (const string& lhs, const string& rhs);
bool operator== (const char*   lhs, const string& rhs);
bool operator== (const string& lhs, const char*   rhs);
(2)
bool operator!= (const string& lhs, const string& rhs);
bool operator!= (const char*   lhs, const string& rhs);
bool operator!= (const string& lhs, const char*   rhs);
(3)
bool operator<  (const string& lhs, const string& rhs);
bool operator<  (const char*   lhs, const string& rhs);
bool operator<  (const string& lhs, const char*   rhs);
(4)
bool operator<= (const string& lhs, const string& rhs);
bool operator<= (const char*   lhs, const string& rhs);
bool operator<= (const string& lhs, const char*   rhs);
(5)
bool operator>  (const string& lhs, const string& rhs);
bool operator>  (const char*   lhs, const string& rhs);
bool operator>  (const string& lhs, const char*   rhs);
(6)
bool operator>= (const string& lhs, const string& rhs);
bool operator>= (const char*   lhs, const string& rhs);
bool operator>= (const string& lhs, const char*   rhs);

 二、string类自我模拟实现主要成员函数

2.1 string类的基本框架

💡为了与std::string不冲突,我们自己开了一个命名空间,在命名空间内调用自己写的string!

//基本框架
namespace wyz
{
	class string
	{
	public:
	private:
		char* _str;//指向char 数组的指针
		size_t _capacity;//数组容量
		size_t _size;//有效字符个数
		const static size_t npos = -1;
	};
}

2.2 构造函数和析构函数

string(const char* str)
{

	_capacity = strlen(str);
	//!注意要多开一个空间给'\0'
	_str = new char[_capacity+1];
	_size = strlen(str);
	strcpy(_str, str);
}
//拷贝构造(传统写法)
string(const string& str)
{
	_capacity = str._capacity;
	_size = str._size;
	//注意深浅拷贝区别!
	_str = new char[str._capacity+1];
	strcpy(_str, str._str);
}



//不传参情况
string()
{
	_str = new char[1];
	_str[0] = '\0';
	_capacity = 0;
	_size = 0;
}
//析构函数
~string()
{
	delete[]_str;
	_str = nullptr;
	_capacity = _size = 0;
}

★2.3 “大佬”写的拷贝构造与赋值构造函数(深拷贝!)

先用一个不巧当的比喻来概述这段大佬写的拷贝构造与赋值构造函数!

主角是一个可怜打工仔,临时变量tmp!

拷贝构造:

给出一个例子:string s1="hello world!"  string s2(s1);

这里s2统一说成*this!

1.首先我们用先前写的传C类型指针构造tmp,这样tmp指向空间内容和s1一样,!但是tmp_str指向的空间不是和s1一样(深拷贝)!_size,_capacity和s1一样!

2.初始化tmp好了以后,眼光放到*this这边!这里先告诉读者我们必须初始化s2,之后会讲为什么一定要初始化!我们要将s1拷贝给s2,临时变量已经创建好,现在只需要交换,交换的内容很简单就是内置成员变量,指针+容量+有效字符个数 给*this,这样tmp拿到了初始化的s2的内置成员值,完成拷贝。

3.出了函数。注意!这里tmp是临时类,出了作用域要自动调用它的析构函数!现在假如*this没有初始化,也就说*this的_str是野指针,析构函数delete不能释放野指针指向的空间,会程序崩溃!这就是为什么一定要初始化s2即*this!


赋值重载:s2=s1

同样需要创建临时变量,我们写过拷贝构造以后可以直接不引用传参,而是拷贝传参给tmp!这里因为s2已经初始化,所以没有上面的初始化,直接将两者交换!同样出了函数,tmp指向s2原指向的空间,出了函数会自动调用析构函数!

所以最终代码简简单单地就是几行!

//传统拷贝构造
/*string(const string& str)
{
  _capacity = str._capacity;
  _size = str._size;
  _str = new char[str._capacity];
  strcpy(_str, str._str);
}*/

//交换函数
void swap(string& tmp)
{
	std::swap(_str, tmp._str);
	std::swap(_size, tmp._size);
	std::swap(_capacity, tmp._capacity);
}

//现代拷贝构造
string(const string& str)
	:_str(nullptr)
	, _size(0)
	, _capacity(0)
{
	string tmp(str.c_str());//注意这里用构造函数!
	swap(tmp);
}

//赋值重载
string& operator=(string tmp)//注意一个细节变化!这里传参的时候用直接用拷贝构造tmp
{
	swap(tmp);
	return *this;
    //交换完以后,原来*this指向的字符串的空间交换给了tmp,tmp是临时变量,出了这个函数就会自动调用析构函数,将原来*this指向的空间释放掉!真是纯纯打工仔冤种!
}

2.3 类对象操作函数

2.3.1 size&&length(返回有效字符个数)


size_t size()
{
	return _size;
}

2.3.2 capacity(返回开辟空间大小)

//返回空间大小
size_t capacity()
{
	return _capacity;
}

2.3.3 empty(判断类字符数组有效字符是否为0)

//判断是否为空
bool empyt()
{
	return _size == 0;
}

2.3.4 clear(清理字符数组)

//清空字符数组
void clear()
{
	_str[0] = '\0';
	_size =0;
}

★★2.3.5 reserve (提前设定空间容量大小)

//开辟空间
void reserve(size_t capacity)
{
	//先保存原数组内容
	char* tmp = _str;
	//创建新数组
	_str = new char[capacity + 1];
	//拷贝
	strcpy(_str, tmp);
	_capacity = capacity;
	delete[]tmp;
}

★★2.3.6 resize (修改有效字符个数)

//修改size
void resize(size_t size)
{
	if (size > _capacity)
	{
		//必须先扩容
		(*this).reserve(size);
		//没传字符参数,将扩容空间放入空字符,这里参数数字很重要!很容易这里出错!!!
		memset(_str + size, ' ', size - _size);
	}
	//最终都要让size位置变为'\0'
	_str[size] = '\0';
	_size = size;
}
void resize(size_t size, char ch)
{
	if (size > _capacity)
	{
		(*this).reserve(size);
		memset(_str + _size, ch, size - _size);
	}
	_str[size] = '\0';
	_size = size;
}

2.4 string类对象的访问及遍历操作函数

2.4.1 operator[](获取字符数组指定位置字符)

char& operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
支持const string 类型访问
const char& operator[](size_t pos)const
{
	assert(pos < _size);
	return _str[pos];
}

2.4.2 begin/end(返回头/尾指针)

typedef char* iterator;
//返回字符串头
iterator begin()
{
	return _str;
}
返回字符串尾'\0'
iterator end()
{
	return _str + _size;
}

2.5 string类对象的修改操作函数

2.5.1 c_str(获取字符数组指针)

//返回字符数组头部指针
const char* c_str()const
{
	return _str;
}

2.5.2 push_back(尾插)

//尾插字符
void Push_back(char ch)
{
	//如果空间已满,分两路讨论扩容 1.原来是空串 初始不能*=2,要先赋值给4个字符的空间
	//2.先前不是空串,那么扩2倍(自己选择)
	size_t newcapacity = _capacity == _size == 0 ? 4 : _capacity * 2;
	reserve(newcapacity);
	//尾插,将之前'\0'位置放字符,然后再补一个'\0’
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';
}

★★2.5.3 operator+=(尾插入字符/字符串)

//插入字符
string& operator+=(char ch)
{
	//相当于尾插
	Push_back(ch);
	return *this;
}

//插入字符串
string& operator+=(const char* str)
{
	//判断是否需要扩容
	int lenth = strlen(str);
	if (_size + lenth > _capacity)
	{
		reserve(_size + lenth);
	}
	//这里如何处理插入?不建议用strcat,效率低,这里建议直接将strcpy,只要注意尾部拷贝位置!
	strcpy(_str + _size, str);
	_size += lenth;
	return *this;
}

2.5.4 append(从某个字符串截取部分尾插到字符数组中)

//从某个字符串截取部分尾插到字符数组中
string& append(const char* str, size_t pos, size_t len)
{
	if (_size + len > _capacity)
		(*this).reserve(_size + len);
	//截取部分通过循环尾插
	for (int i = 0;i < len;i++)
	{
		Push_back(*(str + pos + i));
	}
	return *this;
}

2.5.5 insert(指定位置插入字符/字符串  💡需要挪动数据   注重临界!!)

//指定位置插入字符
string& insert(size_t pos, char ch)
{
	//判断是否需要扩容
	size_t newcapacity = _capacity == _size == 0 ? 4 : _capacity * 2;
	reserve(newcapacity);
	//挪动数据
	int end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end-1];
		end--;
	}
	_str[pos] = ch;
	_size++;
	return *this;
}

//指定位置之后插入字符串
string& insert(const char* str, size_t pos)
{
	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;
}

2.5.6 erase(删除指定位置后面指定个数字符)

//删除指定位置后面指定个数字符
string& erase(size_t pos, size_t len)
{
	//1.如果指定位置后面字符<len,即删除后面所有
	if (len == npos || len >= _size - pos)
	{
		//只需要将'\0'放到pos位置
		_str[pos] = '\0';
		_size = pos;
	}
	//2.后面字符>len,则需要将后面数据挪动到前面
	else
	{
		size_t begin = pos + len;
		while (begin <= _size)
		{
			_str[begin - len] = _str[begin];
			begin++;
		}
		_size -= len;
	}
	return *this;
}

2.5.6 find (从指定位置向后寻找指定字符)

size_t find(char ch, size_t pos)const
{
	assert(pos < _size);
	//循环遍历,找到返回下标
	for (int i = pos;i < _size;i++)
	{
		if (_str[i] == ch)
			return i;
	}
	//找不到返回npos
	return npos;
}

2.6 string类非成员函数

2.6.1 operator<<(申明为友元函数 !)

ostream& operator<<(ostream& out, const string& s)
{
	out << s._str << endl;
	return out;
}

★★2.6.2 operator>>(申明为友元函数)

cin>>流提取拿不到空格,空格是分隔符!注意循环结束条件!

很多人会直接用push_back尾插实现一个一个字符插入,但是这样会不断的扩容!效率会很低,我们可以创一个加载数组buffer,先将数据存入buffer中,然后做字符串+=尾插

istream& operator>>(istream& in, string& s)
{
	int i = 0;
	char buffer[128] = { '\0' };
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		if (i == 127)//+=底层用strcpy,需要'\0',所以这里不能是i==128,这里很细节!!!!
		{
			s += buffer;
			i = 0;
		}
		buffer[i++] = ch;
		ch = in.get();
	}
   //将buffer剩余数据插入
    if(i>=0)
	{
      buffer[i] = '\0';
	  s += buffer;
	  return in;
    }   
}

2.6.3 getline(可以获取输入的空格,也申明为友元)

istream& getline(istream& in, string& s)
{
	int i = 0;
	char buffer[128] = { '\0' };
	char ch = in.get();
	while (ch != '\n')
	{
		if (i == 127)//+=底层用strcpy,需要'\0'!!!!
		{
			s += buffer;
			i = 0;
		}
		buffer[i++] = ch;
		ch = in.get();
	}
	buffer[i] = '\0';
	s += buffer;
	return in;
}

三、总结

作为STL第一个标准类模板,string的用处广泛!很多人会用string的类成员函数,但是不知道它的底层如何实现,我以为如果你能深入了解底层实现原理,在运用函数上可以很好避免错误,能明白它为什么要这么用,传的参数到底是什么?所以我们不仅要知其然,也要知其所以然!这样两者结合,我们运用函数更加得心应手!最后,希望读者能不吝赐教!感谢支持❤❤

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: C++中的string类是标准库中提供的一个非常常用的类,用来操作字符串。其中的find函数是用来在字符串中查找子字符串的方法。在C++中,string的find函数有多个重载版本,可以根据不同的参数类型和个数进行调用。 基本的find函数签名为:size_t find(const string& str, size_t pos = 0) const。 其中,str是要查找的子字符串,pos表示开始查找的位置,默认为0。该函数的返回类型为size_t,即一个无符号整数类型,表示子字符串在字符串中的下标,如果找不到则返回string::npos,即-1。 下面是一个例子,演示了如何使用find函数: ``` #include <iostream> #include <string> using namespace std; int main() { string str = "Hello, world!"; string subStr = "world"; size_t pos = str.find(subStr); if (pos != string::npos) { cout << "子字符串\"" << subStr << "\"在位置" << pos << "找到了" << endl; } else { cout << "子字符串\"" << subStr << "\"未找到" << endl; } return 0; } ``` 在以上例子中,我们定义了一个字符串str和一个子字符串subStr,然后使用find函数在str中查找subStr。如果找到了,会打印子字符串的位置;如果未找到,则会打印未找到的提示。 需要注意的是,find函数是区分大小写的,如果要实现不区分大小写的查找,可以使用其他的函数或者自行实现。此外,find函数还有其他的重载版本,可以指定查找的方向、查找的次数等参数。不同的参数可以满足不同的需求。 ### 回答2: C++中的string类是一个非常常用的字符串类,它提供了许多用于处理字符串的函数,其中包括find函数。find函数的作用是在字符串中查找指定的子字符串,并返回其第一次出现的位置。 find函数有多个重载形式,最常用的形式是以下两种: 1. find(const string& str, size_t pos = 0):在字符串中从指定的位置pos开始查找子字符串str,并返回其第一次出现的位置。如果找不到该子字符串,则返回string::npos。 2. find(const char* s, size_t pos = 0):在字符串中从指定的位置pos开始查找C风格字符串s,并返回其第一次出现的位置。如果找不到该子字符串,则返回string::npos。 下面是一个简单的示例代码,演示了find函数的使用: ``` #include <iostream> #include <string> using namespace std; int main() { string str = "Hello, world!"; string subStr = "world"; size_t pos = str.find(subStr); // 查找子字符串的位置 if (pos != string::npos) { cout << "子字符串在位置 " << pos << " 处找到了。" << endl; } else { cout << "子字符串未找到。" << endl; } return 0; } ``` 运行结果为: ``` 子字符串在位置 7 处找到了。 ``` 通过查找子字符串的位置,我们可以知道子字符串是否在原字符串中以及其位置。这对于字符串的处理非常有用,例如用于查找关键字、替换子字符串等操作。 ### 回答3: C++标准库中的string类提供了一个名为find的成员函数,用于在字符串中查找子字符串。它的使用方法如下: string findstr = "example"; string str = "This is an example string."; int pos = str.find(findstr); if(pos != string::npos){ cout << "子字符串在原字符串中的位置是:" << pos << endl; } else{ cout << "子字符串未在原字符串中找到。" << endl; } 以上例子中,我们首先定义了一个待查找的子字符串findstr和一个原字符串str。然后通过调用str的find函数,传入待查找的子字符串findstr作为参数。find函数返回查找到的子字符串在原字符串中的位置(的首字符索引)。如果查找成功,则返回该位置值;如果查找失败,则返回一个特定的常数string::npos。因此,我们可以通过与string::npos进行比较,判断是否找到了子字符串。 需要注意的是,find函数还可以传入另外两个可选的参数,分别是起始搜索位置和要搜索的字符数量。例如: int pos = str.find(findstr, 5, 10); 表示从原字符串的第5个字符开始,最多搜索10个字符的范围内查找子字符串findstr。 总结起来,C++ string类的find函数可以用于在字符串中查找子字符串,它返回子字符串在原字符串中的位置或者一个特定的常数string::npos表示查找失败。此外,还可以通过传入可选的参数来指定起始搜索位置和搜索字符数量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值