【c++】string类(上)

1、标准库中的string类

学会看文档---学会猜

cplusplus.com/reference/string/string/

比如说这里,有需要再查文档

String类的constuctor(构造)

一、string类常用构造:

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

示例:

#include<iostream>
using namespace std;
#include<string>

void Teststring()
{
	string s1; // 构造空的string类对象s1
	string s2("hello world"); // 用C格式字符串构造string类对象s2
	string s3(s2); // 拷贝构造s3
	string s4(s2, 5);
	string s5 = s4;//赋值构造
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
}					   


int main()
{
	Teststring();

	return 0;
}

输出结果:

二、string的遍历

1.第一种(operator[]):

注意区别:

int a[10]

a[i]等价于 *(a+i)

void test_string1()
{
	string s1("hello world");

	// 下标+[]
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
		//cout << s1.operator[](i) << " ";//这是一个函数调用
	}
	cout << endl;

	for (size_t i = 0; i < s1.size(); i++)
	{
		s1[i]++;

	}

	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;

}

运行结果:

其实相当于调用了:

可读可修改

class string //底层是basic_string
{
public:
	char& operator[] (size_t pos)
	{
		return _str[pos];
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

string::operator[]函数 构成两个运算符重载,又构成函数重载

构成两个运算符重载,又构成函数重载,

为啥有两个版本 一个普通对象,一个const对象,为了让const对象无法修改

参数类型不同才能构成重载

而这里他们的参数类型就不同,他们隐含的this指针不同

1.string*    2.const string*

const修饰的是this指针指向的内容

string s3("hello world");
s3[0]++;
cout << s3 << endl;

const string s2("hello world");
//s2[0]++;
cout << s2 << endl;

cout << s3.size() << endl;
cout << s3.capacity() << endl;

运行结果:

这里注意,size()不包括\0

2.第二种(iterator)

迭代器 iterator 在这里是string的一个类型,这个类型是定义在他的类域里面的

所以要指定类域:string::iterator

一般变量名都取名 it 

string::iterator it3 = s3.begin();

while (it3 != s3.end())
{
	cout << *it3 << " ";
	++it3;
}
cout << endl;

迭代器行为是像指针一样类型对象

迭代器也是一个可读写的

	string::iterator it3 = s3.begin();
	
	while (it3 != s3.end())
	{
		*it3 -= 3;
		++it3;
	}
	cout << endl;

	it3 = s3.begin();

	while (it3 != s3.end())
	{
		cout << *it3 << " ";
		++it3;
	}
	cout << endl;

运行结果:

对比两种遍历:哪种好用?

[]好用

但是,迭代器才是主流

迭代器可以遍历通用的容器(不管底层是数组、是链表还是数)

	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);

	list<int>::iterator itt = lt.begin();
	while (itt != lt.end())
	{
		cout << *itt << " ";
		++itt;
	}
	cout << endl;

运行结果:

3.第三种(范围for)

范围for可以遍历数组

也可以遍历容器

    // 底层就是迭代器
	for (auto e : s3)
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

运行结果:

在底层的角度看,两组代码是一样的,无区别,因为范围for的底层就是迭代器

在string类中vector,范围for和[]好用

反向迭代器、const迭代器:

反向:

示例代码:

void test_string2() {
	string s2("hello world");
	string::reverse_iterator rit = s2.rbegin();
	while (rit != s2.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

运行结果:

类似于这样的原理:

const迭代器

示例:

由于这里是一个const 对象,返回的不是普通迭代器,会报错,应该是有const修饰的

因此应该这样写:

void test_string3() {

	const string s3("hello world");
	string::const_iterator it3 = s3.begin();
	while (it3 != s3.end())
	{
		cout << *it3 << " ";
		++it3;
	}
	cout << endl;
}

运行结果:

普通迭代器和const修饰过的迭代器的区别是什么?

普通迭代器可读可写,const修饰的迭代器只可读

因此还有const反向迭代器

const_reverse_iterator(这里不做赘述)

三、string 类的容量

1.size()和length()

void test_string4()
{
	string s1("hello world");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
}

输出结果一样:11(都不包含"\0")

但是由于规范,我们最好选择size(),size出现的时间更早,通用取名。

2.max_size()意义不大

	cout << s1.max_size() << endl;
	cout << s1.capacity() << endl;

max_size意义不大,

capacity在这里值为:15

3.扩容capacity()

在vs2022中:

	// 查看扩容机制
	string s;
	size_t sz = s.capacity();

	cout << "capacity changed: " << sz << '\n';

	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

运行结果:

实际上,capacity不包含\0占用的空间,因此这里的15实际上是16,第一次扩容二倍,后面都扩容1.5倍

在linux环境下g++:

运行结果:

2倍

总结:STL是一个规范,规定功能,没有规定实现细节 

4.clear()清数据,清空数据但是不一定清空间:

清空数据但是不清空间:通过此验证

    cout << s1 << endl;
    s1.clear();
	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

运行结果:这里只是清除数据但是没有清空间

再比如:

void test_string4()
{
	string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	cout << s1.size() << endl;
	cout << s1.length() << endl;

	cout << s1.max_size() << endl;
	cout << s1.capacity() << endl;

	// 查看扩容机制
	string s;

	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;


	s1.clear();
	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;
}

运行结果:

5.shrink_to_fit()缩容

如此:

	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;


	s1.clear();
	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

	// 缩容
	s1.shrink_to_fit();
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

运行结果:

6.reserve()手动扩容

区分:

reserve --- 保留储备,(在这里是用来请求扩容的,手动扩容)

reverse --- 翻转

扩容是有代价的:

扩容会先拷贝数据到一个更大的空间,在销毁原来的数据空间。

什么方式可以不用扩容,使用reserve(),但是前提是我知道要使用多少空间

提前保留一个这样的空间:

void test_string4()
{
	string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	cout << s1.size() << endl;
	cout << s1.length() << endl;

	cout << s1.max_size() << endl;
	cout << s1.capacity() << endl;

	// 查看扩容机制
	string s;
	s.reserve(100);

	size_t sz = s.capacity();
	cout << "capacity changed: " << sz << '\n';
	cout << "making s grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;


	s1.clear();
	cout << s1 << endl;
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

	// 缩容
	s1.shrink_to_fit();
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;
}

运行结果:

reserve开100的空间就一定开100吗?在vs中不一定

在linux ---g++中是100

reserve会缩容吗?

不会,比当前capacity大的时候才会扩容

void test_string5()
{
	string s1("hello worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;
	s1.reserve(10);
	cout << s1.capacity() << endl;
	cout << s1.size() << endl;

}

运行结果:

7.resize()改变size

reserve是改变容量,resize是改变size,该数据,空间不够会扩容吗?不会

resize()有三种情况

10     会删除            n<size

20     会插入            size<n<capacity

40     扩容+插入      capacity<n

void test_string5()
{
	string s2("hello worldxxxxxxx");
	cout << s2 << endl;
	cout << s2.capacity() << endl;
	cout << s2.size() << endl;
	cout << "xxxxxxxxxxxxxxxxxxxxxxx" << endl;

	s2.reserve(200);
	cout << s2 << endl;
	cout << s2.capacity() << endl;
	cout << s2.size() << endl;

	cout << "xxxxxxxxxxxxxxxxxxxxxxx" << endl;

	s2.resize(10);
	cout << s2 << endl;
	cout << s2.capacity() << endl;
	cout << s2.size() << endl;
}

输出结果:

会保留前十个数据,因此在应用时,如果只想保留比原字符串少的可以用resize来删除多余

此时再:

    s2.resize(20);
    cout << s2 << endl;
    cout << s2.capacity() << endl;
    cout << s2.size() << endl;

运行结果:

相当于再插入了10个数据,并且插入的是"\0"

resize的是比capacity大的呢?

四、数据的访问

1.operator[]

类似于遍历。

2.at(类似于operator)

示例代码:

void test_string6()
{
	string s1("hello_hello");
	cout << s1[5] << endl;
	cout << s1.at(5) << endl;
}

运行结果:

区别在于越界的检查不同:

当我这样写:

	s1[15];
	s1.at(15);

断言错误

		//s1[15];
		s1.at(15);

五、元素的修改

1.push_back在尾部插入字符

void test_string7()
{
	string s1("hello_hello");
	//在尾部插入字符
	s1.push_back('_');
	cout << s1 << endl;
}

运行结果:

2.append插入字符串

a.

	s1.append("hello");
	cout << s1 << endl;

运行结果

b.

void test_string7()
{
	string s1("hello_hello");
	//在尾部插入字符
	s1.push_back('_');
	cout << s1 << endl;
	s1.append("hello");
	cout << s1 << endl;

	s1.append(10, '!');
	cout << s1 << endl;

	string s2("  apple  ");

	s1.append(s2.begin(), s2.end());
	cout << s1 << endl;

}

运行结果:

我想让apple空格少一些

    s1.append(++s2.begin(), --s2.end());
    cout << s1 << endl;

运行结果:

3.operator[]对于字符、字符串的插入实践中:我们都用operator[]

void test_string7()
{
	string s3("hello_hello");
	s3 += ' ';
	s3 += "apple";
	cout << s3 << endl;
}

运行结果:

4-后所有代码:

void test_string8()
{
	string s1("hello_hello");
	cout << s1 << endl;

	s1.assign(" xxxxx");
	cout << s1 << endl;

	s1.insert(0, "yyyy");
	cout << s1 << endl;

	s1.erase(5, 3);
	cout << s1 << endl;

	s1.erase();
	cout << s1 << endl;


	string s2("hello world hello hello");

	//第五个开始的第一个字符替换成20%
	/*s2.replace(5, 1, "20%");
	cout << s2 << endl;*/

	/*size_t pos = s2.find(' ');
	while (pos != string::npos)
	{
		s2.replace(pos, 1, "20%");
		pos = s2.find(' ');
	}
	cout << s2 << endl;*/

	// insert/erase/replace
	// 能少用就要少用,因为基本都要挪动数据,效率不高
	string s3;
	s3.reserve(s2.size());
	for (auto ch : s2)
	{
		if (ch != ' ')
		{
			s3 += ch;
		}
		else
		{
			s3 += "20%";
		}
	}
	cout << s3 << endl;
	s2.swap(s3);
	cout << s2 << endl;
}

4.assign直接对空间进行覆盖

运行结果:

5.insert

    s1.insert(0, "yyyy");
    cout << s1 << endl;

运行结果:

6.erase(指定位置开始删除)

运行结果:

7.replace指定替换

 s2.replace(5, 1, "20%");

String operations:

1.find

如果没有找到就会返回string::npos

这里代码的含义是:

替换所有的空格

在s2中找出所有空格,若找到了用20%替代,没有了就退出循环

	string s2("hello world hello hello");
	//第五个开始的第一个替换成20%
	/*s2.replace(5, 1, "20%");
	cout << s2 << endl;*/

	size_t pos = s2.find(' ');
	while (pos != string::npos)
	{
		s2.replace(pos, 1, "20%");
        //替换了再继续查找,找到了再继续替换
		pos = s2.find(' ');
	}
	cout << s2 << endl;

运行结果:

不替换的写法(消耗空间,但是缩减时间):

//不替换,直接创建新对象赋值
string s3;
s3.reserve(s2.size());
for (auto ch : s2)
{
	if (ch != ' ')
	{
		s3 += ch;
	}
	else
	{
		s3 += "20%";
	}
}
cout << s3 << endl;
s2.swap(s3);
cout << s2 << endl;

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。       在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。        

        你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值