C++ STL——string和vector
注:原创不易,转载请务必注明原作者和出处,感谢支持!
注:内容来自某培训课程,不一定完全正确!
一 STL基本概念
STL(Standard Template Library)标准模板库,最早是惠普实验室开发的一系列软件的统称,现在主要出现在C++中,但是在引入C++之前该技术已经存在很长的时间了。
STL从广义上分为:容器(container),算法(alogrithm)和迭代器(iterator)。容器和算法之间通过迭代器进行无缝连接。STL几乎所有的代码都采用了模板类或者模板函数,这相比传统的由函数和类组成的库来说提供了更好的代码重用机会。
在C++标准库当中,隶属于STL的占到了80%以上。在C++标准库中,STL被组织成以下13个头文件:
<algorithm>
<deque>
<functional>
<iterator>
<vector>
<list>
<map>
<memory>
<numeric>
<queue>
<set>
<stack>
<utility>
STL的优点有哪些?
(1)STL是C++的一部分,因此不用额外安装什么,它被内建在你的编译器之内。
(2)STL的一个重要的特点是数据结构和算法的分离。尽管这是个简单的概念,但是这种分离确实使得STL变得非常通用。例如在STL的vector容器中,可以放入元素,基础数据类型变量,元素的地址;STL的sort()排序函数可以用来操作vector, list等容器。
(3)程序员可以不用思考STL具体的实现过程,只要能够熟练使用STL就OK了。这样他们就可以把精力放在程序开发的别的方面。
(4)STL具有高可重用性,高性能,高移植性,跨平台的优点
- 高可重用性:STL中几乎所有的代码都采用了模板类和模板函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用的机会。
- 高性能:如map可以高效地从十万条记录里查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平衡二叉树的一种)
- 高移植性:在项目A上用STL编写的模块,可以直接移植到项目B上
- 跨平台:windows的visual studio上编写的代码可以在Mac OS的XCode上直接编译
C++中的容器是指用来存放数据的类模板。容器分为两种,分别是序列式容器和关联式容器。它们的区别如下:
序列式容器:容器的元素的位置是由进入容器时机和地点来决定的
关联式容器:容器有其自身的规则,进入容器元素的位置不是由进入的时机和地点决定的
迭代器可以理解为指针,对指针的操作基本都可以对迭代器操作。实际上,迭代器是一个类模板,这个类模板封装了一个指针。迭代器一般用来遍历容器中的元素。
算法可以理解为解决问题有限步骤。STL提供了大约100个实现算法的模板函数,比如算法for_each将为指定序列中的每个元素调用指定的函数等。这样一来,只要我们熟悉了STL之后,许多代码可以被大大的化简,只需要通过调用一两个算法函数,就可以完成所需要的功能并大大地提升效率。
从下面这个小例子可以窥见容器、迭代器和算法三者之间的关系。
// 算法,负责统计某个元素的个数
int MyCount(int *begin, int *end, int val)
{
int cnt = 0;
while (begin != end)
{
if (*begin == val)
++cnt;
++begin;
}
return cnt;
}
int main()
{
// 数组,容器
int arr[] = { 0, 7, 5, 4, 9, 2, 0 };
// 迭代器,开始和结束指针
int *pbegin = arr;
int *pend = &(arr[sizeof(arr) / sizeof(int)]);
int num = MyCount(pbegin, pend, 0);
cout << "num = " << num << endl;
getchar();
return 0;
}
下面的案例揭示了STL的基本使用语法。
void PrintVector(int v)
{
cout << v << endl;
}
// STL基本语法
void Test()
{
// 实例化一个包含int类型元素的容器
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
// STL提供的for_each算法
// 容器提供迭代器
vector<int>::iterator pBegin = v.begin();
vector<int>::iterator pEnd = v.end();
for_each(v.begin(), v.end(), PrintVector);
}
下面的案例演示了容器包含自定义类型的应用案例。
class Person
{
public:
Person() = default;
Person(int age, int id) : age(age), id(id) {}
void Show()
{
cout << "(age, id) = (" << age << ", " << id << ")" << endl;
}
private:
int age;
int id;
};
void Test2()
{
vector<Person> v;
v.push_back(Person(10, 20));
v.push_back(Person(30, 40));
v.push_back(Person(50, 60));
for (vector<Person>::iterator it = v.begin(); it != v.end(); ++it)
{
it->Show();
}
}
二 string容器
string的特性
说到string的特性,就不得不和char *
类型的字符串进行对比:
(1)char *
是一个指针,而string是一个类。string封装了char *
,管理这个字符串,是一个char *
型的容器。
(2)string封装了很多实用的成员方法。查找find,拷贝copy,删除delete,替换replace和插入insert等
(3)string不用考虑内存释放和越界问题。string管理char *
所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。
string转成char *
,实用成员方法c_str()
。char *
转string,直接将char *
传入string的构造方法中即可生成相应的string对象。
下面是string的初始化,赋值和取值操作
// string的初始化
void Test1()
{
string s1; // 无参构造,为空字符串
string s2(10, 'a');
string s3("hello");
string s4(s3); // 拷贝构造
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
}
// string的赋值操作
void Test2()
{
string s1;
string s2("app");
s1 = s2; // 重载等号运算符
cout << s1 << endl;
s1 = 'a';
cout << s1 << endl;
s1.assign("jkl"); // 赋值的成员方法assign
cout << s1 << endl;
}
// 取值操作
void Test3()
{
string s1 = "abcdefg";
// 重载[]操作符
for (int i = 0; i < s1.size(); ++i)
{
cout << s1[i] << endl;
}
// 成员方法at()
for (int i = 0; i < s1.size(); ++i)
{
cout << s1.at(i) << endl;
}
// 两者区别
// []方式,如果访问越界,程序直接崩溃
// at()方式,如果访问越界,程序抛出out_of_range异常
try
{
// 直接奔溃
// cout << s1[100] << endl;
// 抛出异常
cout << s1.at(100) << endl;
}
catch (...) // 捕获所有异常
{
cerr << "越界了!" << endl;
}
}
string的拼接操作
// 重载+=操作符
string &operator+=(const string &str);
string &operator+=(const char *str);
string &operator+=(const char c);
// append()方法
// 把字符串s连接到当前字符串尾部
string &append(const char *s);
// 把字符串s的前n个字符连接到当前字符串结尾
string &append(const char *s, int n);
// 同operator+=()
string &append(const string &str);
// 把字符串s中从pos开始的n个字符串连接到当前字符串结尾
string &append(const string &s, int pos, int n);
// 在当前字符串结尾添加n个字符c
string &append(int n, char c);
下面是字符串拼接的应用案例。
// string的拼接
void Test4()
{
string s1 = "abcd";
string s2 = "1111";
s1 += "abcd";
s1 += s2;
cout << s1 << endl;
string s3 = "2222";
s2.append(s3);
cout << s2 << endl;
string s4 = s2 + s3;
cout << s4 << endl;
}
string查找与替换
// 查找str第一次出现的位置,从Pos位置开始查找
int find(const string &str, int pos = 0) const;
// 查找s第一次出现的位置,从pos位置开始查找
int find(const char *s, int pos = 0) const;
// 从pos位置查找s的前n个字符第一次位置
int find(const char *s, int pos, int n) const;
// 查找字符c第一次出现的位置
int find(const char c, int pos = 0) const;
// 从pos位置开始查找str最后一次出现的位置
int rfind(const string &str, int pos = npos) const;
// 从pos位置开始查找s最后一次出现的位置
int rfind(const char *s, int pos = npos) const;
// 从pos查找s的前n个字符最后一次位置
int rfind(const char *s, int pos, int n) const;
// 查找字符c最后一次出现的位置
int rfind(const char c, int pos = 0) const;
// 替换从pos开始n个字符为字符串str
string &replace(int pos, int n, const string &str);
// 替换从pos开始的n个字符为字符串s
string &replace(int pos, int n, const char *s);
下面是字符串查找和替换的应用案例。
// 查找操作
void Test5()
{
string s1 = "abcdefghifgjklmn";
// 查找第一次出现的位置
int pos = s1.find("fg");
cout << "pos = " << pos << endl;
// 查找最后一次出现的位置
int rpos = s1.rfind("fg");
cout << "rpos = " << rpos << endl;
}
// string的替换
void Test6()
{
string s1 = "abcdefg";
s1.replace(0, 2, "111111");
cout << s1 << endl;
}
string比较
类似strcmp()
,string提供了成员方法compare()用于进行string的比较
int compare(const string &s) const;
int compare(const char *s) const;
string子串
// 返回由pos开始的n个字符串组成的字符串
string substr(int pos = 0, int n = npos) const;
string插入和删除
// 插入字符串
string &insert(int pos, const char *s);
string &insert(int pos, const string &str);
// 在指定位置插入n个字符c
string &insert(int pos, int n, char c);
// 删除从pos开始的n个字符
string &erase(int pos, int n = npos);
下面是string插入和删除的应用案例。
// string插入和删除
void Test7()
{
string s = "abcdefg";
s.insert(3, "111");
cout << s << endl;
s.erase(3, 3);
cout << s << endl;
}
三 vector容器
3.1 vector动态增长原理
当插入新元素的时候,如果空间不足,那么vector会重新申请更大的一块内存空间,将原空间数据拷贝到新的空间,释放旧空间。再把新元素插入到新申请的空间。
3.2 vector构造函数
// 采用模板实现类实现,默认构造函数
vector<T> v;
// 将v[begin(), end())区间中的元素作为初值进行初始化
vector(v.begin(), v.end());
// 将n个elem元素作为初值进行初始化
vector(n, elem);
// 拷贝构造函数
vector(const vector &vec);
下面是vector初始化的应用案例
// 初始化
void Test1()
{
// 默认构造
vector<int> v1;
int arr[] = { 10, 20, 30, 40, 50 };
vector<int> v2(arr, arr + sizeof(arr) / sizeof(int));
vector<int> v3(v2.begin(), v2.end());
vector<int> v4(v3);
printVector(v2);
printVector(v3);
printVector(v4);
}
3.3 vector常用赋值操作
assign(beg, end);
assign(n, elem);
// 重载等号运算符
vector &operator=(const vector &vec);
// 将vec与本身的元素互换
swap(vec);
下面是赋值操作的应用案例
// 常用赋值操作
void Test2()
{
int arr[] = { 10, 20, 30, 40, 50 };
vector<int> v1(arr, arr + sizeof(arr) / sizeof(int));
// 成员方法进行赋值
vector<int> v2;
v2.assign(v1.begin(), v1.end());
// 重载=号赋值
vector<int> v3;
v3 = v2;
// 将v1和v4的数据进行交换
vector<int> v4;
v4.push_back(100);
v4.push_back(200);
v4.push_back(300);
v4.swap(v1);
printVector(v1);
printVector(v4);
}
3.4 vector大小操作
// 返回容器中元素的个数
size()
// 判断容器是否为空
empty();
// 重新制定容器长度为num,如果容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素将被删除
resize(int num);
// 容器的容量
capacity();
// 容器预留len个元素长度,预留位置不初始化,元素不可访问
reserve(int len);
// 常用大小操作
void Test3()
{
int arr[] = { 100, 200, 300, 400 };
vector<int> v4(arr, arr + sizeof(arr) / sizeof(int));
cout << "v4.size() = " << v4.size() << endl;
if (v4.empty())
{
cout << "v4为空!" << endl;
}
printVector(v4);
v4.resize(2);
printVector(v4);
// 设定填充值为1
v4.resize(6, 1);
printVector(v4);
cout << "v4的容量:" << v4.capacity() << endl;
}
3.5 vector数据存取操作和插入删除
// 返回索引idx所指的数据,如果idx越界,则抛出out_of_range异常
at(int idx);
// 返回索引idx所指的数据,越界时,运行直接报错
operator[]
// 返回容器中的第一个数据元素
front();
// 返回容器中的最后一个数据元素
back();
// 往迭代器指向位置pos插入count个元素ele
insert(const_iterator pos, int count, T ele)
// 尾部插入元素
push_back();
// 删除最后一个元素
pop_back();
// 删除迭代器从start到end之间的元素
erase(const_iterator start, const_iterator end);
// 删除迭代器指向的元素
erase(const_iterator pos);
// 清空容器中的所有元素
clear();
下面是vector的插入和删除的应用案例
// 插入和删除
void Test4()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
// 头插法
v.insert(v.begin(), 30);
v.insert(v.begin(), 40);
v.insert(v.end(), 50);
// vector支持随机访问,在位置v.begin()+2处插入60
v.insert(v.begin() + 2, 60);
printVector(v);
v.erase(v.begin());
printVector(v);
v.pop_back();
printVector(v);
v.clear();
cout << "v.size() = " << v.size() << endl;
}
3.6 使用swap()收缩空间
// swap()收缩空间
void Test5()
{
vector<int> v;
for (int i = 0; i < 100000; ++i)
v.push_back(i);
cout << "v.size() = " << v.size() << endl;
cout << "v.capacity() = " << v.capacity() << endl;
// swap()收缩空间,匿名对象随后会被释放空间
vector<int>(v).swap(v);
cout << "v.size() = " << v.size() << endl;
cout << "v.capacity() = " << v.capacity() << endl;
}
3.7 使用reserve()预留空间提高程序效率
reserve和resize有啥区别?
reserve是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新对象之间,不能引用容器内的元素。resize是改变容器的大小,且在创建对象,因此,调用这个函数之后,就可以引用容器内的对象了。
// reserve预留空间
void Test6()
{
int num = 0;
int *addr = nullptr;
vector<int> v;
for (int i = 0; i < 100000; ++i)
{
v.push_back(i);
if (addr != &(v[0]))
{
++num;
addr = &(v[0]);
}
}
cout << "申请了" << num << "次内存!" << endl;
// 如果你知道容器大概要存储多少内存空间,你可以使用reserve()预留空间
// 减少内存申请的次数以提高程序运行效率
num = 0;
addr = nullptr;
vector<int> u;
u.reserve(100000);
for (int i = 0; i < 100000; ++i)
{
u.push_back(i);
if (addr != &(u[0]))
{
++num;
addr = &(u[0]);
}
}
cout << "申请了" << num << "次内存!" << endl;
}
C++ STL——string和vector的更多相关文章
- POJ 3096 Surprising Strings(STL map string set vector)
题目:http://poj.org/problem?id=3096 题意:给定一个字符串S,从中找出所有有两个字符组成的子串,每当组成子串的字符之间隔着n字符时,如果没有相同的子串出现,则输出 &qu ...
- C++进阶 STL(1) 第一天 [容器,算法,迭代器] string容器 vector容器 deque容器
课程大纲 02实现基本原理 容器,算法,迭代器 教室:容器 人:元素 教室对于楼:容器 序列式容器: 容器元素在容器中的位置是由进入容器的时间和地点来决定 序列式容器 关联式容器: 教室中 按年龄排座 ...
- STL容器之vector
[1]模板类vector 模板类vector可理解为广义数组.广义数组,即与类型无关的数组,具有与数组相同的所有操作. 那么,你或许要问:既然C++语言本身已提供了一个序列式容器array,为什么还要 ...
- [知识点]C++中STL容器之vector
零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 关于STL和STL容器的概念参见STL系列第一篇——map(见上).今天介绍第二个成员——vector. 二.用途 ...
- std::string,std::vector,std::accumulate注意事项
在用string做字符串拼接时,会发现随着string的增大越来越慢,原因主要是string(和vector)是基于现行内存的数据结构,在海量数据时,经常会申请新的一块内存,把原有的数据拷贝过去然后再 ...
- C++标准模板库Stand Template Library(STL)简介与STL string类
参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...
- 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个对立的元素存于vector中
#include<iostream> #include<string> #include<vector> #include<fstream> using ...
- 转:用STL中的vector动态开辟二维数组
用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...
- stl string 使用指定的分隔符分割成数个子字符串
#include <iostream> #include <vector> #include <string> #include <algorithm> ...
随机推荐
- docker container 导入和导出
目录 docker container 导入和导出 1.前言 2.docker container 的导出 3.docker container 的导入 4.镜像和容器 导出和导入的区别 docker ...
- IntelliJ IDEA安装及破解
百度搜索IntelliJ IDEA,进入官网. 下载完成后进入安装界面 根据自己的情况选择安装路径 等待下载和安装完成. 安装完成 接下来我们运行IntelliJ IDEA 之后这里就要我们进行激活了 ...
- 《Linux就该这么学》day4-6
继续学习打卡,这几天有事所以看视频补上了这几天的学习进度. day4:主要重点 tar打包和解压命令 tar -zxvf xxx.tar.gz 解压xxx.tar.gz (将xxx.tar.gz ...
- linux上网时断时续问题
[问题描述]打开百度比较慢:登录远程服务器操作一会儿就断了 [问题环境]ip自动获取 [问题分析]自己的ip地址被人占用了 [问题解决]1)重新手动配置一个新的ip 2)释放原来的ip,重新自动 ...
- 十九:mvc强类型声明
落下了几节,自己很懒啊, 得找个时间补上... 1. 强类型 是指变量在定义时就已经明确指定了其类型.如: string s; int x; 2.弱类型 赋值时才确定类型. var s; var x ...
- asp.net 页面静态化
页面静态化,有三种方式 伪静态 真静态,折中法 现在我做的是折中发 创建一个asp.net 页面, 连接跳转到还未生成的页面 创建HttpHandle类 using System;using ...
- new Function()语法
函数的语法: let func = new Function(...args, body); 历史原因,参数也可以以逗号分隔的列表的形式给出,这三个意思相同: new Function('a', 'b ...
- ACM中值得注意/利用的C++语法特性
C++ 的易踩坑点 随时补充 STL不能边循环边erase() //自己写的求交集RE了 for (set <int> ::iterator it = s.begin(); it != s ...
- RxJava——响应式编程
自从06年开始,Rxandroid公司项目中陆续就开始使用它了,而它的基础是由Rxjava演变过来的,如今它也是越来越被广泛使用在商业项目中了,而做为"专业"的自己还是一直对它一知 ...
- HDU 5876 补图最短路
开两个集合,一个存储当前顶点可以到达的点,另一个存储当前顶点不能到达的点.如果可以到达,那肯定由该顶点到达是最短的,如果不能,那就留着下一次再判. #include<bits/stdc++.h& ...