【C++】STL容器---string类

  我要去赶火车,走夜路,先活过那条哀鸣的狗,再回来认我的命。

前言 

  这是我自己学习C++的第六篇博客总结。后期我会继续把C++学习笔记开源至博客上。

  上一期笔记是关于C++的模板初阶知识,没看的同学可以过去看看:

【C++】模板初阶-CSDN博客https://blog.csdn.net/hsy1603914691/article/details/143869918

STL

STL容器概念

1. STL容器C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架

2. STL容器里面的函数都是C++标准库中的函数

STL六大组件

string类

string类的优势 

  在C语言中,字符串是以'\0'结尾的一些字符的集合C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象编程的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问

string类的本质

1. 在使用string类时,必须包含 #include <string> 这一行。

2. string类底层其实是一个储存字符的顺序表结构

3. 下面是string类的官方文本介绍,里面有详细的用法讲解。string类 的官方文本介绍https://cplusplus.com/reference/string/string/?kw=stringhttps://cplusplus.com/reference/string/string/?kw=stringhttps://cplusplus.com/reference/string/string/?kw=stringhttps://cplusplus.com/reference/string/string/?kw=stringhttps://cplusplus.com/reference/string/string/?kw=string

class string
{
public:
	//成员函数
private:
	//一个顺序表结构
	char* arr;//一个存储字符的动态数组
	int size;//字符数组的有效大小
	int capacity;//字符数组的容量
};

auto关键字

1. auto可以让编译器自动推导变量类型

2. auto一般用在变量类型比较长、复杂的地方,把变量类型直接用auto替代,更加方便。

3. auto一般和范围for连用,写起来简洁方便。

4. 如果想让auto声明引用类型变量,则需要使用auto&

5. auto使用时,不能直接拿来声明数组

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	int r1 = 1;
	auto r2 = r1;//自动推导为 int
	auto r3 = &r2;//自动推导为 int*
	int& r4 = r2;
	auto r5 = r4;//自动推导为 int
	auto& r6 = r4;//自动推导为 int&
	return 0;
}

string类的常用接口说明 

string类对象的常见构造 

1. string(),什么也不需要传入,构造一个空的string类对象,即空字符串

2. string(const char*s),传入一个字符串s,然后用字符串s进行构造

3. string(size_t n,char c),构造一个string类对象,里面包含n个c字符

4. string(const string&str),传入一个string类对象str,然后用str进行拷贝构造

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1;
	string str2("abcd");
	string str3(5, 'c');
	string str4(str2);
	cout << str1 << endl;
	cout << str2 << endl;
	cout << str3 << endl;
	cout << str4 << endl;
	return 0;
}
//
//abcd
//ccccc
//abcd

1. string类对象初始化分为两种。

2. 如果使用号,则为拷贝初始化;如果不使用号,则为赋值初始化。 

3. string类对象的初始化时,必须传入一个字符串

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hehe");
	string str2 = "hehe";
	cout << str1 << endl;
	cout << str2 << endl;
	return 0;
}
//hehe
//hehe

string类对象的容量操作 

1. string.size(),返回字符串有效字符的大小

2. string.capacity(),返回字符串底层容量大小

3. string.empty(),检测字符串有效字符是否为空为空返回true为非空返回flase

4. string.clear()清空字符串的有效字符,此时字符串中有效字符大小为0,底层容量大小不变

5. string.reserve(size_t n),为字符串预留出空间,实际预留的空间通常比n大。

6. string.resize(size_t n,char c),将字符串中有效字符个数改成n个,如果有效字符有多出的空间,就用字符c填充。

7. string.clear()只是将字符串中的有效字符清空,只改变有效字符大小,并不改变底层容量大小

8. string.resize(size_t n)string.resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(size_t n)0来填充多出的元素空间,resize(size_t n, char c)字符c来填充多出的元素空间。

9.  string.resize(size_t n) 在改变元素个数时,如果是将元素个数 增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
10.  string.reserve(size_t n) 为字符串预留空间,不改变有效元素个数,当 reserve 的参数小于字符串的底层容量总大小时, reserver 不会改变容量大小
11.  string.resize(size_t n) string.reserve(size_t n) 可能增大 底层容量,但是 不会减小 底层容量。
int main()
{
	string str1("hello world");
	cout << str1.size() << endl;//11
	cout << str1.capacity() << endl;//15
	cout << endl;

	str1.clear();
	cout << str1.size() << endl;//0
	cout << str1.capacity() << endl;//15
	cout << str1.empty() << endl;//1(为空)
	cout << endl;

	str1.resize(10, 'q');
	cout << str1.size() << endl;//10
	cout << str1.capacity() << endl;//15
	cout << endl;

	str1.resize(15);
	cout << str1.size() << endl;//15
	cout << str1.capacity() << endl;//15
	cout << endl;

	str1.reserve(100);
	cout << str1.size() << endl;//15
	cout << str1.capacity() << endl;//111
	cout << endl;

	str1.reserve(5);
	cout << str1.size() << endl;//15
	cout << str1.capacity() << endl;//111
	cout << endl;
	return 0;
}

string类对象的访问遍历操作

1. string[int pos],返回下标为pos位置的字符。

2. string.begin()、string.end()string.begin()获取第一个字符的迭代器string.end()获取最后一个字符的下一个位置的迭代器

3. string.rbegin()、string.rend()string.rbegin()获取最后一个字符的迭代器string.rend()获取第一个字符的前一个位置的迭代器

4. 注意反向迭代器进行迭代的步骤也是++,反向迭代器是用来反向遍历字符串的。 

5. 范围for循环,用于有范围的集合进行遍历,C++11中引入了基于范围的for循环for循环后的括号由冒号" : "分为两部分:第一部分是用于迭代的变量 (可以使用auto让编译器自动判断变量类型),第二部分则表示迭代的范围

6. 范围for循环有着:自动迭代,自动取数据,自动判断结束的特点。

7. 范围for循环,如果需要对范围对象进行修改,则使用auto&来修饰迭代的变量;如果需要对较大的目标对象进行遍历,也可以使用auto&来修饰迭代的变量

8. 范围for循环,是用于遍历容器的,它的底层也是迭代器数组也能使用范围or循环

//正常遍历
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("Test string");
	for (int i = 0; i < str1.size(); i++)
	{
		cout << str1[i];
	}
	return 0;
}
//Test string
//[]操作符底层逻辑
class string
{
public:
	char& operator[](int pos)//进行运算符重构
	{
		assert(pos < _size);
		return _arr[pos];
	}
private:
	char* _arr;
	int _size;
	int _capacity;
};
//迭代器
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("Test string");
	string::iterator it1 = str1.begin();//把iterator看成string类里面的一个内部类。
	while (it1!=str1.end())//str1.end()表示str1的最后一个有效字符的下一位。
	{
		cout << *it1;//可以把it1看成指针
		++it1;
	}
	return 0;
}
//Test string
//反向迭代器
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hello world");
	auto it1 = str1.rbegin();
	while (it1 != str1.rend())
	{
		cout << *it1;
		it1++;
	}
	return 0;
}
//dlrow olleh
//for遍历
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hello world");
	for(auto s:str1)
	{
		cout<<s;
	}
	return 0;
}
//hello world

//for修改
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hello world");
	for(auto& s:str1)
	{
		s++;
		cout<<s;
	}
	return 0;
}
//ifmmp!xpsme

//循环数组
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	for (auto& num : arr1)
	{
		cout << num;
	}
	return 0;
}
//1234567

string类对象的修改操作

1. string.push_back(char c),在字符串后尾插字符c

2. string.append(char s),在字符串之后尾插字符串s

3. +=,在字符串之后尾插字符或者字符串

4. string.insert(int pos,char s)方法可以从下标为pos的位置往前插入字符串s

5. string.erase(int pos,size_t len)方法可以从下标为pos的位置上,删除pos位置及其后面共len个字符

6. replace(int pos,size_t len,char s)方法可以从下标为pos的位置上,替换pos位置及其后面共len个字符,替换为字符串s

7. string.find()、string::nposstring.find()方法用于在字符串中查找相同的字符,如果找到了则返回该字符下标。如果string.find()找到了,则不等于string::npos,如果string.find()没找到,则等于string::npos

8. string.substr(int pos,size_t len)方法可以从下标为pos的位置上,返回pos位置及其后面共n个字符

9. reverse(string.begin(),string.end()),用于反转整个字符串

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hello world");
	str1.push_back('!');
	str1.append(" hello world!");
	cout << str1;
	return 0;
}
//hello world! hello world!
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1("hello world");
	str1 += '!';
	str1 += " hello world";
	cout << str1;
	return 0;
}
//hello world! hello world!
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
	string str("abcdefg");
	str.insert(0, "111");
	cout << str << endl;
	str.erase(0, 3);
	cout << str;
	return 0;
}
//111abcdefg
//abcdefg
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str("ab cd ef g");
	int i = str.find(' ');
	while (i != string::npos)
	{
		str.replace(i, 1, "%%");
		i = str.find(' ');
	}
	cout << str;
	return 0;
}
//ab%%cd%%ef%%g
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str("hello world");
	auto s = str.substr(0, 2);
	cout << s;
	return 0;
}
//he
//反转字符串
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
	string str("abcdefg");
	reverse(str.begin(), str.end());
	cout << str;
	return 0;
}
//gfedcba

string类对象的输入

1. cin输入,string对象会自动忽略开头的空格和换行,并从第一个有效字符开始读取,然后在下一个空格或者换行处自动结束读取

2. getline(cin,string)输入,会在换行处自动断开,可以输入带有空格的字符串

3. getline(cin,string,'*')输入,可以指定'*'结束字符,直到出现'*'结束输入

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1, str2;
	cin >> str1 >> str2;
	cout << str1 << endl;
	cout << str2 << endl;
	return 0;
}

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int main()
{
	string str1, str2;
	getline(cin, str1);
	getline(cin, str2);
	cout << str1 << endl;
	cout << str2 << endl;
	return 0;
}

模拟实现string类 

#include <iostream>
#include <cstring>
#include <cassert>
using namespace std;

class  String
{
public:
    using iterator = char*;

    String()
        :_arr(new char[1] {'\0'})
        , _size(0)
        , _capacity(0)
        //有效字符长度和底层容量都不计入'\0'的空间
    {}

    String(const char* s)
        :_arr(new char[strlen(s) + 1])
        , _size(strlen(s))
        , _capacity(strlen(s))
    {
        strcpy(_arr, s);
    }

    String(const String& s)
    {
        _arr = new char[s._capacity + 1];
        strcpy(_arr, s._arr);
        _size = s._size;
        _capacity = s._capacity;
    }

    ~String()
    {
        delete[] _arr;
        _arr = nullptr;
        _size = 0;
        _capacity = 0;
    }

    const char& operator[](size_t i) const//第一个const避免通过返回的引用修改原来的字符串内容,&防止函数进行值传递,第二个const防止函数修改原字符串内容
    {
        assert(i < _size);
        return _arr[i];
    }

    size_t size()const
    {
        return _size;
    }

    size_t capacity()const
    {
        return _capacity;
    }

    iterator begin()//返回第一个字符的地址(指针)
    {
        return _arr;
    }

    iterator end()//返回最后一个字符的下一个位置的地址(指针)
    {
        return _arr + _size;
    }

    void reserve(size_t n)
    {
        if (n > _capacity)
        {
            char* tmp = new char[n + 1];
            strcpy(tmp, _arr);
            delete[] _arr;
            _arr = tmp;
            _capacity = n;
        }
    }
    void push_back(char c)
    {
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : 2 * _capacity);
            _arr[_size] = c;
            _size++;
        }
    }
    friend ostream& operator<<(ostream& os, const String& str);
    friend istream& operator>>(istream& is, String& str);
protected:
    char* _arr;
    int _size;
    int _capacity;
};

istream& operator>>(istream& is, String& str)
{
    char c;
    is >> c;//先读取第一个字符
    while (c != ' ' && c != '\n')
    {
        str.push_back(c);
        is >> c;
    }
    return is;
}

ostream& operator<<(ostream& os, const String& str)//const防止输出重载函数对str做出修改,&防止输出重载函数进行值传递
{
    os << str._arr;
    return os;
}

int main()
{
    String s1("hello");
    String s2 = s1;
    cout << s1 << endl;
    cout << s1[2] << endl;
    auto it = s2.begin();
    while (it != s2.end())
    {
        cout << *it << " ";
        it++;
    }
    return 0;
}

深浅拷贝 

1. 浅拷贝是指在进行拷贝时仅仅复制原对象的数据成员的值而不递归地复制指针或引用所指向的对象。这意味着如果数据成员中包含指针浅拷贝只会复制指针的值(即地址)而不是分配新的内存来复制指针所指向的内容。因此,新旧对象可能会共享同一块动态分配的内存或资源

2. 如果多个对象共享同一份资源, 则当一个对象销毁时就会将该资源释放掉 而另外对象不知道该资源已经被释放 ,所以当继续对资源进项操作时,就会发生发生了访问违规。
3.  深拷贝是指 在进行拷贝时 不仅复制原对象的数据成员的值还会递归地复制所有动态分配的内存和其他引用的对象两个对象拥有独立的内存空间
//浅拷贝
+------------------+       +------+
| original         |       | 42   |
| ptr ------------>|-----> |      |
+------------------+       +------+
                                 ^
                                 |
+------------------+              |
| copy             |              |
| ptr ------------>|--------------+
+------------------+
//深拷贝
+------------------+       +------+
| original         |       | 42   |
| ptr ------------>|-----> |      |
+------------------+       +------+

+------------------+       +------+
| copy             |       | 42   |
| ptr ------------>|-----> |      |
+------------------+       +------+

致谢

  感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能! 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值