C++string类

C++string类_第1张图片

文章目录

  • 0. 前言
  • 1. string常见构造
  • 2. 成员访问
    • operator[]
    • 迭代器
  • 3. 容量
    • size && length
    • max_size
    • capacity
    • reserve
    • clear
    • resize
    • empty
    • Shrink to fit
  • 4. 字符串的修改
    • operator+=(常用)
    • append
    • push_back
    • assign
    • insert
    • erase
    • replace
    • pop_back
  • 5. 字符串操作
    • c_str
    • find
    • substr
    • compare
  • 6 关系运算符

0. 前言

string类是文本字符串的标准库类,定义在C++标准库,用来管理字符串,它提供了很多方法来处理字符串。

1. string常见构造

  1. 默认构造string()

    string s1;
    
  2. 拷贝构造string (const string& str)

    	string s1 = "hello world";
    	string s2(s1);
    	cout<< s1 << endl;	//hello world
    	cout<< s2 << endl;	//hello world
    
  3. 构造字符串中的字串string (const string& str, size_t pos, size_t len = npos)

    npos,是size_t的最大值

    	string s1 = "hello world";
    	string s2(s1, 6);	//从下标为6的位置开始拷贝构造,拷贝到\0截至	world
    	string s3(s1, 0, 5);	//从下标为0的位置开始拷贝构造,长度为5	hello
    
  4. 构造n个连续的字符string (size_t n, char c)

    string s(5, 'x');	//xxxxx
    

完整的可以参考文档string构造函数、string析构函数、string赋值运算符重载

2. 成员访问

operator[]

定义出string对象之后,想要访问里面的内容,C++重载了operator[],让我们可以像访问数组一样去访问这个对象。

//std::string::operator[]	
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
void test4()
{
	string s("hello world");
	//遍历
	for (size_t i = 0; i <s.size(); i++)
	{
		cout << s[i] << ' ';
	}
	cout << endl;
	//修改
	for (size_t i = 0; i < s.size(); i++)
	{
		s[i] = 'x';
		cout << s[i] << ' ';
	}
}

string还支持atbackfront这几种访问方式。

迭代器

除了用下标+[],C++中还引入了迭代器的概念:

void test5()
{
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << ' ';
		++it;
	}
}

这种访问方式就类似与指针。

我们所使用的范围for的底层,其实就是迭代器

void test5()
{
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << ' ';
		++it;
	}
	cout << endl;
	for (auto ch : s)
	{
		cout << ch << endl;
	}
}

C++string类_第2张图片

STL中,任何容器都是支持迭代器的,迭代器也可以跟算法配合。

void test6()
{
	string s("hello world");
	cout << s << endl;
    //与算法配合
	reverse(s.begin(),s.end());
	for (auto ch : s)
	{
		cout << ch;
	}
	cout<<endl;
}

迭代器有四种迭代方式:

C++string类_第3张图片

3. 容量

C++string类_第4张图片

size && length

size_t size() const;	//size
size_t length() const;	//length

查看文档发现,sizelength两个的定义貌似是一样的

void test6()
{
	string s("hello");
	cout << s.size() << endl;	//5
	cout << s.length() << endl;	//5
}

而且我们调用这两个方法,输出的也是一样的。我猜测早期在设置string的时候,是为了处理字符串,只需要求字符串的长度即可。

在后来STL问世之后,例如二叉树什么的,这时候length就很不合适了,所以统一用size,然后string要向前兼容保留了lenght,同时也增加了size

max_size

size_t max_size() const;

**max_size**的意思就是看这个字符串能达到的最大长度,这个在不同编译器或者不同环节下,可能有所不同。

C++string类_第5张图片

capacity

size_t capacity() const;

不仅仅是对与max_size,容量在不同平台给的也不一样

C++string类_第6张图片

VS2022,会内存对齐,这上面显式的是15,其实真正的是16,还给\0留了一个空间

而g++,初识容量是多少就给多少,每次二倍扩容

reserve

void reserve (size_t n = 0);

多次可能会产生过多的内存碎片,空间利用率也不是很高,reserve接口就可以向内存申请空间,为字符串预留一段指定的空间。

void test9()
{
	string s = "hello world";
	cout << s.capacity() << endl;

	s.reserve(100);
	cout << s.capacity() << endl;
}

当然了这在不同环境下,也是不同的:

C++string类_第7张图片

如果先向内存申请了100个字节的空间,之后又改为了50,这虽然符合语法,但具体还是由编译器实现

	string s = "hello world";
	s.reserve(100);
	cout << s.capacity() << endl;	//VS2022:111	g++:100
	s.reserve(50);
	cout << s.capacity() << endl;	//VS2022:111	g++:50

clear

void clear();

clear可以将string的字符全部删除,让其变成一个空字符串

void test10()
{
	string s("hello");
	cout << s << endl;	// hello
	cout << s.size() << endl;	// 5
	s.clear();
	cout << s << endl;	// 
	cout << s.size() << endl;	//	0
}

Tips:

当用clear清理之后,如果reserve申请的空间比初始空间小,则会缩容

void test()
{
	string s = "hello world";
	cout << s.capacity() << endl;	//	15
	s.reserve(100);
	cout << s.capacity() << endl;//	111
	s.clear();
	s.reserve(10);
	cout << s.capacity() << endl;	//	15
}

resize

void resize (size_t n);
void resize (size_t n, char c);

reserve就有点类似于C语言在的malloc,就是单纯的开空间;而resize就有点realloc的意思。

这里重载了2个函数,如果传入字符,就按照传入的字符填值,如果不传则默认填入\0

如果传入的n比当前大小要小,只会保留n之前的字符,但不会缩容。

void test11()
{
	string s1 = "hello";
	string s2 = "hello";
	string s3 = "hello world";
	s1.resize(10);
	s2.resize(10, 'x');
	s3.resize(5);
}

C++string类_第8张图片

empty

bool empty() const;

返回值为bool类型,判断是否为一个空字符串

Shrink to fit

void shrink_to_fit();

将字符串减小到合适的大小,当然这个请求不具约束力

4. 字符串的修改

C++string类_第9张图片

operator+=(常用)

string& operator+= (const string& str);
string & operator+= (const char* s);
string& operator+= (char c);

重载+=操作符,在字符串的末尾加上要添加的字符,也理解为将添加的字符串尾插到原字符串

void test1()
{
	string s = "abandon";	//	abandon
	cout << s << endl;
	s += " world";
	cout << s << endl;	//	abandon world
}

append

string& append(const string& str);
string& append(const string& str, size_t subpos, size_t sublen);
string & append(const char* s);
string& append(const char* s, size_t n);
string& append(size_t n, char c);
template <class InputIterator>
string& append(InputIterator first, InputIterator last);

也是将字符串尾插到原字符串中,只不过这里有可以插入多少的选项,不过感觉这还是有点鸡肋

void test2()
{
	string s = "abandon";
	cout << s << endl;	//	abandon
	s.append(" world");
	cout << s << endl;	//	abandon world
	s.append(" world", 2);
	cout << s << endl;	//	abandon world w
	s.append(" world", 0, 2);
	cout << s << endl;	// abandon world w w
}

push_back

void push_back (char c);

这就是正儿八经的尾插了,如果学习过数据结构,应该一看这个名字就知道是尾插的接口,每次插入一个字符。

void test3()
{
	string s = "1234";
	s.push_back('5');
	s.push_back('6');
	s.push_back('7');
	cout << s << endl;	//	1234567
}

assign

string& assign(const string& str);
string& assign(const string& str, size_t subpos, size_t sublen);
string & assign(const char* s);
string& assign(const char* s, size_t n);
string& assign(size_t n, char c);
template <class InputIterator>
string& assign(InputIterator first, InputIterator last);

将原字符串的内容替换掉,这几个接口都大同小异,用法都差不多

void test4()
{
	string s = "1234";
	cout << s << endl;	//	1234
	s.assign("567");
	cout << s << endl;	//	567
}

insert

string& insert(size_t pos, const string& str);
string& insert(size_t pos, const string& str, size_t subpos, size_t sublen);
string & insert(size_t pos, const char* s);
string& insert(size_t pos, const char* s, size_t n);
string& insert(size_t pos, size_t n, char c);
void insert(iterator p, size_t n, char c);
iterator insert(iterator p, char c);
template <class InputIterator>
void insert(iterator p, InputIterator first, InputIterator last);

指定位置插入操作

void test5()
{
	string s1 = "1234";
	cout << s1 << endl;	//	1234
	s1.insert(0, "5678");
	s1.insert(s1.size(), 3, '9');
	cout << s1 << endl;	//	56781234999

	string s2 = "hello";
	cout << s2 << endl;
	s2.insert(s2.end(), 5, 'a');
	cout << s2 << endl;	//helloaaaaa
}

erase

string& erase(size_t pos = 0, size_t len = npos);
iterator erase(iterator p);
iterator erase(iterator first, iterator last);

上面这些操作可以理解为插入操作,erase就是指定位置删除操作了。这里参数给了缺省值,如果我们没有指定删除多少,则会从指定位置往后全部删完。

void test6()
{
	string s = "12345";
	s.erase(0, 1);
	cout << s << endl;	//2345
	s.erase(1);
	cout << s << endl;	//2
}

replace

这个结构太复杂了,感兴趣可点击查看文档replace

replace将指定的字符从指定位置替换,可选择替换掉多少个字符。

void test7()
{
	string s = "1234";
	s.replace(1, 2, "abc");
	cout << s << endl;	//	1abc4
}

pop_back

void pop_back();

尾删操作

void test8()
{
	string s = "12345";
	s.pop_back();
	cout << s << endl;	//	1234
	s.pop_back();
	cout << s << endl;	//	123
}

5. 字符串操作

C++string类_第10张图片

c_str

const char* c_str() const;

这是为了兼容C语言,给C提供的一个接口

void test10()
{
	string s = "hello";
	char* cstr = new char[s.size() + 1];
	strcpy(cstr, s.c_str());
	cout << cstr << endl;	//	hello
}

find

size_t find(const string& str, size_t pos = 0) const;
size_t find(const char* s, size_t pos = 0) const;
size_t find(const char* s, size_t pos, size_t n) const;
size_t find(char c, size_t pos = 0) const;

查找字符/字符串是否出现现在原字符串。

  • rfind:反向查找
  • find_first_of:在字符串中查找指定字符集中的任何一个字符的第一个出现位置
  • find_last_of:字符串中查找指定字符集中的任何一个字符的最后一个出现位置
  • find_first_not_of:字符串中查找第一个不在指定字符集中的字符出现的位置
  • find_last_not_of:在字符串中查找最后一个不在指定字符集中的字符出现的位置

这些这就也都不是很常用,感兴趣可以查阅文档

substr

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

用于获取子字符串,用法示例:

void test9()
{
	string url = "https://blog.csdn.net/Dirty_artist?spm=1019.2139.3001.5343";
	size_t pos1 = url.find("://");
	string protocol;
	string domain;
	string uri;
	if(pos1!=string::npos)
		protocol = url.substr(0, pos1);
	size_t pos2 = url.find('/', pos1 + 3);
	if (pos2 != string::npos)
		domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
	uri = url.substr(pos2 + 1);

	cout << protocol << endl;
	cout << domain << endl;
	cout << uri << endl;
}

compare

这个设计有点冗余,我们可以直接使用重载操作符,有兴趣课看文档compare

6 关系运算符

C++string类_第11张图片

文档:relational operators (string)


string的设计稍微有些冗余,不需要全部都记住,需要用的时候查阅文档就行。
那本期分享就到这里咯,我们下期再见,如果还有下期的话。

你可能感兴趣的:(C++,原创,c++,java,数据库)