注:原创不易,转载请务必注明原作者和出处,感谢支持!

注:内容来自某培训课程,不一定完全正确!

一 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具有高可重用性,高性能,高移植性,跨平台的优点

  1. 高可重用性:STL中几乎所有的代码都采用了模板类和模板函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用的机会。
  2. 高性能:如map可以高效地从十万条记录里查找出指定的记录,因为map是采用红黑树的变体实现的。(红黑树是平衡二叉树的一种)
  3. 高移植性:在项目A上用STL编写的模块,可以直接移植到项目B上
  4. 跨平台: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的更多相关文章

  1. POJ 3096 Surprising Strings(STL map string set vector)

    题目:http://poj.org/problem?id=3096 题意:给定一个字符串S,从中找出所有有两个字符组成的子串,每当组成子串的字符之间隔着n字符时,如果没有相同的子串出现,则输出 &qu ...

  2. C++进阶 STL(1) 第一天 [容器,算法,迭代器] string容器 vector容器 deque容器

    课程大纲 02实现基本原理 容器,算法,迭代器 教室:容器 人:元素 教室对于楼:容器 序列式容器: 容器元素在容器中的位置是由进入容器的时间和地点来决定 序列式容器 关联式容器: 教室中 按年龄排座 ...

  3. STL容器之vector

    [1]模板类vector 模板类vector可理解为广义数组.广义数组,即与类型无关的数组,具有与数组相同的所有操作. 那么,你或许要问:既然C++语言本身已提供了一个序列式容器array,为什么还要 ...

  4. [知识点]C++中STL容器之vector

    零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 关于STL和STL容器的概念参见STL系列第一篇——map(见上).今天介绍第二个成员——vector. 二.用途 ...

  5. std::string,std::vector,std::accumulate注意事项

    在用string做字符串拼接时,会发现随着string的增大越来越慢,原因主要是string(和vector)是基于现行内存的数据结构,在海量数据时,经常会申请新的一块内存,把原有的数据拷贝过去然后再 ...

  6. C++标准模板库Stand Template Library(STL)简介与STL string类

    参考<21天学通C++>第15和16章节,在对宏和模板学习之后,开启对C++实现的标准模板类STL进行简介,同时介绍简单的string类.虽然前面对于vector.deque.list等进 ...

  7. 编写函数,以读模式打开一个文件,将其内容读入到一个string的vector中,将每一行作为一个对立的元素存于vector中

    #include<iostream> #include<string> #include<vector> #include<fstream> using ...

  8. 转:用STL中的vector动态开辟二维数组

    用STL中的vector动态开辟二维数组 源代码:#include <iostream>#include <vector>using namespace std;int mai ...

  9. stl string 使用指定的分隔符分割成数个子字符串

    #include <iostream> #include <vector> #include <string> #include <algorithm> ...

随机推荐

  1. 5.API详解

    Dao 中需要通过 SqlSession 对象来操作 DB.而 SqlSession 对象的创建, 需要其工厂对象 SqlSessionFactory.SqlSessionFactory 对象, 需要 ...

  2. 解决Django项目静态资源无法访问的问题

    静态资源无法访问 url.py中配置 from django.conf.urls import url from django.views import static from django.conf ...

  3. JQuery初始加载时注册文本框失去焦点事件

    在JQuery初始加载时注册文本框失去焦点事件 $(function(){ $('#文本框ID').blur(function(){ //对文本框内容进行处理 }); });

  4. 8. Object References, Mutability, and Recycling

    1. Variables Are Not Boxes # Think variables as sticky notes a = [1, 2, 3] b = a a.append(4) print b ...

  5. okhttp拦截器之CacheInterceptor解析

    在上一次[https://www.cnblogs.com/webor2006/p/9150658.html]了解了缓存的存与取的细节之后,接下来就可以分析一下OkHttp的缓存拦截器啦: OkHttp ...

  6. linux程序编译过程

    大家肯定都知道计算机程序设计语言通常分为机器语言.汇编语言和高级语言三类.高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类 ...

  7. 【2-sat】8.14B. 黑心老板

    2-sat 只写过板子 题目大意 有一个长度为$k$取值只有01的序列,现在$n$个人每人下注三个位置,请构造一个序列使每个人最多猜对一个位置 $k\le 5000,n \le 10000$ 题目分析 ...

  8. BZOJ4886 [Lydsy1705月赛]叠塔游戏[基环树]

    很妙的一道题. 由于本人过于zz,不会这道题,通过厚颜无耻翻阅题解无数终于懂了这道题,所以这里转载一位神仙的blog. 没有看懂?没事,再来一篇. 这题个人认为主要在于转化题意和建图,这两点想通了应该 ...

  9. Linq 分组查询

    根据部门分组 ,然后存储部门下所有员工 public class Custom { public string dname { get; set; } public List<Employees ...

  10. BZOJ 2346: [Baltic 2011]Lamp Dijkstra

    不难发现如果一个边的方向改变,就一定不会改回来(这样肯定不是最短路). 所以就直接建双向边,边权为 $0$ 代表不改变,边权为 $1$ 代表改变,跑一个最短路即可. #include <bits ...